Chápu snahu o jednoduchost, ale co separace grafiky od herní logiky? ;-) . Hodit všechno do jedné struktury gameState je dost velký mix.
Kdysi hodně dávno jsem napsal v SDL (později upraveno na SDL2) hračku wormik: https://github.com/kvr000/wormik.git , dokonce ji někdo dostal do nějaké distribuce (teď bych ji musel asi trochu oprášit, aby fungovala, jsou tam nějaké hardcoded cesty k resources a fontům apod). Port SDL na SDL2 nebyl úplně přímočarý, ale zase žádná velká tragédie (matně si vzpomínám, že největší problém bylo SDL2-ttf, které stále pracovalo s původním software surface). V každém případě SDL2 je asi logicky jediná volba dnes.
Je otázka, zda 2D grafika má ještě moc velký smysl, na LCD se pak stejně mění scaling a výkonově je to asi už jedno (nebo možná i horší?). Přišlo mi, že ty různé alpha blending apod nejsou úplně přímočaré a hlavně závislé na kontextu, takže bych si tipnul, že možná přijde další upgrade, až budou chtít využít Vulkan backend (ale zkušenosti s 3D mám malé, tak je to jenom odhad).
Jinak mi zatím z uvedených příkladů nepřijde až tak velký rozdíl mezi Go a C++, obzvláště, když se původní "defer delete whatever" přesunul do jedné funkce, která ještě možná není korektní v případě, že se povede jen částečná inicializace. Čímž nechcu podceňovat možnosti jazyka, ale tady bude ta logika docela jednoduchá na to, aby nějak výrazně těch vlastností využila...
Jj s SDL (jednickou) uz dneska mohou byt i problemy pri instalaci zavislosti, dnes uz jen 2.
SDL2, kdyz se pouziji textury, renderuje taky pres texturovaci jednotku GPU, takze tam rozdil nebude. I pri modulaci, scalingu atd.
Rozdily mezi Go a C++ (nebo radeji spis C, protoze SDL je ceckovina) jsou treba v jednodussim handlingu chyb - proste chyba je bud nil nebo struktura s jejim obsahem. Nemusi se resit, jestli SDL_Init vraci zaporne cislo, funkce pro ziskani surface NULL a mraky dalsich vyjimek. Je to mozna malickost, ale pise se mi takto mnohem rychleji. Taky je fajn, ze proste muzu udelat strukturu ve funkci a vratit ukazatel na ni (tedy fajn - neni zapotrebi na to myslet).
Jinak je samozrejme pravda, ze pro takto primitivni veci se moc rozdilu nepozna. Zkusim nahodit to reseni s gorutina pro NPC, to mozna bude flame :)
PS: castecna inicializace - asi myslite, ze si nekdo zachyti panic() a bude pokracovat dal? Jj to muze nastat.
Tou částečnou inicializací jsem myslel, že třeba něco z SDL init, create window a create surface uspěje a něco ne. Finalize se v takovém případě nezavolá (v případě poslední varianty kódu). Je pravda, že ty objekty stejně posbírá GC v nějakém náhodném pořadí, ale korektní by bylo ty resources pozavírat slušně v opačném pořadí a pak teprve provést panic a "návrat" z funkce. Takže ta jednoduchost je zčásti za cenu korektnosti :-) . Vrácená chyba asi něco ušetří, ale stejně by byla volána na dalším řádku, který tu chybu reportuje jako SDL_error() nebo něco podobného. Exceptions s pouze optimistickým blokem by asi byly nejjednodušší, i když samozřejmě stále by měly uzavírat resources korektně. Ošetřování chyb je vždycky peklo :-)
Ohledně scalingu apod - jasně, performance asi bude celkem jedno, zvláště u takových hříček. Problém je, že 640x480 na celou obrazovku neodpovídá rozlišení LCD a původní hra předpokládá, že obrázek příšery nebo jiných ikon se mapuje 1:1 ze zdrojového obrázku na obrazovku. Třeba zmiňovaný Wormik používá asi 16x16 ikonky a celkové rozlišení 640x480 (počítám, že tady to skončí podobně). Kdyby se automaticky upravila velikost obrazovky podle rozlišení, mohly by být původní ikony větší a vyhlazovat se podle skutečného rozlišení. V zásadě je to pořád 2D, ale takové dynamičtější :-)
Ty goroutines jsou docela zajímavý nápad. V normální (C) hře by to asi zabilo výkon mutexama, ale Go s GC si může hrát s funkcionálními vlastnostmi a se snapshoty stavu, takže jednotlivé objekty se mezi sebou nemusí nutně synchronizovat. V podstatě stejně jako multiplayer po síti, akorát tady v rámci stejného runtime. Držím palce!
V té poslední verzi ale právě ty defer chybí. Místo toho je tam jedna finalize funkce, která se zavolá v případě, že celá inicializace skončila úspěšná. Obecně, ta výhoda defer se ztrácí, pokud se má výsledek vrátit a defer-ovat pouze v případě neúspěchu.
Ohledně GCD - mutexy byly myšleny na modifikaci sdíleného stavu jednotlivých objektů, to GCD nezachrání (pokud nebudu schopen v předstihu identifikovat, které objekty se mohou dostat do konfliktu). Ale možnost pracovat se snapshotem, který může potenciálně být outdated, by věc zjednodušit mohla. V C by se to dělalo o dost hůř, neboť by se musela řešit životnost snapshotu, což znamená opět nějakou synchronizaci. GC jazyky to zvládnou líp.
Jo, to s tím finalize jsem přehlédl. Tam by se skutečně mělo testovat na nil.
V GCD lze mít hodně front a určit serializovatelnost, když výběru serial namísto concurrent, žádné dva tasky nepoběží v rámci fronty zároveň (teoreticky na jednom vlákně, ale to si interně rozhoduje knihovna). Popravdě nevím, jestli to je efektivnější než konkurentní fronta a mutexy.