Cavescape, il diario di bordo

Ho scritto questo post quasi due mesi fa. Sono riuscito a rimandarne la pubblicazione promettendomi di farlo solo quando avrei avuto per le mani qualcosa di concreto di cui parlare. Cavescape, nome che sostituisce il precedente Caves & Creatures, è il mio primo, basilare, motore per lo sviluppo di un roguelike. Parlo di motore o engine (parecchio più cool) perché l’obiettivo del progetto è imparare e sviluppare un framework, più che tendere a un prodotto finito. Come è già stato per Adventurescape, l’engine per librogame digitali, del quale abbiamo parlato un bel po’ di tempo fa. Il codice Java/Android in quel caso è bello che pronto nell’hard disk, con tanto di una versione giocabile di Fuga dall’Altrove. Sta bene lì e vi saluta tutti.

I motivi di questo approccio sono semplici, riconducibili al fatto che non ho interesse a gestire quello che viene dopo (grafica, packaging, deployment, test, bugfixing,…). C’è già LGC a richiedere correzioni e qualche nuova feature, il resto delle ore giornaliere, anche volendo scavare tra quel che resta del sonno, gli allenamenti, i libri e qualche film, non basterebbero a farmi arrivare a un risultato soddisfacente. L’obiettivo è imparare e divertirsi. Quando uno di questi due pilastri cede, il gioco finisce.

cavescape-fov-animation

Iniziamo citando le fonti d’ispirazione, partendo da Game Programming Patterns, l’ottimo libro di Robert Nystrom (il suo blog è, come si usa dire, una miniera inesauribile di consigli e idee). Affascinato dallo sviluppo multipiattaforma (e incuriosito dagli articoli dell’amico Marco Giorgini) ho lasciato Javascript e Java per l’ambiente Microsoft (Visual Studio Community 2015) e riprendendo le vie del C# (per altro non dissimili dalle vie del Java). Passare da Eclipse a Visual Studio è come godersi qualche ora di ferie ogni giorno.

L’idea è nata strisciando, insinuandosi tra i rilasci di LGC, alimentata dalle partite a Brogue (erede di Rogue e NetHack, alla base dei giochi appena citati e di molti altri roguelike) e Crypt of the Necrodancer, How To Host A Dungeon di Tony Drowler (un dungeon builder in solitario del quale abbiamo già parlato qui e qui) e DungeonStorming dei sempre bravi Matteo Cortini e Leonardo Moretti, così come dalla lettura di articoli sul legame tra informatica e matematica nella generazione procedurale (fino agli incredibili estremi dell’imminente No Man’s Sky) o di racconti fantasy (come quel Red Nails di Robert E. Howard, con la sua città sotterranea e dimenticata).

Lo sviluppo iniziale è stato suddiviso in tre parti: ambiente, protagonisti, interazioni, mantenendo il focus sulla scrittura di codice il più possibile generico e modulabile.

Ho iniziato dai classici algoritmi di dungeon building. Essenzialmente questi ruotano attorno a uno schema di base, lo stesso dai tempi di Angband: 1) posizionare a random delle stanze in uno spazio vuoto 2) connettere le stanze con corridoi 3) risolvere eventuali incongruenze, mettere porte e altri elementi. Ne esistono innumerevoli varianti, tra le quali l’interessante versione (trovata sul blog di Nystrom, ispirata da un post di u/FastAsUcan sul reddit /r/roguelikedev a sua volta ispirato al builder di Jamis Buck’s) che lavora in maniera opposta: 1) crea un labirinto di corridoi 2) ne accorcia alcune parti ricavando spazio per le stanze 3) aggiunge le stanze e le porte.

Entrambe le soluzioni sono ottime e adattabili senza troppa difficoltà, ma in generale trovo i risultati troppo “dungeon oriented”. Okay qualche corridoio e okay che si chiamano dungeon crawler (e non room wanderer, sempre che questo termine abbia senso) ma il risultato è troppo sbilanciato (per i miei gusti). Così me ne sono scritto uno che generasse livelli con meno corridoi, più stanze interconnesse e una struttura caotica. Le room vengono scavate a partire da quella iniziale, sui lati liberi, proseguendo fino a raggiungere un certo numero o a saturare lo spazio. Le uscite rimaste vengono connesse da corridoi, dove possibile, il resto viene murato. C’è ancora lavoro da fare (okay lo so ci sarà sempre ancora lavoro da fare), soprattutto buttar giù il codice per altri builder, da innestare quando ci sarà bisogno di livelli ad hoc. Ne prevedo almeno uno che dia risultati più organici (sfruttando i sempre classici automi cellulari) e una miglior parametrizzazione per gestire i casi in cui volessi tornare a una struttura con più corridoi che stanze.

Ho quindi  introdotto modifiche strutturali, casuali in un range prefissato, tramite dei “semi” impiantati in fase di build e fatti evolvere a livello completato. Zone allagate, zone infestate dall’erba, e un inizio di approccio di sovrapposizione (banalmente se la sezione acqua tocca la sezione erbosa dà origine a una nuova sezione “acquitrinosa”). Voglio poter generare rovine scavate nella roccia delle montagne, catacombe di re stregoni, paludi abitate da esseri semi senzienti la cui memoria è ormai perduta, boschi impenetrabili e valli innevate battute da venti inesorabili.

cavescape-dungeon-building
Il dungeon builder in azione

Non amando i giochi in cui nello stesso livello convivono goblin, lupi, corvi (sottoterra?!) mummie, elfi, mummie elfiche, goblin e altro ancora, in Cavescape queste “zone” saranno le basi per la generazione della popolazione del livello. Una zona umida attirerà anfibi (rane giganti, uomini-pesce, progenie di Dagon, fate voi) mentre un livello con abbondanza di antichi tumuli mortuari sarà abitato da file di ritornanti (scheletri, zombie,…) così come il deserto pullulerà di scorpioni giganti mutanti. Credo abbiate il panorama.

I protagonisti sono al momento definiti da una classe generica, le cui caratteristiche vengono definite da file esterni. Mi sto concentrando sul buon vecchio “eroe”, i “cattivi” sono limitati a una scimmia scattante e una lumaca gigante e lentissima. Niente roditori taglie forti, ma solo per ora. Ho apprezzato la generazione casuali di mini boss in Dungeon of Slyn e punto a qualcosa del genere. Il lavoro sta tutto nel riuscire a scrivere una base di dati semplice e flessibile, con razze e individui.

Per ultime sono arrivate le interazioni, quindi algoritmi come il pathfinding e la base per il FOV. La parte più matematica per ora, per la quale ho attinto al mio stesso codice (portando con pochissime modifiche la funzione A* dal Javascript al C#). Per il FOV ho scelto di usare lo Shadow Casting. La scrittura si è dimostrata più semplice di quanto temessi, partendo dall’algoritmo ricorsivo. La mia versione C# è sicuramente migliorabile, ma funziona a dovere, non mostra artefatti e soprattutto ha il livello di indirection che volevo, grazie a due callback (una per leggere lo stato della Tile e una per impostarne la visibilità per ora limitata a uno stato binario).

Cavescape Shadow Casting
L’algoritmo di SC che spalma il FOV nell’area di gioco
Cavescape Dungeon Graph
Il grafo del livello, da usare in futuro per tracciare percorsi, quest,…

Chiudo l’articolo con la parte a cui ho messo mano davvero poco. Non c’è engine senza una qualche interfaccia, necessaria a mostrare i risultati e leggere l’input del giocatore. L’obiettivo principale del progetto è riuscire a ottenere il maggior livello di decoupling possibile, trovandosi “sul tavolo” un framework per roguelike (pathfinding, fov, elementi geometrici discreti,…), un motore di gioco e almeno un’interfaccia (ASCII, pixel art,…).

Il “comparto grafico” di Cavescape, virgolette d’obbligo, è per ora ridotto al minimo indispensabile. C’è una GUI in .NET che risponde agli eventi sollevati dall’engine e disegna quadretti colorati su un Panel. Ho inserito, nel lunghissimo elenco delle “cose da fare”, lo studio di MonoGame/XNA per avere qualcosa di portabile (almeno Windows Desktop e Windows Mobile). Me la tengo per, credo, i primi giorni d’autunno.

I prossimi passi? Classi comportamentali per le entità, oggetti, inventario, attributi e caratteristiche. Vi terrò informati.

Print Friendly, PDF & Email

Vuoi rispondere?