- Common Lisp गेम डेवलपमेंट environment पर ECS आर्किटेक्चर और meta-linguistic programming को एक वास्तविक dungeon crawler उदाहरण तक विस्तार देने वाला ट्यूटोरियल है
- Tiled XML मैप को cl-tiled से पढ़ने के बाद CLOS objects को सीधे इस्तेमाल करने के बजाय उन्हें ECS components में बदलकर rendering, collision और memory management को अलग किया गया है
- tile prefab, image pointer, parent-child index और finalizer को मिलाकर duplicate loading और double free से बचा गया है, और Tiled custom properties को data की तरह इस्तेमाल किया गया है
- player और enemy, ECS systems के जरिए movement, animation switching और collision handling करते हैं, जबकि enemy cl-astar आधारित A* pathfinding से दीवारों से बचते हुए पीछा करता है
- Nuklear आधारित UI, narrative objects, pause और victory condition तक जोड़कर लगभग 500 lines का एक छोटा dungeon crawler उदाहरण पूरा किया गया है
प्रोजेक्ट की शुरुआत और बेसिक रन
- Part 1 में कवर किए गए Entity-Component-System आर्किटेक्चर और meta-linguistic programming तकनीकों का इस्तेमाल करके UI वाला एक छोटा dungeon crawler बनाया जाता है
- executable demo binary और source code ecs-tutorial-2 GitHub repository में हैं
- development environment, Part 1 के Common Lisp गेम डेवलपमेंट environment को मानकर चलता है, और SBCL REPL में Quicklisp distribution को अपडेट किया जाता है
(ql-util:without-prompting (ql:update-all-dists))
- cookiecutter-lisp-game template से नया project
ecs-tutorial-2बनाया जाता है, और उदाहरण में backend के रूप मेंliballegroचुना गया है - project directory को Quicklisp
local-projectsसे जोड़ने के बादsrc/main.lispकी window size को 1280×800 में बदला जाता है (ql:quickload :ecs-tutorial-2)और(ecs-tutorial-2:main)चलाने पर तय resolution वाली काली window और FPS counter दिखाई देता है
Tiled मैप और ECS storage
- dungeon map बनाने के लिए open source map editor Tiled का उपयोग किया गया है
- Tiled एक cross-platform·cross-engine tool है, और map data को XML में सेव करता है
- Common Lisp में cl-tiled, Tiled files को Lisp objects के रूप में लोड करता है
- उदाहरण tileset के लिए Dungeon Tileset II - Extended का उपयोग किया गया है
- मूल 16×16 tiles छोटे होने की वजह से ImageMagick से 200% बड़ा करके उन्हें 32×32 tiles के रूप में इस्तेमाल किया गया है
level1.tmxऔर tileset files, ट्यूटोरियल में दिए गएResources.zipसे प्राप्त किए जा सकते हैं
ecs-tutorial-2.asdमेंcl-tileddependency जोड़ी जाती है, औरsrc/map.lispनया बनाकर map loading और display code को अलग किया जाता हैsrc/package.lispमेंcl-tiledकोtiledनाम के local nickname के रूप में रजिस्टर किया जाता है
CLOS objects को ECS components में बदलने की वजह
cl-tiled, map data को CLOS objects के रूप में लौटाता है, इसलिए REPL में उन्हें देखना आसान होता है- अगर game loop में इन objects को सीधे इस्तेमाल किया जाए, तो runtime dispatch cost बढ़ सकती है
- 1280×800 window को 32×32 tiles से भरने के लिए कम से कम 40×25 = 1000 tiles चाहिए
- एक अलग demo में, 12-core Ryzen 5 3600 पर map rendering चालू करने पर FPS 20,000 से 600 तक गिर जाता है
- यानी प्रति frame लगभग
1/600 - 1/20000 = 0.0016सेकंड, यानी 1.5ms से अधिक अतिरिक्त लगता है
cl-tiledसे पढ़े गए data कोcl-fast-ecsstorage में ले जाने से dispatch कम किया जा सकता है और CPU cache उपयोग बेहतर हो सकता हैcl-fast-ecsdependency जोड़ी जाती है, औरinitमेंecs:make-storage,updateमेंecs:run-systemsकॉल किया जाता है
मैप, टाइल और prefab components
map, लोड किए गए map entity को दर्शाने वाला एक tag component हैmap-tile, अलग-अलग tiles को दर्शाता है, और दीवार या बंद दरवाज़े जैसी बाधा होने या न होने के लिएobstacleBoolean slot रखता हैparentcomponent यह दिखाता है कि tile और map से जुड़े objects किस map entity के child हैंentityslot में:index childrenदेकर किसी खास parent के child entities को जल्दी खोजा जाता है- index, open-address hash table पर आधारित है, इसलिए औसतन O(1) lookup देता है, लेकिन create/delete के समय update cost होती है
ecs:*entity-deleting-hook*में hook जोड़कर यह सुनिश्चित किया जाता है कि parent entity delete होने परchildrenindex से मिले child entities भी साथ में delete हो जाएँimagecomponent,ALLEGRO_BITMAPके लिए केवल C pointer स्टोर करता है- tileset image के 32×32 टुकड़े
al_create_sub_bitmapसे बनाए जाते हैं और उनके pointers स्टोर किए जाते हैं
- tileset image के 32×32 टुकड़े
map-tile-prefab, Tiled tile की global IDgidरखने वाला tile prefab हैgidपर:index map-tile-prefab :unique tदेकर ID से एक single prefab entity खोजी जाती है- map पर मौजूद असली tiles, prefab से
imageजैसे components कॉपी करते हैं, लेकिन position अलगpositioncomponent में रखते हैं
imagefinalizer केवल तबal_destroy_bitmapकॉल करता है जब entitymap-tile-prefabहो- क्योंकि कई map tiles एक ही
ALLEGRO_BITMAPpointer साझा करते हैं, इसलिए double free से बचना ज़रूरी है
- क्योंकि कई map tiles एक ही
positionऔरsize, screen coordinates और आकार कोsingle-floatके रूप में स्टोर करते हैंliballegro, OpenGL compatibility की वजह से screen coordinates को single-precision floating point में संभालता है, इसलिए वही तरीका अपनाया गया है
image rendering और map loading
render-imagessystem,positionऔरimageवाले entities को render करता हैal_hold_bitmap_drawingसे sprite batching चालू और बंद किया जाता हैal_draw_bitmapसे तय coordinates पर image draw की जाती है- prefab में
positionनहीं होता, इसलिए इस system में उसे प्रोसेस नहीं किया जाता
load-bitmap,al_load_bitmapकोal:ensure-loadedसे wrap करने वाला image loading function हैtile->spec, tile prefab बनाने के लिए ECS object specification तैयार करता है- parent map entity
- tile image slice
- Tiled global tile ID
- tile size
load-tile-prefab,map-tile-prefabindex से जाँचता है कि prefab पहले से लोड है या नहीं, और न होने परmake-objectसे बनाता हैload-tile, असली map tile entity बनाते समय prefab से components कॉपी करता है औरpositionजोड़ता हैload-map,tiled:load-mapसे पढ़े गए CLOS objects में tilesets और layers पर iterate करता है- tileset images लोड करके हर tile के लिए prefab बनाया जाता है
- tile layer की हर cell को entity बनाकर prefab data कॉपी किया जाता है
- Tiled में layer order editor के order के अनुसार बना रहता है, और
make-entityबढ़ते हुए entity numbers की गारंटी देता है- system पुराने entities को पहले प्रोसेस करता है, इसलिए ऊपर वाली layer के tiles बाद में draw होकर नीचे वाली layer को ढक लेते हैं
- हर tile को अलग entity के रूप में स्टोर करना ही एकमात्र तरीका नहीं है; static map को buffer में पहले से render करने का तरीका भी संभव है
टाइल एनीमेशन
- Tiled एनीमेटेड टाइल्स को सपोर्ट करता है, इसलिए मशाल या जादुई फव्वारे जैसे एलिमेंट्स दिखाए जा सकते हैं
common.lispऔरanimation.lispजोड़कर common components और animation-संबंधित components·systems को अलग किया गया हैanimation-framecomponent एनीमेशन के एक फ्रेम को दर्शाता हैsequenceएनीमेशन का नाम है और इसेkeywordtype में स्टोर किया जाता हैsequence-framesindex से किसी खास एनीमेशन के फ्रेम्स खोजे जाते हैंdurationसेकंड में फ्रेम की अवधि है
animation-stateमैप पर मौजूद एनीमेटेड टाइल की वर्तमान स्थिति स्टोर करता है- वर्तमान
sequence - वर्तमान
frame - वर्तमान फ्रेम
duration - वर्तमान फ्रेम के दिखाए जाने का
elapsedसमय
- वर्तमान
let-plusdependency जोड़कर फ्रेम बदलने वाला कोड अधिक संक्षिप्त लिखा गया हैupdate-animationssystemdtजितनाelapsedबढ़ाता है, और अवधि पार होने पर अगले फ्रेम पर स्विच करता है- क्योंकि फ्रेम समय बड़े
dtसे छोटा हो सकता है, इसलिएfloorसे यह निकाला जाता है कि कितने फ्रेम्स स्किप करने हैं truncateसे फ्रेम नंबर लिस्ट की लंबाई से आगे निकलने पर फिर शुरुआत पर घुमाया जाता हैimageका bitmap pointer अगले फ्रेम prefab के bitmap से बदल दिया जाता है
- क्योंकि फ्रेम समय बड़े
- एनीमेशन अवधि Tiled में milliseconds में स्टोर होती है, इसलिए
animation->specमें इसे सेकंड में बदला जाता है instantiate-animationअसली टाइल entity परanimation-stateबनाता है, और ताकि वही एनीमेशन पूरी तरह sync न हों,elapsedको 0 औरdurationके बीच एक random value से initialize किया जाता है- एनीमेशन टाइल्स में Tiled property
"sequence"होना चाहिए- यह property न हो तो वह
NILनाम से लोड होगी, अपेक्षित एनीमेशन नाम से नहीं मिलेगी, और type error हो सकता है
- यह property न हो तो वह
प्लेयर कैरेक्टर और नियंत्रण
character.lispजोड़ा गया है, और चल सकने वाले कैरेक्टर्स के लिएcharactercomponent परिभाषित किया गया हैspeedप्रति सेकंड pixels की गति हैtarget-x,target-yचलने के लक्ष्य coordinates हैं- शुरुआती target values को
single-float-nanरखा गया है ताकि नया कैरेक्टर बिना वजह ऊपर-बाएँ कोने की ओर न चल पड़े
playertag componentbitslot और:index player-entity :unique tका उपयोग करता है- यह
(player-entity 1)से प्लेयर entity को O(1) में खोजने की संरचना है - प्लेयर entity को global variable में स्टोर नहीं किया जाता
- यह
- शुरुआती implementation में tileset से orc image काटकर
player.pngबनाया गया औरload-playerसे प्लेयर को hardcode करके बनाया गया- स्थान
(64.0, 64.0) - आकार 32×32
- गति 100.0
- स्थान
move-characterssystem कैरेक्टर्स को लक्ष्य बिंदु तक चलाता है- यदि लक्ष्य coordinates
NaNहों तो उन्हें वर्तमान स्थान से initialize किया जाता है - floating-point की direct comparison के बजाय
approx-equalका उपयोग किया जाता है atan,cos,sin, speed, औरdtसे नए coordinates निकाले जाते हैं
- यदि लक्ष्य coordinates
control-playersystemW,A,S,Dkey input पढ़कर लक्ष्य coordinates अपडेट करता हैal:with-current-keyboard-stateऔरal:key-downका उपयोग करता हैclampसे स्क्रीन सीमा के बाहर जाने से रोका जाता है:after (move-characters)के साथ इसे movement system के बाद चलाया जाता है ताकिNaNinitialization समस्या से बचा जा सके
Tiled properties से collision और object loading
- शुरुआत में दीवारें फर्श टाइल्स जैसी सामान्य images थीं, इसलिए प्लेयर दीवारों के आर-पार चला जाता था
- Tiled के custom type से
map-tileclass बनाई गई औरobstacleBoolean member जोड़ा गया- दीवार टाइल की property में
map-tileजोड़ा गया औरobstacleको check किया गया
- दीवार टाइल की property में
properties->specfunction Tiled property hash table को ECS object spec में बदलता है- Tiled custom classes को components की तरह handle किया जाता है
- class members को component slots की तरह handle किया जाता है
- उदाहरण
((:map-tile :obstacle t))के रूप में है
load-tile-prefabमेंproperties->specके परिणाम को prefab spec में शामिल किया जाता है- properties न हों तो
spec-adjoinसे defaultmap-tilecomponent जोड़ा जाता है औरobstacleकी default valuenilरहती है
- properties न हों तो
positioncomponent मेंtile-hashslot औरtilesindex जोड़े गए हैंtile-hashx,yको integer में बदलने के बाद एक 64-bit integer में pack करता है- किसी खास टाइल के ऊपर-बाएँ coordinates पर मौजूद सभी entities को
tilesindex से खोजा जाता है
tile-startकिसी भी coordinate के शामिल grid tile का ऊपर-बाएँ coordinate लौटाता हैtile-obstacle-pयह जांचता है कि उसी coordinate की entities में कोईmap-tileहै या नहीं, और उसकाobstacletrue है या नहींobstaclepकिसी भी coordinate के लिए जांचता है कि संबंधित टाइल obstacle है या नहींcontrol-playermovement direction के अनुसार character rectangle के संबंधित corner tiles की जांच करता है, और obstacle होने पर लक्ष्य coordinate को वर्तमान स्थान पर वापस कर देता है- यह collision तरीका पूरी तरह परिपूर्ण नहीं है
- अगर character center coordinate के आधार पर डिजाइन किया जाए तो math और code सरल हो सकते हैं, लेकिन उदाहरण जटिलता से बचने के लिए मौजूदा तरीका ही रखता है
मैप से प्लेयर और एनीमेटेड कैरेक्टर्स लोड करना
- Tiled में
characterऔरplayercustom classes जोड़ी गई हैंcharacterमें केवलspeedfloat member हैtarget-x,target-yको default values इस्तेमाल करने के लिए छोड़ा गया हैplayerमें default value 1 वालाplayerint member है
- Tiled object layer में tile object के रूप में प्लेयर कैरेक्टर रखा जाता है, और उसे
characterतथाplayerproperties दी जाती हैं load-mapकोtiled:object-layerभी process करने के लिए विस्तारित किया गया है- object properties को
properties->specसे ECS components में बदला जाता है tiled:tile-objectload-tileके जरिए tile data और animation कॉपी करता है और position सेट करता है- Tiled object coordinates नीचे-बाएँ आधार पर होते हैं, इसलिए
yसे object height घटाकर उसे ऊपर-बाएँ आधार के अनुसार मिलाया जाता है
- object properties को
- hardcoded
load-playercall और function हटा दिए गए हैं - यह संरचना Tiled map data को सीधे ECS objects के रूप में पढ़ती है, और data-driven programming के अधिक करीब हो जाती है
- character animation के लिए tileset में orc के
orc-idleऔरorc-runsequences परिभाषित करके इस्तेमाल किए जाते हैं change-animation-sequenceentity की वर्तमान animation बदलता है- अगर sequence पहले से वही हो तो कोई काम नहीं करता
- नई sequence का पहला frame
sequence-framesindex से खोजकरanimation-stateऔरimage-bitmapअपडेट किया जाता है
move-charactersकैरेक्टर रुकने पर:orc-idle, और चलने पर:orc-runमें स्विच करता है
दुश्मन, गेम ओवर, A* पाथफाइंडिंग
enemyकंपोनेंट में दुश्मन के व्यवहार के लिए दो स्लॉट होते हैंvision-range: वह दूरी जहाँ से दुश्मन खिलाड़ी को देखकर प्रतिक्रिया शुरू करता हैattack-range: हमला करने की रेंज
- गेम समाप्त करने के लिए
*should-quit*global variable जोड़ा जाता है, और main loop यह मान सच होने पर समाप्त हो जाता है handle-enemiesसिस्टम खिलाड़ी के coordinates लेकर उन्हें दुश्मनों से तुलना करता है- अगर खिलाड़ी vision range में है, तो दुश्मन के target coordinates खिलाड़ी की position पर सेट किए जाते हैं
- अगर खिलाड़ी attack range में है, तो
*should-quit*को true सेट किया जाता है औरYou diednative message box दिखाया जाता है
- दुश्मन animation के लिए
demon-idle,demon-runsequences का उपयोग होता हैmove-charactershas-player-pके परिणाम के आधार पर खिलाड़ी के लिए orc animation और दुश्मनों के लिए demon animation चुनता है
- सीधे पीछा करने के तरीके में दुश्मन दीवारों के आर-पार भी चले जाते हैं, इसलिए A* पाथफाइंडिंग जोड़ी जाती है
- cl-astar को dependency के रूप में जोड़ा जाता है
- यह लाइब्रेरी समस्या के अनुसार optimized pathfinding functions को macro के रूप में generate करती है
- path को component slot के अंदर array के रूप में रखने के बजाय, हर path point को अलग entity के रूप में दर्शाया जाता है
path-pointमेंx,y,travellerहोते हैं, औरtravellerमेंpath-pointsindex होता हैpathअंतिम destinationdestination-x,destination-yको store करता हैcharacterके target coordinates अगले path point को दर्शाते हैं, जबकिpathअंतिम destination को दर्शाता है
follow-pathसिस्टम पहला path point लेकर character को उस point तक move कराता है- point पर पहुँचने पर संबंधित
path-pointentity हटा दी जाती है - अगर आगे कोई point नहीं बचता, तो
pathcomponent हटा दिया जाता है
- point पर पहुँचने पर संबंधित
find-pathकोa*:define-path-finderसे define किया जाता है- world size की गणना window size को tile size से भाग देकर की जाती है
- row-major indexer का उपयोग होता है
- target तक पहुँचना tile coordinates के समान होने से तय किया जाता है
- neighbors को 8 दिशाओं में enumerate किया जाता है
- obstacles या obstacle को काटते हुए diagonal movement को
most-positive-single-floatcost देकर व्यावहारिक रूप से असंभव बना दिया जाता है - heuristic के लिए octile distance का उपयोग होता है
- अगर पुराना path मौजूद है, तो उसके path points हटा दिए जाते हैं और नया
pathassign किया जाता है - result path के हर point के लिए
path-pointऔरparentवाले entities बनाए जाते हैं
handle-enemiesमें जब दुश्मन खिलाड़ी को देखता है और मौजूदा path destination खिलाड़ी की position से अलग होती है, तोfind-pathको call किया जाता है- बदलाव के बाद दुश्मन खिलाड़ी का पीछा करते हैं, लेकिन obstacles से बचते हुए चलते हैं
Nuklear-आधारित गेम UI
- narrative elements के लिए GUI की जरूरत होती है, लेकिन Qt या GTK जैसी पारंपरिक GUI libraries
liballegrographics context पर draw होने वाले game UI के लिए उपयुक्त नहीं हैं - UI library के रूप में Nuklear का उपयोग किया जाता है
liballegroके साथ उपयोग के लिए Common Lisp binding cl-liballegro-nuklear उपलब्ध है- binding declarative interface के लिए DSL भी प्रदान करती है
cl-liballegro-nuklear/declarativedependency जोड़ी जाती है, औरsrc/narrative.lispको नई file के रूप में जोड़ा जाता है- package में
uilocal nickname register किया जाता है ताकिcl-liballegro-nuklear/declarativeको छोटे रूप में refer किया जा सके - UI font के रूप में Google Fonts की Alegreya का उपयोग होता है, और filename को
alegreya-sc.ttfमें बदला जाता है ui:defwindow narrativenarrative window function define करता है- window position स्क्रीन के central area के रूप में calculate की जाती है
ui:label-wrapसे automatic line wrapping वाला text दिखाया जाता हैui:button-label "Ok"click होने पर true return करता है
- Nuklear एक immediate mode UI library है
- retained mode की तरह widget objects को memory में बनाए रखने के बजाय, यह हर frame rendering और processing करता है
- button click को callback से नहीं, बल्कि हर frame के return value और conditions से handle किया जाता है
main.lispUI font load करता है औरnk:allegro-initसे UI context initialize करता है- event loop में
nk:input-begin,nk:allegro-handle-event,nk:input-endको call किया जाता है - rendering के समय
nk:allegro-renderको call किया जाता है - समाप्ति पर
nk:allegro-shutdown,nk:allegro-font-delको call किया जाता है
- event loop में
UI skin और narrative objects
- default UI साधारण लगती है, इसलिए Kenney के
fantasy-ui-bordersimage assets का उपयोग करके उसे style किया जाता है *window-background*,*button-normal-background*,*button-hover-background*,*button-active-background*global variables में UI images store की जाती हैंload-uink:allegro-create-imageसे images load करता है, औरunload-uink:allegro-del-imageसे C side image resources release करता हैinitमेंload-uiको call किया जाता है, और main loop समाप्त होने परunload-uiको call किया जाता हैui:defwindowके:stylesargument से background, button state के अनुसार images, और text color specify किए जाते हैंnarrativecomponent environmental storytelling के objects को दर्शाता हैtext: दिखाया जाने वाला textshown: क्या यह पहले एक बार दिखाया जा चुका हैactive: क्या अभी window सक्रिय हैactiveमेंactive-narrativesindex होता है
show-narrativeसिस्टम तब window दिखाता है जब खिलाड़ी narrative object के पास होता है- interaction distance की गणना
+interact-distance-factor+और खिलाड़ी के tile size से की जाती है - अगर window पहले से active हो, या अभी तक कभी न दिखाई गई हो, या
Ekey दबाई गई हो, तो उसे दिखाया जाता है Okbutton,Esc,Space,Enterमें से किसी एक से window बंद की जाती है
- interaction distance की गणना
- Tiled में
narrativecustom type बनाया जाता है औरtextstring member जोड़ा जाता है - collision detection और alignment के लिए non-passable objects के coordinates को tile grid पर align होना चाहिए
- narrative window खुली होने पर dungeon के चलते रहने की समस्या को system execution condition से रोका जाता है
move-charactersऔरcontrol-playerमें:when (null (active-narratives t))जोड़ा जाता है- active narrative होने पर movement और control systems execute नहीं होते
- victory condition को
wintag component से जोड़ा जाता हैnarrativeके साथ लगे object में window बंद होने पर*should-quit*को true सेट किया जाता है और गेम समाप्त हो जाता है
समापन और दायरा
- अंतिम example
cl-fast-ecs,cl-tiled,cl-astar,cl-liballegro-nuklearका उपयोग करके environmental storytelling, enemy AI, और GUI वाला Souls-like dungeon crawler बनाता है - implementation का आकार लगभग 500 lines of code है
- पूरा code GitHub repository में उपलब्ध है, और tutorial code के अलावा
declaimका उपयोग करने वाली optional type declarations भी शामिल हैं - sound design, cutscenes, main menu, level transitions, और “door problem” जैसे विषय शामिल नहीं हैं
- Autumn Lisp Game Jam 2024, 25 अक्टूबर 2024 को itch.io पर आयोजित होगा; यह एक ऐसा event है जिसमें Lisp dialects में 10 दिनों तक game बनाकर एक-दूसरे को rate, feedback और review दिया जाता है
- यह भाग Spring Lisp Game Jam 2023 की entry Thoughtbound पर आधारित है
- अगले भाग में स्केल बढ़ाकर और अधिक उन्नत AI जोड़कर real-time strategy game बनाने की चुनौती का संकेत दिया गया है
1 टिप्पणियां
Hacker News की राय
काश सभी tech tutorials ऐसे ही होते। लेख की संरचना अच्छी है, grammar errors भी लगभग नहीं हैं, हर नए विषय को पेश करते समय ठीक उतनी ही व्याख्या दी गई है जितनी चाहिए, और इसमें पूरा code example के साथ-साथ code असल में क्या कर रहा है, यह दिखाने वाले visuals भी हैं
सामग्री को गहराई से कवर करने के लिए यह पर्याप्त लंबा है, लेकिन इतना standalone भी है कि अगर आपने Part 1 नहीं पढ़ा और कुछ साल पहले Common Lisp को बस कुछ महीनों तक छुआ था, तब भी साथ चल सकते हैं। मैंने Clojure और Emacs Lisp काफी किया है
Bravo, awkravchuk/Andrew :^)
(Crossposted from https://mxjn.me/2024/10/17/1)
Offline इस्तेमाल या archive के लिए इसे आसानी से save और annotate किया जा सकता है, और search करना भी आसान है
Common Lisp पर कोई शानदार project या लेख जितना tech में दिल छूता है, वैसा बहुत कम होता है। यह सच में एक बड़ा तोहफ़ा जैसा लेख है
Part 1 आने पर मैंने पढ़ा था, और इस part को पढ़ने के लिए भी बहुत उत्साहित हूँ। लेखक को सलाम
package.shऔर कुल मिलाकर 3 operating systems के build management अपने आप में एक masterclass हैं। GitHub repository को बस सरसरी तौर पर देखने से ही बहुत कुछ सीखाआम तौर पर मैं SBCL या LispWorks से Common Lisp command-line apps build करता हूँ, लेकिन अगली बार ECL से भी कर सकता हूँ। macOS और Linux दोनों builds होना बढ़िया है, और कुछ नया try करने में भी मज़ा आएगा
बहुत अच्छा लेख है। मैं Lisp, यानी ठीक-ठीक कहें तो ClojureScript में multiplayer third-person spell-based shooter बना रहा हूँ। यह web-based 3D game है, और project के लिए बनाए गए tools और abstractions सहित पूरी journey पर blog लिखने की योजना है
अगर रुचि हो तो demo यहाँ है: https://wizardmasters.io
लेख अपने आप में वाकई मजबूत है, लेकिन Part 1 की setup प्रक्रिया Common Lisp खुद, Python, C और कई चरणों से गुजरती है—इसे देखकर लगता है कि CL खासकर युवा programmers में इतना popular क्यों नहीं है
यह अफ़सोस की बात है, और अच्छा होगा अगर कोई installation के लिहाज से language को ज्यादा approachable बनाने की मेहनत करे
मेरी समझ में यह बहुत सारे विकल्पों और पुराने defaults के outdated दिखने वाली problem पर अधिक focus करता है
event loop इस बात का बेहतरीन example है कि
loopकितना गंभीर iteration के लिए domain-specific language है। पसंद करें या न करें ;)loopकी जगह https://iterate.common-lisp.dev/ इस्तेमाल नहीं कर सकते? न S-expression से अलग अजीब syntax है, न Lisp syntax में लौटने के लिएdoकी जरूरतबदसूरत
else/endके बिना सामान्यif/whenइस्तेमाल करता है, और कुल मिलाकर उपयोगी features भी जोड़े गए हैंloopमेरे पसंदीदा CL constructs में से एक बन गयायह लेख "Caves of Clojure" की याद दिलाता है: https://stevelosh.com/blog/2012/07/caves-of-clojure-01/
संयोग से इसी हफ्ते मैंने Python में roguelike develop करना शुरू किया है, लेकिन Lisp में करना भी बढ़िया रहेगा
मुझे तो ठगा हुआ महसूस हो रहा है। मैं एक simple game बनाना सीखने आया था, और computing overall के बारे में बहुत कुछ सीख गया
वाकई बढ़िया