Ieri sera ho partecipato alla quarta sessione del webcast Video Game Development e questa volta sono rimasto piuttosto soddisfatto. Ho visto codice C# interessante, ho visto il mitico Star Trooper in movimento (con la limitatezza della velocità di rete) e mi sono entrate in testa diverse logiche a cui non avevo mai pensato. Non che fossero complicate o che richiedessero chissà quali conoscenze, però non avendo mai creato videogiochi, non ho mai pensato a come affrontare certi problemi e quale fosse il modo migliore per rendere tutto veloce e ben strutturato.
Ma andiamo con ordine.
Innanzitutto il gioco si chiama Star Trooper. Di cosa si tratta? E' un videogioco in 2D: mettete un'immagine di sfondo (background), un'astronave buona (la nostra) e una serie di nemici che tentano di distruggerci. Noi possiamo muovere la nostra astronave per lo schermo, i nemici si spostano, noi possiamo sparare e farli fuori. La visuale in breve è un po' come i classici videogame anni '80 (Space Invaders, Galaga e i vari cloni). Ho dato un'occhiata al sito del webcast per tentare di farvi vedere uno screenshot, ma non l'ho trovato. Pazienza.
In breve, il gioco 1) inizializza grafica e suoni e 2) comincia una sorta di loop infinito. Ad ogni loop, il codice calcola le posizioni dei nemici, prende l'input del giocatore, calcola le nuove posizioni degli oggetti su schermo, poi le renderizza a video. E via così fino alla fine. Non posso molto scendere nel dettaglio, in questo momento. Cito solamente le cose che mi sono sembrate interessanti e che mi hanno aperto gli occhi.
Ogni oggetto inserito nel gioco è un oggetto in C#. Background, StarTrooper (astronave buona) e Condor (astronavi cattive). Premetto che gran parte del codice era già scritto e Matthew il relatore (ormai lo chiamo così) ha mostrato solamente lo scheletro dell'applicazione (in pratica, a grandi linee il loop di cui ho parlato sopra). L'oggetto padre di tutti è Game. Ogni oggetto ha metodi come Render, Add o Play. L'inizializzazione consiste nel caricare le bitmap fisse(background), caricare i singoli frames BMP per le animazioni, impostare le posizioni iniziali degli oggetti, etc.
Concetto di sprite. Definizione letta e tradotta dal PDF: uno sprite è una picture mostrata davanti al background del gioco. Astronavi e proiettili sono tutti sprites. Ogni sprite deve memorizzare le sue proprietà: posizione, velocità, animazione (seguenza di picture) e frame corrente della stessa. Non solo: visibilità, se è attivo oppure no, zorder. Il concetto di attivo è particolare: quando uno sprite è inattivo, tutti i suoi behaviors non vengono elaborati, non viene preso in considerazione durante il mainloop del gioco e non è visibile sullo schermo. A cosa serve? A creare velocemente un altro oggetto uguale, attivo, a run-time, senza troppe complicazioni. Oppure per avere un oggetto che non serve quando il gioco parte, ma che deve apparire soltanto dopo un po'. Uhm, astronave di fine livello? :-)
Altra cosa. L'unica volta che ho sviluppato un gioco (Keyzard di cui ho parlato anche qui sul mio blog) dissi alla mia grafica (eh, che era anche la mia ex): "Ascolta, tu disegna i frame del pistolero che spara, produci tu un avi o un swf, poi dammelo. Al resto penso io!". Alla fine lei, lavorando con Flash, mi fece un SWF, io in VB6 ho messo l'ocx, carico il filmato all'avvio e faccio Stop. Poi, quando mi serviva, chiamavo il metodo Play e via così. Cosa diversa hanno fatto gli sviluppatori di Star Trooper: invece di caricare un filmato già fatto, hanno caricato singolarmente tutti i frame dai files BMP. Il metodo Play dell'oggetto (di cui non ho visto l'implementazione) fa tutto il resto. Gli oggetti animati del gioco sono: il background che contiene a scorrere, lo StarTrooper (che agita la coda in continuazione). I Condor, i cattivi, se ho ben capito, hanno un'animazione che li fa esplodere; l'animazione quindi parte realmente solo quando vengono colpiti: loro sono fermi, io sparo, li colpisco, metodo Play del Condor, e BOOM!!!
Il gioco fa uso di DirectX managed per grafica, input e suoni. Ho visto una cosa bella bella per la rilevazione dell'input dell'utente. Ad ogni loop, il gioco usa una function DX per riempire un'array di 256 bytes con lo stato true/false di ciascun tasto. Sarò anche scemo, ma non avevo mai pensato ad una soluzione del genere. Poi esploro l'array e capisco quali tasti sono premuti oppure no: queste cosa viene fatta 60 volte al secondo. Uao.
Il sorgente non è ancora disponibile per il download. Vero peccato, perchè ieri sera ne ho visto un bel po', però era già scritto, scritto da un altro e quindi non lo ricordo benissimo a memoria. Non solo: il gioco è composto da diverse classi generiche (tipo Animation, Frame, Game) il cui codice è rimasto dietro le quinte. Alla fine il succo è tutto lì. La classe Game è la radice di tutto: chiamo il metodo Add per ogni oggetto che voglio rappresentare nell'universo di gioco. Nel loop globale, faccio un For...Each di ogni oggetto chiamando Render e via così. Chiamo il Remove se un oggetto deve sparire.
Webcast molto piacevole e interessante. La voce di Matthew il relatore è sparita un paio di volte per qualche secondo, ma nulla di grave. Al di là delle questioni tecniche, del codice in sè, sono state viste tecniche molto ma molto belle. Normali e banali per qualcun'altro, magari, ma non per me.