- Common Lisp oyun geliştirme ortamı üzerinde ECS mimarisini ve metadilsel programlama tekniklerini gerçek bir dungeon crawler örneğiyle genişleten bir öğreticidir
- Tiled XML haritasını cl-tiled ile okuduktan sonra CLOS nesnelerini doğrudan kullanmak yerine ECS bileşenlerine aktararak render, çarpışma ve bellek yönetimini ayırır
- Tile prefab’ları, görüntü işaretçileri, ebeveyn-çocuk indeksleri ve finalizer’ları birleştirerek yinelenen yükleme ve double free sorunlarından kaçınır; Tiled özel özelliklerini veri gibi kullanır
- Oyuncu ve düşmanlar ECS sistemleriyle hareket, animasyon geçişi ve çarpışma işlemlerini yürütür; düşmanlar cl-astar tabanlı A* yol bulma ile duvarlardan kaçınarak takip eder
- Nuklear tabanlı UI, anlatı nesneleri, duraklatma ve kazanma koşulları da eklenerek yaklaşık 500 satırlık küçük bir dungeon crawler örneği tamamlanır
Projeye başlama ve temel çalıştırma
- Part 1’de ele alınan Entity-Component-System mimarisi ve metadilsel programlama teknikleri kullanılarak UI’ı olan küçük bir dungeon crawler yapılır
- Çalıştırılabilir demo ikili dosyası ve kaynak kodu ecs-tutorial-2 GitHub deposunda bulunur
- Geliştirme ortamı, Part 1’deki Common Lisp oyun geliştirme ortamını varsayar; SBCL REPL’de Quicklisp dağıtımı güncellenir
(ql-util:without-prompting (ql:update-all-dists))
- cookiecutter-lisp-game şablonuyla yeni
ecs-tutorial-2projesi oluşturulur; örnekte backend olarakliballegroseçilir - Proje dizini Quicklisp
local-projectsiçine bağlandıktan sonrasrc/main.lispiçindeki pencere boyutu 1280×800 olarak değiştirilir (ql:quickload :ecs-tutorial-2)ve(ecs-tutorial-2:main)çalıştırıldığında belirtilen çözünürlükte siyah bir pencere ve FPS sayacı görüntülenir
Tiled haritası ve ECS deposu
- Dungeon haritası üretmek için açık kaynak harita editörü Tiled kullanılır
- Tiled, çapraz platform ve çapraz motor bir araçtır; harita verilerini XML olarak kaydeder
- Common Lisp’te cl-tiled, Tiled dosyalarını Lisp nesneleri olarak yükler
- Örnek tileset olarak Dungeon Tileset II - Extended kullanılır
- Orijinal 16×16 tile’lar küçük olduğundan ImageMagick ile %200 büyütülerek 32×32 tile olarak kullanılır
level1.tmxve tileset dosyası, öğreticide sağlananResources.zipüzerinden alınabilir
ecs-tutorial-2.asddosyasınacl-tiledbağımlılığı eklenir ve harita yükleme/görüntüleme kodunu ayırmak için yenisrc/map.lispoluşturulursrc/package.lispiçindecl-tiled,tiledadlı yerel takma adla kaydedilir
CLOS nesnelerini ECS bileşenlerine taşımanın nedeni
cl-tiled, harita verilerini CLOS nesneleri olarak döndürdüğü için REPL’de keşfetmesi kolaydır- Oyun döngüsünde bu nesneleri doğrudan kullanmak runtime dispatch maliyetini artırabilir
- 1280×800 pencereyi 32×32 tile’larla doldurmak için en az 40×25 = 1000 tile gerekir
- Ayrı bir demoda, 12 çekirdekli Ryzen 5 3600’de harita render’ı açıldığında FPS 20.000’den 600’e düşer
- Kare başına yaklaşık
1/600 - 1/20000 = 0.0016saniye, yani 1,5 ms’den fazla ek yük oluşur
cl-tiledtarafından okunan verilercl-fast-ecsdeposuna taşındığında dispatch azaltılabilir ve CPU önbelleği kullanımı iyileştirilebilircl-fast-ecsbağımlılığı eklenir;initiçindeecs:make-storage,updateiçindeecs:run-systemsçağrılır
Harita, tile ve prefab bileşenleri
map, yüklenen harita entity’sini işaretleyen bir etiket bileşendirmap-tile, tekil tile’ı temsil eder ve duvar ya da kapalı kapı gibi engel durumları içinobstacleBoolean slot’una sahiptirparentbileşeni, tile ve harita ile ilgili nesnelerin hangi harita entity’sinin çocuğu olduğunu gösterirentityslot’una:index childrenbelirtilerek belirli bir ebeveynin çocuk entity’leri hızlıca bulunur- İndeks, open-address hash table tabanlı olduğundan ortalamada O(1) sorgu sağlar; ancak oluşturma ve silme sırasında güncelleme maliyeti vardır
ecs:*entity-deleting-hook*içine bir hook eklenerek ebeveyn entity silindiğindechildrenindeksiyle bulunan çocuk entity’ler de birlikte silinirimagebileşeni yalnızcaALLEGRO_BITMAPiçin bir C işaretçisi saklar- Tileset görüntüsü
al_create_sub_bitmapile 32×32 parçalara ayrılır ve işaretçi saklanır
- Tileset görüntüsü
map-tile-prefab, Tiled tile’ının global ID’si olangiddeğerine sahip tile prefab’ıdırgidiçin:index map-tile-prefab :unique tbelirtilerek ID üzerinden tek bir prefab entity bulunur- Harita üzerindeki gerçek tile, prefab’ın
imagegibi bileşenlerini kopyalar; konumu ise ayrı birpositionbileşeniyle tutulur
imagefinalizer’ı, yalnızca entitymap-tile-prefabolduğundaal_destroy_bitmapçağırır- Bunun nedeni, aynı
ALLEGRO_BITMAPişaretçisinin birden çok harita tile’ı tarafından paylaşılması nedeniyle double free durumundan kaçınmaktır
- Bunun nedeni, aynı
positionvesize, ekran koordinatlarını ve boyutusingle-floatolarak saklarliballegro, OpenGL uyumluluğu nedeniyle ekran koordinatlarını tek duyarlıklı kayan nokta olarak ele aldığından aynı yaklaşım izlenir
Görüntü render’ı ve harita yükleme
render-imagessistemi,positionveimagesahibi entity’leri render ederal_hold_bitmap_drawingile sprite batching açılıp kapatılıral_draw_bitmapile görüntü belirtilen koordinatlara çizilir- Prefab’ların
positiondeğeri olmadığından bu sistem tarafından işlenmezler
load-bitmap,al_load_bitmapçağrısınıal:ensure-loadedile saran bir görüntü yükleme işlevidirtile->spec, tile prefab’ı oluşturmak için ECS nesne belirtimi üretir- Ebeveyn harita entity’si
- Tile görüntü parçası
- Tiled global tile ID’si
- Tile boyutu
load-tile-prefab,map-tile-prefabindeksiyle prefab’ın zaten yüklenip yüklenmediğini kontrol eder; yoksamake-objectile oluştururload-tile, gerçek harita tile entity’sini oluştururken bileşenleri prefab’dan kopyalar vepositioneklerload-map,tiled:load-mapile okunan CLOS nesnesinden tileset’leri ve katmanları dolaşır- Tileset görüntüsünü yükler ve her tile’ı prefab olarak oluşturur
- Tile katmanındaki her hücreyi entity’ye dönüştürür ve prefab verilerini kopyalar
- Tiled’in katman sırası editördeki sırayla korunur;
make-entityartan entity numaralarını garanti eder- Sistem eski entity’lerden başlayarak işlediği için üst katmandaki tile’lar daha sonra çizilir ve alt katmanları örter
- Tüm tile’ları ayrı entity’ler olarak saklamak tek doğru yanıt değildir; statik haritayı önceden bir buffer’a render etmek de mümkündür
Tile animasyonları
- Tiled animasyonlu tile’ları desteklediği için meşale veya sihirli çeşme gibi öğeler temsil edilebilir
- Ortak bileşenleri ve animasyonla ilgili bileşen/sistemleri ayırmak için
common.lispveanimation.lispeklendi animation-framebileşeni animasyonun tek bir karesini temsil edersequenceanimasyon adıdır vekeywordtüründe saklanırsequence-framesindeksiyle belirli bir animasyonun kareleri bulunurdurationsaniye cinsinden kare süresidir
animation-stategerçek harita üzerindeki animasyonlu tile’ın mevcut durumunu saklar- mevcut
sequence - mevcut
frame - mevcut karenin
durationdeğeri - mevcut karenin gösterildiği
elapsedsüre
- mevcut
- Kare geçiş kodunu daha kısa yazmak için
let-plusbağımlılığı eklendi update-animationssistemielapseddeğerinidtkadar artırır ve süre aşılırsa sonraki kareye geçer- Kare süresi büyük bir
dtdeğerinden küçük olabileceği için kaç karenin atlanması gerektiğinifloorile hesaplar - Kare numarası liste uzunluğunu aşarsa başa sarması için
truncatekullanır imageiçindeki bitmap işaretçisini sonraki kare prefab’ının bitmap’iyle değiştirir
- Kare süresi büyük bir
- Animasyon süresi Tiled’da milisaniye olarak saklandığından
animation->speciçinde saniyeye dönüştürülür instantiate-animation, gerçek tile entity’sindeanimation-stateoluşturur ve aynı animasyonların tamamen senkronize olmaması içinelapseddeğerini 0 iledurationarasında rastgele bir sayıyla başlatır- Animasyonlu tile’lar Tiled özelliği
"sequence"değerine sahip olmalıdır- Bu özellik yoksa
NILadıyla yüklenir, beklenen animasyon adıyla bulunamaz ve tür hatasına yol açabilir
- Bu özellik yoksa
Oyuncu karakteri ve kontroller
character.lispeklendi ve hareket edebilen karakterler içincharacterbileşeni tanımlandıspeedsaniye başına piksel cinsinden hızdırtarget-x,target-yhareket hedef koordinatlarıdır- İlk hedef değerleri
single-float-nanolarak ayarlanır; böylece yeni karakterler sebepsiz yere sol üste hareket etmez
playeretiket bileşenibitslotu ve:index player-entity :unique tkullanır- Bu,
(player-entity 1)ile oyuncu entity’sini O(1) sürede bulmaya yönelik bir yapıdır - Oyuncu entity’si global değişkende saklanmaz
- Bu,
- İlk uygulamada tileset’ten ork görseli kesilip
player.pngyapıldı veload-playerile oyuncu hardcoded olarak oluşturuldu- Konum
(64.0, 64.0) - Boyut 32×32
- Hız 100.0
- Konum
move-characterssistemi karakteri hedef noktaya kadar hareket ettirirNaNhedef koordinatı varsa mevcut konumla başlatır- Kayan noktalı sayıları doğrudan karşılaştırmak yerine
approx-equalkullanır - Yeni koordinatları
atan,cos,sin, hız vedtile hesaplar
control-playersistemiW,A,S,Dtuş girişlerini okuyarak hedef koordinatları güncelleral:with-current-keyboard-stateveal:key-downkullanır- Ekran sınırlarının dışına çıkılmaması için
clampkullanır NaNbaşlatma sorununu önlemek için:after (move-characters)ile hareket sisteminden sonra çalışır
Tiled özellikleriyle çarpışma ve obje yükleme
- Başta duvarlar zemin tile’larıyla aynı türden normal görseller olduğu için oyuncu duvarların içinden geçiyordu
- Tiled’ın özel türleriyle
map-tilesınıfı oluşturuldu veobstacleBoolean üyesi eklendi- Duvar tile’larının özelliği olarak
map-tileeklendi veobstacleişaretlendi
- Duvar tile’larının özelliği olarak
properties->specfonksiyonu Tiled özellik hash tablosunu ECS obje tanımına dönüştürür- Tiled özel sınıfları bileşen olarak işlenir
- Sınıf üyeleri bileşen slotları olarak işlenir
- Örnek biçim
((:map-tile :obstacle t))şeklindedir
load-tile-prefab,properties->specsonucunu prefab tanımına dahil eder- Özellik yoksa
spec-adjoinile varsayılanmap-tilebileşeni eklenir veobstaclevarsayılannildeğerini alır
- Özellik yoksa
positionbileşeninetile-hashslotu vetilesindeksi eklenditile-hash,xveydeğerlerini tamsayıya çevirip tek bir 64 bit tamsayıya paketler- Belirli bir tile’ın sol üst koordinatındaki tüm entity’ler
tilesindeksiyle bulunur
tile-start, herhangi bir koordinatın içinde bulunduğu grid tile’ının sol üst koordinatını döndürürtile-obstacle-p, aynı koordinattaki entity’ler arasındamap-tileolupobstacledeğeri doğru olan bir tile bulunup bulunmadığını kontrol ederobstaclep, herhangi bir koordinat için ilgili tile’ın engel olup olmadığını denetlercontrol-player, hareket yönüne göre karakter dikdörtgeninin ilgili köşe tile’larını kontrol eder ve engel varsa hedef koordinatları mevcut konuma geri alır- Bu çarpışma yöntemi kusursuz değildir
- Karakter merkez koordinatını temel alan bir tasarım matematiği ve kodu basitleştirebilir; ancak örnek, karmaşıklıktan kaçınmak için mevcut yöntemi korur
Haritadan oyuncu ve animasyonlu karakter yükleme
- Tiled’a
characterveplayerözel sınıfları eklendicharacteryalnızcaspeedfloat üyesine sahiptir- Varsayılan değerlerin kullanılması için
target-x,target-yatlandı player, varsayılan değeri 1 olanplayerint üyesine sahiptir
- Tiled obje katmanına oyuncu karakteri tile objesi olarak yerleştirildi ve
characterileplayerözellikleri verildi load-map,tiled:object-layerişleyecek şekilde genişletildi- Obje özelliklerini
properties->specile ECS bileşenlerine dönüştürür tiled:tile-object,load-tileile tile verisini ve animasyonu kopyalar, ardından konumu ayarlar- Tiled obje koordinatları sol altı temel aldığı için, sol üst temele uydurmak üzere
ydeğerinden obje yüksekliği çıkarılır
- Obje özelliklerini
- Hardcoded
load-playerçağrısı ve fonksiyonu kaldırıldı - Bu yapı, Tiled harita verilerini doğrudan ECS objeleri olarak okuyan bir yaklaşımdır ve veri odaklı programlamaya yaklaşır
- Karakter animasyonları, ork için
orc-idleveorc-runsekansları tileset’te tanımlanarak kullanılır change-animation-sequence, entity’nin mevcut animasyonunu değiştirir- Zaten aynı sekanssa hiçbir şey yapmaz
- Yeni sekansın ilk karesini
sequence-framesindeksiyle bulupanimation-stateveimage-bitmapdeğerlerini günceller
move-characters, karakter duruyorsa:orc-idle, hareket ediyorsa:orc-runsekansına geçer
Düşmanlar, Oyun Bitti ve A* Yol Bulma
enemybileşeni, düşman davranışı için gereken iki slota sahiptirvision-range: oyuncuyu görüp tepki vermeye başladığı mesafeattack-range: saldırı menzili
- Oyunu sonlandırmak için
*should-quit*global değişkeni eklenir; ana döngü bu değer doğruysa sona erer handle-enemiessistemi, oyuncu koordinatlarını alıp düşmanlarla karşılaştırır- Oyuncu görüş menzilindeyse düşmanın hedef koordinatları oyuncunun konumuna ayarlanır
- Oyuncu saldırı menzilindeyse
*should-quit*doğru yapılır veYou diedyerel mesaj kutusu gösterilir
- Düşman animasyonları
demon-idle,demon-runsekanslarını kullanırmove-characters,has-player-psonucuna göre oyuncu için ork animasyonunu, düşman için demon animasyonunu seçer
- Doğrudan takip yaklaşımında düşmanlar da duvarların içinden geçtiği için A* yol bulma eklenir
- cl-astar bağımlılıklara eklenir
- Bu kütüphane, probleme göre özelleştirilmiş en iyi yol bulma fonksiyonunu makro ile üretir
- Yol, bileşen slotundaki bir dizi olarak saklanmaz; her yol noktası ayrı bir entity olarak temsil edilir
path-point,x,y,travelleralanlarına sahiptir;travellerüzerindepath-pointsindeksi vardırpath, nihai hedef olandestination-x,destination-ydeğerlerini saklarcharacterhedef koordinatları bir sonraki yol noktasını,pathise nihai hedefi ifade eder
follow-pathsistemi ilk yol noktasını alıp karakteri o noktaya doğru hareket ettirir- Noktaya ulaşıldığında ilgili
path-pointentity'si silinir - Artık nokta kalmazsa
pathbileşeni silinir
- Noktaya ulaşıldığında ilgili
find-path,a*:define-path-finderile tanımlanır- Dünya boyutu, pencere boyutunun tile boyutuna bölünmesiyle hesaplanır
- Row-major indeksleyici kullanılır
- Hedefe ulaşılıp ulaşılmadığı, tile koordinatlarının aynı olup olmamasına göre belirlenir
- Komşular 8 yönde listelenir
- Engeller veya bir engelin üzerinden çapraz geçişler,
most-positive-single-floatmaliyetiyle pratikte imkânsız hale getirilir - Heuristik olarak octile distance kullanılır
- Mevcut bir yol varsa yol noktaları silinir ve yeni
pathatanır - Sonuç yolundaki her nokta,
path-pointveparentiçeren bir entity olarak oluşturulur
handle-enemies, düşman oyuncuyu gördüğünde mevcut yol hedefi oyuncunun konumundan farklıysafind-pathçağırır- Değişiklikten sonra düşman, oyuncuyu kovalar ama engellerden kaçınarak hareket eder
Nuklear tabanlı oyun UI'ı
- Anlatı öğeleri için GUI gerekir; ancak Qt veya GTK gibi geleneksel GUI kütüphaneleri,
liballegrografik bağlamına çizilen oyun UI'ları için uygun değildir - UI kütüphanesi olarak Nuklear kullanılır
liballegroile birlikte kullanmak için Common Lisp binding'i cl-liballegro-nuklear vardır- Binding, deklaratif arayüz için bir DSL de sağlar
cl-liballegro-nuklear/declarativebağımlılığı eklenir ve yeni dosya olaraksrc/narrative.lispeklenir- Pakete
uiyerel takma adı kaydedilerekcl-liballegro-nuklear/declarativekısa şekilde referanslanır - UI fontu olarak Google Fonts'tan Alegreya kullanılır ve dosya adı
alegreya-sc.ttfolarak değiştirilir ui:defwindow narrative, anlatı penceresi fonksiyonunu tanımlar- Pencere konumu ekranın orta bölgesine göre hesaplanır
ui:label-wrapile otomatik satır kaydırmalı metin gösterilirui:button-label "Ok", tıklandığında doğru değer döndürür
- Nuklear bir immediate mode UI kütüphanesidir
- Widget nesnelerini bellekte tutan retained mode yerine, her karede render ve işleme yapar
- Buton tıklamaları callback ile değil, her karede dönüş değeri ve koşullarla işlenir
main.lisp, UI fontunu yükler venk:allegro-initile UI context'ini başlatır- Event loop içinde
nk:input-begin,nk:allegro-handle-event,nk:input-endçağrılır - Render sırasında
nk:allegro-renderçağrılır - Kapanışta
nk:allegro-shutdown,nk:allegro-font-delçağrılır
- Event loop içinde
UI skini ve anlatı nesneleri
- Varsayılan UI sade olduğu için Kenney'nin
fantasy-ui-bordersgörsel asset'leri kullanılarak stil verilir *window-background*,*button-normal-background*,*button-hover-background*,*button-active-background*global değişkenlerinde UI görselleri saklanırload-ui, görsellerink:allegro-create-imageile yükler;unload-uiise C tarafındaki görsel kaynaklarınınk:allegro-del-imageile serbest bırakırinitiçindeload-uiçağrılır; ana döngü sonlanırkenunload-uiçağrılırui:defwindow'un:stylesargümanıyla arka plan, buton durumlarına göre görseller ve metin rengi belirtilirnarrativebileşeni, çevresel hikâye anlatımı nesnesini temsil edertext: gösterilecek metinshown: daha önce bir kez gösterilip gösterilmediğiactive: mevcut pencerenin aktif olup olmadığıactiveüzerindeactive-narrativesindeksi bulunur
show-narrativesistemi, oyuncu anlatı nesnesinin yakınındaysa pencereyi gösterir- Etkileşim mesafesi
+interact-distance-factor+ve oyuncunun tile boyutuyla hesaplanır - Pencere zaten aktifse, henüz hiç gösterilmemişse veya
Etuşuna basılırsa gösterilir - Pencere
Okbutonu,Esc,Space,Enterseçeneklerinden biriyle kapatılır
- Etkileşim mesafesi
- Tiled'da
narrativeözel tipi oluşturulur vetextstring üyesi eklenir - Çarpışma algılamasıyla uyumlu olması için içinden geçilemeyen nesnelerin koordinatları tile ızgarasına hizalanmalıdır
- Anlatı penceresi açıkken zindanın hareket etmeye devam etmesi sorunu, sistem çalıştırma koşuluyla engellenir
move-charactersvecontrol-playerüzerine:when (null (active-narratives t))eklenir- Aktif bir anlatı varsa hareket ve kontrol sistemleri çalıştırılmaz
- Kazanma koşulu
wintag bileşeni olarak eklenirnarrativeile birlikte eklenmiş bir nesnede pencere kapatıldığında*should-quit*doğru yapılır ve oyun biter
Kapanış ve kapsam
- Nihai örnek,
cl-fast-ecs,cl-tiled,cl-astar,cl-liballegro-nuklearkullanarak çevresel hikâye anlatımı, düşman yapay zekası ve GUI içeren Souls-like bir dungeon crawler oluşturur - Uygulama boyutu yaklaşık 500 satır koddur
- Tüm kod GitHub deposunda bulunur; eğitim kodunun yanı sıra
declaimkullanılan isteğe bağlı tip bildirimlerini de içerir - Ses tasarımı, ara sahneler, ana menü, seviye geçişleri, “door problem” gibi konular ele alınmaz
- Autumn Lisp Game Jam 2024, 25 Ekim 2024'te itch.io'da düzenlenir; Lisp lehçeleriyle 10 gün boyunca oyun yapıp birbirini değerlendirme ve geri bildirim verme etkinliğidir
- Bu bölüm, Spring Lisp Game Jam 2023 katılımı Thoughtbound temel alınarak hazırlanmıştır
- Bir sonraki bölümde ölçeği büyütüp daha gelişmiş yapay zeka ekleyerek gerçek zamanlı strateji oyunu yapma mücadelesi duyurulur
1 yorum
Hacker News yorumları
Keşke tüm teknik eğitimler böyle olsa. Yazının kurgusu iyi, dilbilgisi hatası neredeyse yok ve her yeni konuyu tanıtırken tam kararında açıklıyor; ayrıca tam kod örnekleri ve kodun gerçekte ne yaptığını gösteren görseller de var
Konuyu derinlemesine ele alacak kadar uzun, ama 1. bölümü okumamış ve birkaç yıl önce Common Lisp’i birkaç ay kurcalamış biri için bile takip edilebilecek kadar bağımsız. Clojure ve Emacs Lisp ile epey uğraştım
Bravo, awkravchuk/Andrew :^)
(https://mxjn.me/2024/10/17/1 üzerinden de paylaşılmış)
Çevrimdışı kullanım veya arşiv için kolayca kaydedilebilmesi, not alınabilmesi ve kolay aranabilmesi de ayrı avantaj
Common Lisp hakkında harika bir proje ya da yazı kadar teknik tarafta insanı duygulandıran çok az şey var. Gerçekten büyük bir hediye gibi bir yazı
Sadece
package.shve genel olarak 3 işletim sisteminde derleme yönetimi bile başlı başına bir ustalık dersi. Sadece GitHub deposuna göz gezdirerek bile çok şey öğrendimGenelde SBCL ya da LispWorks ile Common Lisp komut satırı uygulamaları derliyorum ama bir sonrakinde ECL denemeyi düşünebilirim. Hem macOS hem Linux derlemelerine sahip olmak güzel ve yeni bir şey denemek de eğlenceli olabilir
Çok iyi bir yazı. Lisp, daha doğrusu ClojureScript ile çok oyunculu üçüncü şahıs büyü tabanlı bir shooter geliştiriyorum. Web tabanlı bir 3D oyun ve proje için yaptığım araçlar ile soyutlamalar da dahil olmak üzere bu yolculuğu blogda yazmayı planlıyorum
İlgilenenler için demo burada: https://wizardmasters.io
Yazı gerçekten sağlam ama 1. bölümdeki kurulum sürecinin Common Lisp’in kendisi, Python, C ve birden çok aşamaya yayılması, CL’nin neden özellikle genç programcılar arasında o kadar popüler olmadığını gösteriyor gibi
Üzücü bir durum ve keşke biri kurulum açısından dili daha erişilebilir hale getirmek için uğraşsa
Anladığım kadarıyla daha çok çok fazla seçenek olması ve eski varsayılanların demode görünmesi sorununa odaklanıyor
Olay döngüsü,
loopun ne kadar ciddi bir yineleme için alan-özel dil olduğunu gösteren harika bir örnek. Sevseniz de sevmeseniz de ;)loopyerine https://iterate.common-lisp.dev/ kullanılamaz mı? S-ifadesi olmayan tuhaf bir sözdizimi yok ve Lisp sözdizimine geri dönmek içindoda gerekmiyorÇirkin
else/endolmadan normalif/whenkullanıyorsun ve genel olarak kullanışlı özellikler de ekliyorloopen sevdiğim CL bileşenlerinden biri olduBu yazı bana "Caves of Clojure"u hatırlattı: https://stevelosh.com/blog/2012/07/caves-of-clojure-01/
Tam da bu hafta Python ile bir roguelike geliştirmeye başlamıştım; bunu Lisp ile yapmak da güzel olabilir
Kendimi kandırılmış hissediyorum. Basit bir oyun yapmayı öğrenmek için geldim ama bunun yerine genel olarak bilişim hakkında inanılmaz derecede çok şey öğrenmiş oldum
Gerçekten harika