SDL.NET

Mettiamo tutto insieme (Conclusioni) – Creazione di un gioco con SDL.NET

In realtà sotto questo punto di vista c’è tanto da scrivere ma anche poco da scrivere: tanto perchè ci sarebbe innanzitutto da compilare il nostro codice. Una volta ottenuti gli eseguibili funzionanti dobbiamo avere una precisa struttura della cartella del nostro programma. Ed a questo, come avete visto, ci abbiamo pensato in precedenza.

Dobbiamo testare a fondo il gioco, in modo tale da notare rallentamenti (non in questo caso ovviamente). Nel caso ci sia qualcosa che non va, si riprende il codice e lo si modifica, si cercano degli errori. Certo, questo è facile da dire ma spesso è assolutamente stancante da fare nella pratica. Nonostante abbia fatto questo gioco in due giorni scarsi c’è da dire che non era tutto perfetto da subito: man mano che andavo avanti ho fatto prove, compilato, ricompilato e modificato per poi compilare nuovamente.

Niente perchè, appunto, è facile da scrivere ma difficile da fare.

Una grande fortuna, devo ammetterlo, è stato quello di poter lavorare con un linguaggio managed come il C#, eliminando in questo modo tutta una serie di problemi che si potevano presentare con un linguaggio si più efficace, ma altrettanto complesso come il C++.

Adesso che abbiamo la nostra struttura pronta, non dobbiamo fare altro che creare un programma di installazione per il nostro gioco: potrete scoprire come si fa leggendo l’articolo dedicato a questo scopo su questo stesso blog.

Grazie per l’ascolto!

  • Share/Bookmark

Gestiamo l’Input. – Creazione di un gioco con SDL.NET

Il gioco che stiamo creando, ovviamente, non va mica avanti da solo. Qualcuno deve far muovere le cose, fare in modo che determinati eventi e determinati spostamenti avvengano. Quel qualcuno è il nostro giocatore. Come già spiegato nella “guida base” l’input è gestito facilmente dalle librerie SDL e applicare questi concetti alla pratica sarà ancora più facile.

In tutta la struttura del gioco c’è solo un metodo che si occupa di prendere tutto l’input ed elaborarlo: il metodo Events_KeyboardDown. Ecco il codice, riproposto qui di seguito, che verrà successivamente commentato in modo da essere compreso in tutto e per tutto.

[code]]czoyNDcwOlwiDQpwcml2YXRlIHZvaWQgRXZlbnRzX0tleWJvYXJkRG93bihvYmplY3Qgc2VuZGVyLCBLZXlib2FyZEV2ZW50QXJncyB7WyYqJl19ZSkNCiAgICAgICAgew0KICAgICAgICAgICAgc3dpdGNoIChzdGF0bykNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBjYXtbJiomXX1zZSBHYW1lU3RhdGUuSW50cm86DQogICAgICAgICAgICAgICAgICAgIGlmIChlLktleSA9PSBLZXkuUmV0dXJuKSBzdGF0byA9IEdhe1smKiZdfW1lU3RhdGUuR2FtZTsNCiAgICAgICAgICAgICAgICAgICAgaWYgKGUuS2V5ID09IEtleS5Fc2NhcGUpIHN0YXRvID0gR2FtZVN0YXR7WyYqJl19ZS5FeGl0Ow0KICAgICAgICAgICAgICAgICAgICBicmVhazsNCg0KICAgICAgICAgICAgICAgIGNhc2UgR2FtZVN0YXRlLkdhbWU6DXtbJiomXX0KICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKHN0YXRvX3ApDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5QcmltYToNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKGUuS2V7WyYqJl19eSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LntbJiomXX1SZXR1cm46DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0b19wID0gU3RhdG9QYXJ0aXRhLkluQ29yc287e1smKiZdfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVzZXRHYW1lKCk7DQoNCiAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgICBicmVhazsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleS5Fc2NhcGU6DQogIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdG8gPSBHYW1lU3RhdGUuRXhpdDsNCiAgICAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5JbkNvcnNvOg0KICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKGUuS2V5KQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LkxlZnRBcnJvdzoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICBpZiAocG9zaXppb25lX2dpb2NhdG9yZSA+IDApIHBvc2l6aW9uZV9naW9jYXRvcmUtLTsNCiAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICBicmVhazsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleS5SaWdoe1smKiZdfXRBcnJvdzoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwb3NpemlvbmVfZ2lvY2F0b3JlIDwgcG9zaXp7WyYqJl19aW9uaV9naW9jYXRvcmUuQ291bnQgLSAxKSBwb3NpemlvbmVfZ2lvY2F0b3JlKys7DQogICAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSBLZXkuRXNjYXBlOg0KICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlc2V0R2FtZSgpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgIHN0YXRvX3AgPSBTdGF0b1BhcnRpdGEuUHJpbWE7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3tbJiomXX07DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuRmluZToNCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN7WyYqJl19d2l0Y2ggKGUuS2V5KQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gIGNhc2UgS2V5LlJldHVybjoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRvX3AgPSBTdGF0b1BhcnRpe1smKiZdfXRhLlByaW1hOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgIGNhc2UgS2V5LkVzY2FwZToNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRvID0gR3tbJiomXX1hbWVTdGF0ZS5FeGl0Ow0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgfQ0KICB7WyYqJl19ICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICBjYXNlIEdhbWVTdGF0ZS5FeGl0Og0KDQogICAgICAgIHtbJiomXX0gICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgIH0NCiAgICAgICAgfQ0KXCI7e1smKiZdfQ==[[/code]

Innanzitutto diamo uno sguardo ai parametri in input: c'è il sender, di tipo generico object, e la variabile “e” di tipo KeyboardEventArgs. Proprio quest'ultima variabile sarà il perno di tutto il metodo.

Prima di dare uno sguardo al codice, inoltre, ricordatevi sempre che stiamo ragionando in base agli stati: diverso è lo stato in cui il gioco si trova, diverse saranno le necessità a cui dovremo far fronte. In parole povere: ci troviamo nella schermata d'introduzione? Non ci sarà bisogno di gestire lo spostamento del personaggio. E così via discorrendo.

Di conseguenza opereremo in questo modo: creando prima uno switch che comprenda i casi di tutti gli stati, e per ognuno di essi effettuare operazioni diverse. Partiamo dal primo caso:

[code]]czoxOTM6XCINCmNhc2UgR2FtZVN0YXRlLkludHJvOg0KICAgICAgICAgICAgICAgICAgICBpZiAoZS5LZXkgPT0gS2V5LlJldHVybil7WyYqJl19IHN0YXRvID0gR2FtZVN0YXRlLkdhbWU7DQogICAgICAgICAgICAgICAgICAgIGlmIChlLktleSA9PSBLZXkuRXNjYXBlKSBzdGF0b3tbJiomXX0gPSBHYW1lU3RhdGUuRXhpdDsNCiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQpcIjt7WyYqJl19[[/code]

Questo è sicuramente il caso più semplice. Fin quando il gioco si troverà nello stato Intro, verrà mostrato a schermo il background con le istruzioni del gioco, oltre ad un invito a premere invio per continuare. Dunque, sono due le cose che possiamo fare: o passiamo alla schermata di gioco oppure ce ne usciamo.

Qui arriva in nostro aiuto il parametro “e”. La proprietà e.Key, infatti, permette di sapere se in quel momento un determinato pulsante sulla tastiera è stato premuto. Il controllo, nella sua maniera più semplice, avviene con questa sintassi:

if (e.Key == Key.codice_chiave) // qui vanno le operazioni da eseguire.

Come potete vedere dall'intellisense, Key (e non e.Key, che è una proprietà) è un enumerazione comprendente tutti i caratteri della tastiera. Quindi ci basterà confrontare e.Key con un valore di Key per sapere se quel determinato pulsante è stato premuto.

Tornando al nostro codice, quindi, nella prima riga verifichiamo che il tasto invio venga premuto. Nel caso viene premuto (e l'uguaglianza ritorna true) allora lo stato viene modificato in GameState.Game. Se invece la condizione non è verificata tutto rimane come prima.

Nella seconda istruzione le cose sono simili: verifichiamo che il pulsante ESC venga premuto. Se si allora passeremo allo stato GameState.Exit, che provvederà a farci uscire dal programma. Altrimenti tutto rimarrà invariato e la schermata continuerà ad essere mostrata. Andiamo avanti col nostro codice.

[code]]czoyMDI2OlwiDQogY2FzZSBHYW1lU3RhdGUuR2FtZToNCiAgICAgICAgICAgICAgICAgICAgc3dpdGNoIChzdGF0b19wKQ0KICAgICB7WyYqJl19ICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuUHJpbWE6DQogICAgICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgIHN3aXRjaCAoZS5LZXkpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgY2FzZSBLZXkuUmV0dXJuOg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19IHN0YXRvX3AgPSBTdGF0b1BhcnRpdGEuSW5Db3JzbzsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlc2V0R3tbJiomXX1hbWUoKTsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgIGNhc2UgS2V5LkVzY2FwZToNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRvID0gR2F7WyYqJl19bWVTdGF0ZS5FeGl0Ow0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCg0KICAgICAgICAgICAgICAgICAgICAgICAge1smKiZdfWNhc2UgU3RhdG9QYXJ0aXRhLkluQ29yc286DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3dpdGNoIChlLktleSkNCiAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleS5MZWZ0QXJyb3tbJiomXX13Og0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBvc2l6aW9uZV9naW9jYXRvcmUgPiAwKSBwb3Npemlve1smKiZdfW5lX2dpb2NhdG9yZS0tOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LlJpZ2h0QXJyb3c6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gaWYgKHBvc2l6aW9uZV9naW9jYXRvcmUgPCBwb3NpemlvbmlfZ2lvY2F0b3JlLkNvdW50IC0gMSkgcG9zaXppb25lX2dpb2NhdG9ye1smKiZdfWUrKzsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICBjYXNlIEtleS5Fc2NhcGU6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZXNldEdhbWUoKTsNCntbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0b19wID0gU3RhdG9QYXJ0aXRhLlByaW1hOw0KICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgU3RhdG9QYXJ0aXRhLkZpbmU6DXtbJiomXX0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3dpdGNoIChlLktleSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7e1smKiZdfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleS5SZXR1cm46DQogICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgc3RhdG9fcCA9IFN0YXRvUGFydGl0YS5QcmltYTsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICBicmVhazsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleS5Fc2NhcGU6DQogICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgc3RhdG8gPSBHYW1lU3RhdGUuRXhpdDsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYntbJiomXX1yZWFrOw0KICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KXCI7e1smKiZdfQ==[[/code]

Dunque, in questa parte di codice stiamo gestendo il caso dello stato GameState.Game. Ci troviamo nella schermata di gioco vera e propria e, quindi, è da qui che dobbiamo partire per gestire i vari stati in cui si trova la partita (la variabile stato_p).

Per questo apriamo un nuovo switch, stavolta prendendo in esame il valore della variabile, appunto, stato_p.

Nel primo caso il discorso è semplice: ci troviamo nella schermata in cui il gioco non ancora inizia e possiamo fare solo due cose: o premere invio per iniziare la partita, oppure premere esc per uscire dal gioco. In codice, ciò si traduce in:

[code]]czo1NDI6XCINCmNhc2UgU3RhdG9QYXJ0aXRhLlByaW1hOg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN3aXRjaCAoZS5LZXl7WyYqJl19KQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSBLZXkuUntbJiomXX1ldHVybjoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRvX3AgPSBTdGF0b1BhcnRpdGEuSW5Db3JzbzsNe1smKiZdfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZXNldEdhbWUoKTsNCg0KICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LkVzY2FwZToNCiAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0byA9IEdhbWVTdGF0ZS5FeGl0Ow0KICAgICAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgYnJlYWs7DQpcIjt7WyYqJl19[[/code]

Come potete vedere, al posto di fare più if ho fatto un altro blocco switch con i casi corrispondenti ai pulsanti che volevo gestire. Se premiamo invio lo stato della partita passa da "Prima" a "InCorso" e le statistiche di gioco vengono resettate tramite ResetGame(). Nel caso in cui venga premuto Esc, invece, c'è poco da dire: la variabile "stato" passa a GameState.Exit e usciremo dal gioco dopo gli opportuni passaggi.

Per il caso della partita in corso, invece, le cose da gestire sono giusto un paio in più.

[code]]czo3ODg6XCINCmNhc2UgU3RhdG9QYXJ0aXRhLkluQ29yc286DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3dpdGNoIChlLkt7WyYqJl19ZXkpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleXtbJiomXX0uTGVmdEFycm93Og0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBvc2l6aW9uZV9naW9jYXRvcmUgPiAwe1smKiZdfSkgcG9zaXppb25lX2dpb2NhdG9yZS0tOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LlJpZ2h0QXJyb3c6DQogICAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgaWYgKHBvc2l6aW9uZV9naW9jYXRvcmUgPCBwb3NpemlvbmlfZ2lvY2F0b3JlLkNvdW50IC0gMSkgcG9zaXppb25le1smKiZdfV9naW9jYXRvcmUrKzsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgICAgICBjYXNlIEtleS5Fc2NhcGU6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZXNldHtbJiomXX1HYW1lKCk7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0b19wID0gU3RhdG9QYXJ0aXRhLlByaW1hOw0Ke1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQpcIjt7WyYqJl19[[/code]

Si fa il classico switch per vedere qualche tasto è stato premuto e ora i tasti da gestire sono tre: la freccia sinistra (Key.LeftArrow nella nostra enumerazione Key), la freccia destra (corrispondente a RightArrow) e il tasto Esc per uscire dalla partita in corso. Non è stata prevista una pausa durante il gioco.

E per ultimo gestiamo il caso della partita terminata:

[code]]czo0ODk6XCINCmNhc2UgU3RhdG9QYXJ0aXRhLkZpbmU6DQoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKGUuS2V7WyYqJl19eSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LntbJiomXX1SZXR1cm46DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0b19wID0gU3RhdG9QYXJ0aXRhLlByaW1hOw0Ke1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICBjYXNlIEtleS5Fc2NhcGU6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0byA9IEdhbWVTdGF0ZS5FeHtbJiomXX1pdDsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAge1smKiZdfSB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQpcIjt7WyYqJl19[[/code]

Non credo serva spiegare davvero quello che ho fatto, è lo stesso identico procedimento che ho adoperato per i due casi precedenti.

Bene, anche per l'input ci siamo e, a meno che non ci siamo scordati qualcosa in precedenza, il nostro gioco è praticamente completo a livello di codice. Adesso ci prepareremo per la compilazione, concludendo così l'ultimo articolo di questa serie di tutorials.

  • Share/Bookmark

Disegnamo gli oggetti. – Creazione di un gioco con SDL.NET

In fase di disegno degli oggetti, come potete immaginare, le cose si complicano un po’. Dobbiamo contare varie cose:

  • Dobbiamo generare un oggetto al secondo;
  • Spostare la moglie una volta al secondo;
  • Capire se abbiamo preso l’oggetto in caduta oppure no, quindi segnare i punti o togliere le vite.

Iniziamo quindi con il codice, stavolta completo, di UpdatePartita().

[code]]czozMDczOlwiDQpwcml2YXRlIHZvaWQgVXBkYXRlUGFydGl0YSgpDQogICAgICAgIHsNCiAgICAgICAgICAgIHN3aXRjaCAoc3RhdG97WyYqJl19X3ApDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuUHJpbWE6DQogICAgICAgICAgICAgIHtbJiomXX0gICAgICBWaWRlby5TY3JlZW4uQmxpdChiYWNrZyk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJle1smKiZdfW5kZXIoXCJQdW50ZWdnaW86IDBcIiwgU3lzdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoNntbJiomXX0wLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlByZW1pIEludmlvIHBlciBpbntbJiomXX1pemlhcmUgbGEgcGFydGl0YS4uLlwiLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qb2ludHtbJiomXX0oNjAsIDE3KSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlZpdGU6IDBcIiwgU3lzdGV7WyYqJl19bS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDQwMCwgMikpOw0KICAgICAgICAgICAgICAgIHtbJiomXX0gICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5JbkNvcnNvOg0KICAgICAgICAgICAgICAgICAge1smKiZdfSAgVmlkZW8uU2NyZWVuLkJsaXQoYmFja2cpOw0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXJ7WyYqJl19KFwiUHVudGVnZ2lvOiBcIiArIHB1bnRlZ2dpby5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0e1smKiZdfWVtLkRyYXdpbmcuUG9pbnQoNjAsIDIpKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZGVyKFwie1smKiZdfVZpdGU6IFwiICsgdml0ZS5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qe1smKiZdfW9pbnQoNDAwLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKHBvc2l6aW9uZV97WyYqJl19Z2lvY2F0b3JlKQ0KICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDA6DQogICAgICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGdpb2MxLCBwb3NpemlvbmlfZ2lvY2F0b3JlW3Bvc2l6aW9uZV9ne1smKiZdfWlvY2F0b3JlXSk7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICAgICAgICAgIGN7WyYqJl19YXNlIDE6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZ2lvYzIsIHBvc2l6aW9uaV9naW9jYXtbJiomXX10b3JlW3Bvc2l6aW9uZV9naW9jYXRvcmVdKTsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCg0KICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgIGNhc2UgMjoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChnaW9jMyx7WyYqJl19IHBvc2l6aW9uaV9naW9jYXRvcmVbcG9zaXppb25lX2dpb2NhdG9yZV0pOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZXtbJiomXX1hazsNCiAgICAgICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KHdpZmUsIHBve1smKiZdfXNpemlvbmlfbW9nbGllW3Bvc2l6aW9uZV9tb2dsaWVdKTsNCg0KICAgICAgICAgICAgICAgICAgICBVcGRhdGVPZ2dldHRpKCk7DQp7WyYqJl19DQogICAgICAgICAgICAgICAgICAgIGZvcmVhY2ggKE9nZ2V0dG9DYWRlbnRlIG8xIGluIG9nZ2V0dGkpDQogICAgICAgICAgICAgIHtbJiomXX0gICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKG8xLmZpbmVzdHJhKQ0KICAgICAgICAgICAgICAgICAgICAge1smKiZdfSAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDA6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZ7WyYqJl19aWRlby5TY3JlZW4uQmxpdChvMS5DYW52YXMsIHBvc2l6aW9uaV9maW5lc3RyYTFbbzEucG9zaXppb25lXSk7DQogICAgICAgICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDE6DQogICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChvMS5DYW52YXMsIHBvc2l6aW9uaV9maW5lc3RyYTJbbzEucG97WyYqJl19c2l6aW9uZV0pOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgY2FzZSAyOg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChvMS5DYW52YXMsIHBve1smKiZdfXNpemlvbmlfZmluZXN0cmEzW28xLnBvc2l6aW9uZV0pOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgICAgICAgICBicmVha3tbJiomXX07DQoNCiAgICAgICAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5GaW5lOg0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3Jle1smKiZdfWVuLkJsaXQoYmFja2cpOw0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXIoXCJQdW50ZWdnaW86e1smKiZdfSBcIiArIHB1bnRlZ2dpby5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qe1smKiZdfW9pbnQoNjAsIDIpKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZGVyKFwiUGFydGl0YSBUZXJte1smKiZdfWluYXRhLCBwcmVtZXJlIEludmlvLi4uXCIsIFN5c3RlbS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBve1smKiZdfWludCg2MCwgMTcpKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZGVyKFwiVml0ZTogMFwiLCBTeXtbJiomXX1zdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoNDAwLCAyKSk7DQogICAgICAgICAgICAge1smKiZdfSAgICAgICBicmVhazsNCiAgICAgICAgICAgIH0NCiAgICAgICAgfQ0KXCI7e1smKiZdfQ==[[/code]

Stavolta l'aggiunta è più succosa: c'è il metodo UpdateOggetti() che analizzeremo a breve, ed il ciclo foreach(OggettoCadente o1 in oggetti). Partiamo da questo ciclo, chiariamolo bene e successivamente spiegheremo nel dettaglio il metodo sopraccitato.

[code]]czo3MDg6XCINCmZvcmVhY2ggKE9nZ2V0dG9DYWRlbnRlIG8xIGluIG9nZ2V0dGkpDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKG8xLmZpbmVzdHJhKQ0KICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICBjYXNlIDA6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5Ce1smKiZdfWxpdChvMS5DYW52YXMsIHBvc2l6aW9uaV9maW5lc3RyYTFbbzEucG9zaXppb25lXSk7DQogICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDE6DQogICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChvMS5DYW52YXMsIHBvc2l6aW9uaV9maW5lc3RyYTJbbzEucG9zaXppb25lXSk7DQoge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDI6DQp7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChvMS5DYW52YXMsIHBvc2l6aW9uaV9maW5lc3tbJiomXX10cmEzW28xLnBvc2l6aW9uZV0pOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICB9DQogICAgICAgICAgICAgICB9DQpcIjt7WyYqJl19[[/code]

Questo blocco di codice scorre tutti gli elementi della lista di oggetti che stiamo usando: in poche parole, contiene tutte le informazioni degli oggetti che stanno cadendo dalle finestre. Per ogni oggetto, quindi, eseguiamo due operazioni specifiche:

  • Controlliamo da quale finestra sta cadendo, tramite il blocco switch(o1.finestra);
  • Disegnamo su schermo l'oggetto tramite la funzione Blit, usando come surface quella dell'oggetto attualmente preso in considerazione e come coordinate quelle della finestra dalla quale sta cadendo.

Avendo un percorso suddiviso in tre “parti”, ogni oggetto verrà disegnato nella parte indicata dalla variabile “o1.posizione”. Spero di essere stato chiaro, non potevo semplificare di più :) Adesso ci rimane solo da analizzare la funzione UpdateOggetti(). Prestate la massima attenzione soprattutto in questa parte.

Innanzitutto, ecco il codice completo del metodo:

[code]]czoxNTIwOlwiDQp2b2lkIFVwZGF0ZU9nZ2V0dGkoKQ0KICAgICAgICB7DQogICAgICAgICAgICBmcmFtZXMrKzsNCg0KICAgICAgICB7WyYqJl19ICAgIGlmIChmcmFtZXMgPT0gZnJhbWVsaW1pdCkNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBzMS5QbGF5KCk7DQoNCntbJiomXX0gICAgICAgICAgICAgICAgaW50IGZpbmVzdHJhID0gcjEuTmV4dCgwLCAzKTsNCiAgICAgICAgICAgICAgICBpbnQgaW5kaWNlID0ge1smKiZdfS0xOw0KDQogICAgICAgICAgICAgICAgcG9zaXppb25lX21vZ2xpZSA9IGZpbmVzdHJhOw0KDQogICAgICAgICAgICAgICAgZm9yZWF7WyYqJl19Y2ggKE9nZ2V0dG9DYWRlbnRlIG8xIGluIG9nZ2V0dGkpDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICBzd3tbJiomXX1pdGNoIChvMS5wb3NpemlvbmUpDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgMDoNe1smKiZdfQogICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDE6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbzEucG9zaXppb25lKyt7WyYqJl19Ow0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAyOg0KICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICBpZiAobzEuZmluZXN0cmEgPT0gcG9zaXppb25lX2dpb2NhdG9yZSkNCiAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdW50ZWdnaW8gKz0gMTA7DQogICAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdml0ZSAtPSAxOw0KICAgICAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgczIuUGxheSgpOw0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh2aXRlID09IDApIHN7WyYqJl19dGF0b19wID0gU3RhdG9QYXJ0aXRhLkZpbmU7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgICBpbmRpY2UgPSBvZ2dldHRpLkluZGV4T2YobzEpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFre1smKiZdfTsNCiAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgICAgIGlmKGluZGljZSAhPSB7WyYqJl19LTEpIG9nZ2V0dGkuUmVtb3ZlQXQoaW5kaWNlKTsNCiAgICAgICAgICAgICAgICBvZ2dldHRpLkFkZChuZXcgT2dnZXR0b0NhZGVudHtbJiomXX1lKHBvc2l6aW9uZV9tb2dsaWUsIHIxLk5leHQoMCwgNikpKTsNCg0KICAgICAgICAgICAgICAgIGZyYW1lcyA9IDA7DQoNCiAgICAge1smKiZdfSAgICAgICAgICAgaWYgKChwdW50ZWdnaW8gJSAzMDAgPT0gMCkmJihwdW50ZWdnaW8gIT0gMCkpDQogICAgICAgICAgICAgICAgew17WyYqJl19CiAgICAgICAgICAgICAgICAgICAgZnJhbWVsaW1pdCAtPSA1Ow0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgIHtbJiomXX0gICAgIH0NClwiO3tbJiomXX0=[[/code]

Non vi preoccupate se non capite, può essere comprensibile: spiegherò tutto riga per riga.

Innanzitutto vi rispiego brevemente il ragionamento che ho fatto e che ho applicato: ogni secondo devono succedere diverse cose. Dobbiamo far proseguire gli oggetti nella loro caduta, dobbiamo spostare la moglie in una finestra a caso delle tre e dobbiamo creare un nuovo oggetto.

Quindi la prima riflessione da fare sta qua: dato che la cosa dovrà avvenire ogni secondo e che il nostro programma va a 60 frames al secondo, se usiamo la variabile "frames" come contatore ad ogni esecuzione del metodo UpdateOggetti(), quando "frames" avrà un valore pari a 60 sapremo che sarà passato esattamente un secondo. Questo è il senso dell'istruzione

frames++;

deve essere una sorta di "timer" molto grezzo e adattato a questo singolo scopo. La variabile "framelimit" invece serve a specificare la lunghezza dell'intervallo. Inizialmente avremo bisogno di determinate azioni ogni secondo? Allora il "framelimit" sarà pari a 60. Per velocizzare questi eventi ed aumentare la difficoltà del prodotto sarà sufficiente diminuire di un tot la variabile "framelimit".

Niente di più. Tenete bene a mente questo concetto. Proseguiamo con il codice:

if (frames == framelimit)


serve a specificare che tutto il contenuto di questo blocco if sarà svolto solo al raggiungimento di 60 frames. Ovvero, una volta al secondo. Ma cosa abbiamo deciso di svolgere? Ecco riga per riga tutto spiegato.

s1.Play();

è il suono del bip classico di questi giochini. Non c'è molto da spiegare, il suo uso è puramente estetico e viene riprodotto ogni volta che le cose nel gioco cambiano...

int finestra = r1.Next(0, 3);

int indice = -1;

posizione_moglie = finestra;


alla variabile finestra associamo invece il valore generato dal Random r1, tra 0 e 3. L'indice è una variabile che scopriremo tra poco, mentre la terza istruzione, semplicemente assegna a posizione_moglie il valore generato dal random.

Rileggere il proprio codice fa sempre bene: in questo caso infatti potevo tranquillamente assegnare il valore del random a posizione_moglie, eliminando la necessità di "finestra".

Adesso che abbiamo calcolato la nuova posizione della moglie, preoccupiamoci di gestire tutti gli oggetti:

[code]]czo4ODg6XCINCmZvcmVhY2ggKE9nZ2V0dG9DYWRlbnRlIG8xIGluIG9nZ2V0dGkpDQogICAgICAgICAgICAgICAgew0KICAgICAgICB7WyYqJl19ICAgICAgICAgICAgc3dpdGNoIChvMS5wb3NpemlvbmUpDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICBjYXNlIDA6DQogICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDE6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAge1smKiZdfW8xLnBvc2l6aW9uZSsrOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19IGNhc2UgMjoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobzEuZmluZXN0cmEgPT0gcG9zaXppb25lX2dpb2NhdG9yZXtbJiomXX0pDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdW50ZWdnaW8ge1smKiZdfSs9IDEwOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlDQogICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdml0ZSAtPSAxOw0KICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICAgICAgczIuUGxheSgpOw0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlme1smKiZdfSAodml0ZSA9PSAwKSBzdGF0b19wID0gU3RhdG9QYXJ0aXRhLkZpbmU7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgICBpbmRpY2UgPSBvZ2dldHRpLkluZGV4T2YobzEpOw0KICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIH0NClwiO3tbJiomXX0=[[/code]

Come punto di riferimento per capire cosa succede dobbiamo utilizzare la posizione attuale di ogni oggetto. Per questo, con il ciclo foreach, prendiamo in considerazione ogni oggetto attualmente presente in lista.

Nel caso la posizione dell'oggetto attualmente preso in considerazione sia 0 oppure 1, c'è ben poco da fare: lo si sposta in giù di uno. A questo serve l'istruzione o1.posizione++.

Nel caso invece la posizione sia 2, quindi ad un passo dal giocatore, abbiamo questo blocco if.

[code]]czoxOTc6XCINCmlmIChvMS5maW5lc3RyYSA9PSBwb3NpemlvbmVfZ2lvY2F0b3JlKQ0Kew0KICAgIHB1bnRlZ2dpbyArPSAxMDsNCn17WyYqJl19DQplbHNlDQp7DQogICAgdml0ZSAtPSAxOw0KICAgIHMyLlBsYXkoKTsNCg0KICAgIGlmICh2aXRlID09IDApIHN0YXRvX3AgPSBTdHtbJiomXX1hdG9QYXJ0aXRhLkZpbmU7DQp9DQppbmRpY2UgPSBvZ2dldHRpLkluZGV4T2YobzEpOw0KXCI7e1smKiZdfQ==[[/code]

Nel caso il giocatore si trovi sotto l'oggetto (la finestra appartenente all'oggetto può essere 0,1 o 2. idem per la posizione del giocatore) allora viene aumentato il punteggio di dieci punti. L'oggetto è stato preso con successo.

Nel caso contrario invece le cose cambiano. Togliamo una vita con vite -= 1; e riproduciamo s2, il suono che si utilizza nel caso si perda una vita. Infine facciamo un ultimo controllo: se le vite sono pari a zero allora il gioco finisce. Basta quindi dare un nuovo valore a stato_p: StatoPartita.Fine.

Infine ecco che introduciamo qualche informazione in più sulla variabile intera "indice". Normalmente non possiamo cancellare un oggetto dentro un ciclo che scorre l'oggetto stesso, non trovate? Parliamo della nostra lista di oggetti.

Ogni secondo infatti, un nuovo oggetto deve essere creato. Non possiamo accumulare oggetti all'infinito però! Per cui dobbiamo cancellare anche l'oggetto che è già caduto. A questo serve l'indice, che assume il valore di ritorno del metodo oggetti.IndexOf(o1), che indica la posizione dell'oggetto o1 nella lista.

Le cose tuttavia non finiscono qua:

[code]]czoyMDU6XCINCmlmKGluZGljZSAhPSAtMSkgb2dnZXR0aS5SZW1vdmVBdChpbmRpY2UpOw0Kb2dnZXR0aS5BZGQobmV3IE9nZ2V0dG97WyYqJl19Q2FkZW50ZShwb3NpemlvbmVfbW9nbGllLCByMS5OZXh0KDAsIDYpKSk7DQoNCmZyYW1lcyA9IDA7DQoNCmlmICgocHVudGVnZ2lvIHtbJiomXX0lIDMwMCA9PSAwKSYmKHB1bnRlZ2dpbyAhPSAwKSkNCnsNCiAgICBmcmFtZWxpbWl0IC09IDU7DQp9DQpcIjt7WyYqJl19[[/code]

Come prima cosa controlliamo che l'indice sia diverso da -1.

Domanda: perchè questo controllo?
Risposta: la variabile indice ha il compito che abbiamo spiegato sopra. Tuttavia, nei primi due secondi di gioco, gli oggetti sono in caduta e ce ne sono precisamente 2. Nessuno di questi, inoltre, è a contatto col giocatore. In questo modo, finchè non c'è un indice "valido" (ovvero diverso da zero) non verrà cancellato nessun'oggetto.

In questo modo appena avremo tre oggetti in campo (e la posizione del primo oggetto sarà 2, guardacaso proprio ad un passo dal giocatore) allora avremo un nuovo indice ed eseguiremo le operazioni nel blocco.

"oggetti.RemoveAt(indice)" è l'operazione che fa per noi. Dalla lista oggetti elimineremo quindi l'oggetto all'indice specificato nell'argomento.

Dopo aver rimosso l'oggetto, ne creiamo uno nuovo:

oggetti.Add(new OggettoCadente(posizione_moglie, r1.Next(0, 6)));

con questa sintassi abbiamo creato un nuovo oggetto cadente, passando come parametri del costruttore, nell'ordine, la posizione attuale della moglie e un nuovo valore generato a caso tra 1 e 6. Scrivo 1 anzichè zero perchè quando specifichiamo il margine inferiore è considerato escluso. L'istruzione new OggettoCadente(...) è a sua volta argomento di oggetti.Add(). In questo modo l'oggetto viene aggiunto alla lista.

Tramite "frames = 0;" il contatore del nostro grezzissimo timer viene azzerato e le operazioni ripartono da capo.

Infine, ecco le istruzioni che permettono di velocizzare il gioco man mano che si va avanti:

[code]]czo3NzpcIg0KaWYgKChwdW50ZWdnaW8gJSAzMDAgPT0gMCkmJihwdW50ZWdnaW8gIT0gMCkpDQp7DQogICAgZnJhbWVsaW1pdCAtPSB7WyYqJl19NTsNCn0NClwiO3tbJiomXX0=[[/code]

con queste poche righe di codice viene innanzitutto controllato che il punteggio attuale sia un multiplo di 300 (avevamo deciso che ogni 300 punti la velocità venisse aumentata: basterà controllare che il punteggio attuale sia un multiplo dello stesso numero specificato) e che ovviamente sia diverso da zero, in modo tale da non velocizzare tutto all'inizio. Se le condizioni sopraccitate vengono rispettate, allora il limite dettato da "framelimit" viene diminuito di cinque frames.

Per spiegarlo in parole povere, fin quando non faremo 300 punti avremo un framelimit di 60. Da 300 punti a 600 avremo un framelimit di 55, e così via, di trecento in trecento diminuendo di 5.

Ce l'abbiamo fatta! Dopo questa istruzione il nostro metodo è completato e non abbiamo bisogno di altre istruzioni aggiuntive... ora che la fase di disegno è completata al 100%, manca ancora da gestire l'input. Infine, metteremo tutto insieme.

Avanti!

  • Share/Bookmark

Disegnamo il Marito. – Creazione di un gioco con SDL.NET

Dato che abbiamo voluto utilizzare tre immagini differenti per il marito, il discorso sarà leggermente diverso ma comunque niente di complesso. Il nostro codice mostra nuove aggiunte:

[code]]czoyMjQzOlwiDQpwcml2YXRlIHZvaWQgVXBkYXRlUGFydGl0YSgpDQogICAgICAgIHsNCiAgICAgICAgICAgIHN3aXRjaCAoc3RhdG97WyYqJl19X3ApDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuUHJpbWE6DQogICAgICAgICAgICAgIHtbJiomXX0gICAgICBWaWRlby5TY3JlZW4uQmxpdChiYWNrZyk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJle1smKiZdfW5kZXIoXCJQdW50ZWdnaW86IDBcIiwgU3lzdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoNntbJiomXX0wLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlByZW1pIEludmlvIHBlciBpbntbJiomXX1pemlhcmUgbGEgcGFydGl0YS4uLlwiLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qb2ludHtbJiomXX0oNjAsIDE3KSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlZpdGU6IDBcIiwgU3lzdGV7WyYqJl19bS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDQwMCwgMikpOw0KICAgICAgICAgICAgICAgIHtbJiomXX0gICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5JbkNvcnNvOg0KICAgICAgICAgICAgICAgICAge1smKiZdfSAgVmlkZW8uU2NyZWVuLkJsaXQoYmFja2cpOw0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXJ7WyYqJl19KFwiUHVudGVnZ2lvOiBcIiArIHB1bnRlZ2dpby5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0e1smKiZdfWVtLkRyYXdpbmcuUG9pbnQoNjAsIDIpKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZGVyKFwie1smKiZdfVZpdGU6IFwiICsgdml0ZS5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qe1smKiZdfW9pbnQoNDAwLCAyKSk7DQoNCgkgICAgIHN3aXRjaCAocG9zaXppb25lX2dpb2NhdG9yZSkNCiAgICAgICAgICAgICAgICAgICAgew17WyYqJl19CiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgMDoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQntbJiomXX1saXQoZ2lvYzEsIHBvc2l6aW9uaV9naW9jYXRvcmVbcG9zaXppb25lX2dpb2NhdG9yZV0pOw0KICAgICAgICAgICAgICAgICAgICAge1smKiZdfSAgICAgICBicmVhazsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAxOg0KICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICBWaWRlby5TY3JlZW4uQmxpdChnaW9jMiwgcG9zaXppb25pX2dpb2NhdG9yZVtwb3NpemlvbmVfZ2lvY2F0b3JlXSk7DQogICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDI6DQogICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGdpb2MzLCBwb3NpemlvbmlfZ2lvY2F0b3JlW3Bvc2l6aW9uZV9naW97WyYqJl19Y2F0b3JlXSk7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAgICAgIH0NCg0KCSAgIHtbJiomXX0gICBWaWRlby5TY3JlZW4uQmxpdCh3aWZlLCBwb3NpemlvbmlfbW9nbGllW3Bvc2l6aW9uZV9tb2dsaWVdKTsNCiAgICAgICAgICAge1smKiZdfSAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuRmluZToNCiAgICAgICAgICAgICAgICB7WyYqJl19ICAgIFZpZGVvLlNjcmVlbi5CbGl0KGJhY2tnKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZHtbJiomXX1lcihcIlB1bnRlZ2dpbzogXCIgKyBwdW50ZWdnaW8uVG9TdHJpbmcoKSwgU3lzdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3l7WyYqJl19c3RlbS5EcmF3aW5nLlBvaW50KDYwLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcntbJiomXX0oXCJQYXJ0aXRhIFRlcm1pbmF0YSwgcHJlbWVyZSBJbnZpby4uLlwiLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN7WyYqJl19dGVtLkRyYXdpbmcuUG9pbnQoNjAsIDE3KSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcntbJiomXX0oXCJWaXRlOiAwXCIsIFN5c3RlbS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDQwMCwgMikpOw17WyYqJl19CiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICB9DQp9DQpcIjt7WyYqJl19[[/code]

Abbiamo inserito un altro switch, che stavolta serve a controllare sotto quale finestra si trova il giocatore: il metodo utilizzato ed il ragionamento alla base, però, sono gli stessi che abbiamo adottato per la moglie. Così, dopo aver controllato la posizione del giocatore tramite lo switch, ecco che utilizziamo l'istruzione

Video.Screen.Blit(gioc1, posizioni_giocatore[posizione_giocatore]);

oppure

Video.Screen.Blit(gioc2, posizioni_giocatore[posizione_giocatore]);

oppure

Video.Screen.Blit(gioc3, posizioni_giocatore[posizione_giocatore]);


In poche parole, cambia solo il nome della variabile. La cosa si complica un po' di più, invece, per ciò che concerne gli oggetti. Andiamo avanti per scoprire come.

  • Share/Bookmark

Disegnamo la moglie. – Creazione di un gioco con SDL.NET

Torniamo ad esaminare il codice di prima, facendo una piccola aggiunta in grassetto:

[code]]czoyMjQzOlwiDQpwcml2YXRlIHZvaWQgVXBkYXRlUGFydGl0YSgpDQogICAgICAgIHsNCiAgICAgICAgICAgIHN3aXRjaCAoc3RhdG97WyYqJl19X3ApDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuUHJpbWE6DQogICAgICAgICAgICAgIHtbJiomXX0gICAgICBWaWRlby5TY3JlZW4uQmxpdChiYWNrZyk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJle1smKiZdfW5kZXIoXCJQdW50ZWdnaW86IDBcIiwgU3lzdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoNntbJiomXX0wLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlByZW1pIEludmlvIHBlciBpbntbJiomXX1pemlhcmUgbGEgcGFydGl0YS4uLlwiLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qb2ludHtbJiomXX0oNjAsIDE3KSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlZpdGU6IDBcIiwgU3lzdGV7WyYqJl19bS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDQwMCwgMikpOw0KICAgICAgICAgICAgICAgIHtbJiomXX0gICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5JbkNvcnNvOg0KICAgICAgICAgICAgICAgICAge1smKiZdfSAgVmlkZW8uU2NyZWVuLkJsaXQoYmFja2cpOw0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXJ7WyYqJl19KFwiUHVudGVnZ2lvOiBcIiArIHB1bnRlZ2dpby5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0e1smKiZdfWVtLkRyYXdpbmcuUG9pbnQoNjAsIDIpKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZGVyKFwie1smKiZdfVZpdGU6IFwiICsgdml0ZS5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qe1smKiZdfW9pbnQoNDAwLCAyKSk7DQoNCgkgICAgIHN3aXRjaCAocG9zaXppb25lX2dpb2NhdG9yZSkNCiAgICAgICAgICAgICAgICAgICAgew17WyYqJl19CiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgMDoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQntbJiomXX1saXQoZ2lvYzEsIHBvc2l6aW9uaV9naW9jYXRvcmVbcG9zaXppb25lX2dpb2NhdG9yZV0pOw0KICAgICAgICAgICAgICAgICAgICAge1smKiZdfSAgICAgICBicmVhazsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAxOg0KICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICBWaWRlby5TY3JlZW4uQmxpdChnaW9jMiwgcG9zaXppb25pX2dpb2NhdG9yZVtwb3NpemlvbmVfZ2lvY2F0b3JlXSk7DQogICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDI6DQogICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGdpb2MzLCBwb3NpemlvbmlfZ2lvY2F0b3JlW3Bvc2l6aW9uZV9naW97WyYqJl19Y2F0b3JlXSk7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAgICAgIH0NCg0KCSAgIHtbJiomXX0gICBWaWRlby5TY3JlZW4uQmxpdCh3aWZlLCBwb3NpemlvbmlfbW9nbGllW3Bvc2l6aW9uZV9tb2dsaWVdKTsNCiAgICAgICAgICAge1smKiZdfSAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuRmluZToNCiAgICAgICAgICAgICAgICB7WyYqJl19ICAgIFZpZGVvLlNjcmVlbi5CbGl0KGJhY2tnKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZHtbJiomXX1lcihcIlB1bnRlZ2dpbzogXCIgKyBwdW50ZWdnaW8uVG9TdHJpbmcoKSwgU3lzdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3l7WyYqJl19c3RlbS5EcmF3aW5nLlBvaW50KDYwLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcntbJiomXX0oXCJQYXJ0aXRhIFRlcm1pbmF0YSwgcHJlbWVyZSBJbnZpby4uLlwiLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN7WyYqJl19dGVtLkRyYXdpbmcuUG9pbnQoNjAsIDE3KSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcntbJiomXX0oXCJWaXRlOiAwXCIsIFN5c3RlbS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDQwMCwgMikpOw17WyYqJl19CiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICB9DQp9DQpcIjt7WyYqJl19[[/code]

L'istruzione evidenziata serve a disegnare la moglie nella posizione scelta dal sistema. Spieghiamola meglio:
La moglie può essere disegnata in tre posizioni differenti:

  1. alla finestra 1,
  2. alla finestra 2,
  3. alla finestra 3.

Queste tre posizioni sono identificabili come dei punti. Nel nostro caso, i punti sono:

  • x = 151 – y = 71;
  • x = 351 – y = 71;
  • x = 551 – y = 71;

Abbiamo quindi creato una lista di punti (List, precisamente ;) ): la lista “posizioni_moglie”. Ovviamente la moglie non può essere in tre luoghi contemporaneamente: la sua posizione attuale è regolata dalla variabile “posizione_moglie”, un intero che viene passato come indice della lista. Riguardate e capirete per bene:

Video.Screen.Blit(wife, posizioni_moglie[posizione_moglie]);

Con questa sola riga di codice disegneremo senza problemi la moglie.

  • Share/Bookmark

Disegnamo il Background. – Creazione di un gioco con SDL.NET

Un esempio di disegno già l’abbiamo visto nell’articolo precedente, con l’istruzione “Video.Screen.Blit(intro);”, disegnando appunto la schermata di intro del gioco. Le cose ovviamente non sono così semplici per quanto riguarda il disegno della schermata di gioco vero e proprio. Iniziamo quindi ad analizzare la funzione UpdatePartita(). Il codice completo sarà scaricabile a parte, qui invece inserirò solo le parti man mano necessarie.

Partiamo con:

[code]]czoxNTg2OlwiDQpwcml2YXRlIHZvaWQgVXBkYXRlUGFydGl0YSgpDQogICAgICAgIHsNCiAgICAgICAgICAgIHN3aXRjaCAoc3RhdG97WyYqJl19X3ApDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuUHJpbWE6DQogICAgICAgICAgICAgIHtbJiomXX0gICAgICBWaWRlby5TY3JlZW4uQmxpdChiYWNrZyk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJle1smKiZdfW5kZXIoXCJQdW50ZWdnaW86IDBcIiwgU3lzdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoNntbJiomXX0wLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlByZW1pIEludmlvIHBlciBpbntbJiomXX1pemlhcmUgbGEgcGFydGl0YS4uLlwiLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qb2ludHtbJiomXX0oNjAsIDE3KSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlZpdGU6IDBcIiwgU3lzdGV7WyYqJl19bS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDQwMCwgMikpOw0KICAgICAgICAgICAgICAgIHtbJiomXX0gICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5JbkNvcnNvOg0KICAgICAgICAgICAgICAgICAge1smKiZdfSAgVmlkZW8uU2NyZWVuLkJsaXQoYmFja2cpOw0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXJ7WyYqJl19KFwiUHVudGVnZ2lvOiBcIiArIHB1bnRlZ2dpby5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0e1smKiZdfWVtLkRyYXdpbmcuUG9pbnQoNjAsIDIpKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZGVyKFwie1smKiZdfVZpdGU6IFwiICsgdml0ZS5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qe1smKiZdfW9pbnQoNDAwLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnR7WyYqJl19aXRhLkZpbmU6DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGJhY2tnKTsNCiAgICAgICAgICAgICAgICAgIHtbJiomXX0gIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlB1bnRlZ2dpbzogXCIgKyBwdW50ZWdnaW8uVG9TdHJpbmcoKSwgU3lzdGVtLkR7WyYqJl19cmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDYwLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIHtbJiomXX1WaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXIoXCJQYXJ0aXRhIFRlcm1pbmF0YSwgcHJlbWVyZSBJbnZpby4uLlwiLCBTeXN0ZW0uRHJ7WyYqJl19YXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoNjAsIDE3KSk7DQogICAgICAgICAgICAgICAgICAgIHtbJiomXX1WaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXIoXCJWaXRlOiAwXCIsIFN5c3RlbS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3R7WyYqJl19ZW0uRHJhd2luZy5Qb2ludCg0MDAsIDIpKTsNCiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICB9DQp9DQpcIjt7WyYqJl19[[/code]

Come potete vedere qui gestiamo ogni possibile caso per la variabile stato_p. Come ben sappiamo può assumere tre valori: Prima, InCorso (precedentemente detto Durante) e Fine. All'inizio, infatti, il gioco è fermo: dovremo premere invio per iniziare la nostra partita.

[code]]czo1MTA6XCINCmNhc2UgU3RhdG9QYXJ0aXRhLlByaW1hOg0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChiYWN7WyYqJl19a2cpOw0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXIoXCJQdW50ZWdnaW86IDBcIiwgU3lzdGVte1smKiZdfS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDYwLCAyKSk7DQogICAgICAgICAgICAgICAgICB7WyYqJl19ICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXIoXCJQcmVtaSBJbnZpbyBwZXIgaW5pemlhcmUgbGEgcGFydGl0YS4uLlwiLCBTeXN0e1smKiZdfWVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoNjAsIDE3KSk7DQogICAgICAgICAgICAgICB7WyYqJl19ICAgICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXIoXCJWaXRlOiAwXCIsIFN5c3RlbS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3e1smKiZdfSBTeXN0ZW0uRHJhd2luZy5Qb2ludCg0MDAsIDIpKTsNCiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQpcIjt7WyYqJl19[[/code]

Ecco cosa facciamo in ordine:

Disegnamo il nostro background, contenuto nella variabile backg;

  • Scriviamo a schermo il punteggio, uguale a zero. L'istruzione che usiamo è Video.Screen.Blit che prende come parametro la funzione Render dell'oggetto font f1 precendetemente istanziato.

Domanda: perchè queste funzioni annidate?
Risposta: Innanzitutto, tramite l'intellisense scorri le varie proprietà e metodi dell'oggetto f1. Scoprirai che Render() restituisce una Surface ;) Per questo motivo è tranquillamente disegnabile tramite Blit().

Con lo stesso metodo del punto precedente disegnamo una scritta per invitare il giocatore a premere invio.
In egual modo scriviamo a schermo il numero delle vite, attualmente pari a 0 in quanto il gioco non ancora inizia.

Dovrebbe essere di facile comprensione :) Ma andiamo avanti e leggiamo il codice scritto successivamente:

[code]]czozOTg6XCINCmNhc2UgU3RhdG9QYXJ0aXRhLkluQ29yc286DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGJ7WyYqJl19YWNrZyk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlB1bnRlZ2dpbzogXCIgKyBwdW50e1smKiZdfWVnZ2lvLlRvU3RyaW5nKCksIFN5c3RlbS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDYwLCB7WyYqJl19MikpOw0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXIoXCJWaXRlOiBcIiArIHZpdGUuVG9TdHJpe1smKiZdfW5nKCksIFN5c3RlbS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDQwMCwgMikpOw0KICAgICB7WyYqJl19ICAgICAgICAgICAgICAgYnJlYWs7DQoNClwiO3tbJiomXX0=[[/code]

Dato che per adesso disegnamo solo il nostro background, la struttura del codice è simile al caso precedente. L'unica differenza è che non ci sarà, ovviamente, la scritta “Premi Invio per iniziare la partita...” e del punteggio e delle vite scriveremo il valore attuale, contenuto nelle variabili intere “punteggio” e “vite”.

Dopo aver spiegato questi due casi il codice del caso StatoPartita.Fine è di facilissima comprensione, per cui non mi dilungherò ulteriormente.

  • Share/Bookmark

Nel Loop. – Creazione di un gioco con SDL.NET

Ci eravamo fermati all’avvio del loop, ed è proprio da qui che riprenderemo. Adesso che il programma è stato impostato correttamente, grazie al metodo AddHandler() sappiamo che ad ogni frame generato dal sistema verrà eseguito un metodo, Events_Tick. Eccolo qui di seguito, riproposto nel suo codice. Prima di continuare, ricordate che il Visual Studio, tramite intellisense, si occupa di generare la struttura base e dare un nome al metodo associato all’evento con la sola pressione del tasto Tab.

[code]]czo1NjA6XCINCnByaXZhdGUgdm9pZCBFdmVudHNfVGljayhvYmplY3Qgc2VuZGVyLCBUaWNrRXZlbnRBcmdzIGUpDQp7DQogICAgICB7WyYqJl19ICAgICAgVmlkZW8uU2NyZWVuLkZpbGwoU3lzdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spOw0KDQogICAgICAgICAgICBzd2l0Y2ggKHtbJiomXX1zdGF0bykNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBjYXNlIEdhbWVTdGF0ZS5JbnRybzoNCiAgICAgICAgICAgICAge1smKiZdfSAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGludHJvKTsNCiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICB7WyYqJl19ICAgY2FzZSBHYW1lU3RhdGUuR2FtZToNCiAgICAgICAgICAgICAgICAgICAgVXBkYXRlUGFydGl0YSgpOw0KICAgICAgICAgICAgIHtbJiomXX0gICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICBjYXNlIEdhbWVTdGF0ZS5FeGl0Og0KICAgICAgICAgICAgICAgICAgICBFe1smKiZdfXZlbnRzLlF1aXRBcHBsaWNhdGlvbigpOw0KICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgIH0NCg0KICAgICB7WyYqJl19ICAgICAgIFZpZGVvLlNjcmVlbi5VcGRhdGUoKTsNCiB9DQpcIjt7WyYqJl19[[/code]

Negli articoli precedenti vi ho parlato dell'importanza delle enumerazioni, degli “stati” che il gioco può assumere e così via. Ecco che mettiamo in pratica queste nozioni. Prima però analizziamo la struttura che ho voluto utilizzare per il Loop principale.

La prima istruzione, Video.Screen.Fill() si occupa di riempire lo schermo di un colore specificato. In questo modo cancelliamo le elaborazioni precedenti e ad ogni frame possiamo disegnare tutto ciò che vogliamo senza “sporcare”. L'ultima istruzione, invece, esegue l'Update dello schermo, mandando quindi sul nostro monitor tutto quello che abbiamo elaborato precedentemente.

Ed ecco che parliamo dell'aspetto più importante: la fase di “elaborazione”. Ora tenete a mente la struttura degli stati che avevo proposto negli articoli precedenti e guardate questo switch:

[code]]czozNTk6XCINCnN3aXRjaCAoc3RhdG8pDQp7DQogICAgICAgICAgICAgICAgY2FzZSBHYW1lU3RhdGUuSW50cm86DQogICAgICAgICB7WyYqJl19ICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChpbnRybyk7DQogICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgIHtbJiomXX0gICAgICAgIGNhc2UgR2FtZVN0YXRlLkdhbWU6DQogICAgICAgICAgICAgICAgICAgIFVwZGF0ZVBhcnRpdGEoKTsNCiAgICAgICAge1smKiZdfSAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgY2FzZSBHYW1lU3RhdGUuRXhpdDoNCiAgICAgICAgICAgICAgICB7WyYqJl19ICAgIEV2ZW50cy5RdWl0QXBwbGljYXRpb24oKTsNCiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQp9DQpcIjt7WyYqJl19[[/code]

Fa esattamente ciò che ci serve. La variabile “stato”, infatti, contiene informazioni sullo stato attuale del gioco, identificabili con valori dell'enumerazione GameState. In questo modo, per ogni stato possibile ci saranno azioni differenti da eseguire.

Per esempio, analizziamo il primo caso: lo stato del gioco attuale è GameState.Intro? Bene, allora dovremo semplicemente disegnare a schermo la nostra schermata iniziale! Il tutto è tranquillamente fattibile tramite l'istruzione Video.Screen.Blit(intro).

Le cose sono invece differenti per lo stato GameState.Game. Come potete ricordare, per la partita abbiamo una variabile a parte che si occupa di memorizzare le informazioni sullo stato di questa, ovvero “stato_p”. Tutto il codice della gestione della partita, per semplicità di lettura dello stesso, è stato posto nel metodo UpdatePartita(), che vedremo tra un po'. Per adesso, vi basti pensare che nello stato GameState.Game, logicamente, svolgiamo tutte le operazioni necessarie a gestire la partita in corso.

Nel caso di GameState.Exit, invece, le cose sono semplici: eseguiamo il metodo QuitApplication che ci fa uscire dal programma. Nulla di più, nulla di meno.

  • Share/Bookmark

Che struttura avrà il codice? – Creazione di un gioco con SDL.NET

Andando a scrivere il codice, dobbiamo ragionare sul modo in cui lo scriviamo. Come ho già spiegato precedentemente, il metodo che viene eseguito ad ogni frame è quello associato all’evento Tick. Inoltre, analizzando il file Template.Cs (file che ho usato come scheletro per il progetto) abbiamo visto due metodi private:

  • AddHandlers()
  • RemoveHandlers()

Questi due metodi, rispettivamente, aggiungono e rimuovono gli handlers al programma. La loro utilità è indubbia, specie in casi magari un pò più complicati di questo. Ecco il codice dei due metodi:

[code]]czo1Nzk6XCINCnByaXZhdGUgdm9pZCBBZGRIYW5kbGVycygpDQp7DQogICAgICAgICAgICBFdmVudHMuUXVpdCArPSBuZXcgRXZlbnR7WyYqJl19SGFuZGxlcjxRdWl0RXZlbnRBcmdzPih0aGlzLkV2ZW50c19RdWl0KTsNCiAgICAgICAgICAgIEV2ZW50cy5LZXlib2FyZERvd24gK3tbJiomXX09IG5ldyBFdmVudEhhbmRsZXI8S2V5Ym9hcmRFdmVudEFyZ3M+KEV2ZW50c19LZXlib2FyZERvd24pOw0KICAgICAgICAgICAgRXZle1smKiZdfW50cy5UaWNrICs9IG5ldyBFdmVudEhhbmRsZXI8VGlja0V2ZW50QXJncz4odGhpcy5FdmVudHNfVGljayk7DQp9DQoNCnByaXZhdGV7WyYqJl19IHZvaWQgUmVtb3ZlSGFuZGxlcnMoKQ0Kew0KICAgICAgICAgICAgRXZlbnRzLlF1aXQgLT0gbmV3IEV2ZW50SGFuZGxlcjxRdWl0RXtbJiomXX12ZW50QXJncz4odGhpcy5FdmVudHNfUXVpdCk7DQogICAgICAgICAgICBFdmVudHMuS2V5Ym9hcmREb3duIC09IG5ldyBFdmVudEhhe1smKiZdfW5kbGVyPEtleWJvYXJkRXZlbnRBcmdzPihFdmVudHNfS2V5Ym9hcmREb3duKTsNCiAgICAgICAgICAgIEV2ZW50cy5UaWNrIC09IG57WyYqJl19ZXcgRXZlbnRIYW5kbGVyPFRpY2tFdmVudEFyZ3M+KHRoaXMuRXZlbnRzX1RpY2spOw0KfQ0KXCI7e1smKiZdfQ==[[/code]

Pensate per esempio ad un insieme di menù o di schermate di gioco. Passando un parametro ad un ipotetico metodo di gestione degli handlers sarà possibile rimuoverli ed aggiungerli in base alle necessità, cosa decisamente ordinata.

Ma torniamo a noi, non complichiamoci la vita. Rimaniamo sul semplice. Come prima cosa analizziamo il costruttore della classe Template, quella principale.

[code]]czoxMTQ5OlwiDQpwdWJsaWMgVGVtcGxhdGUoKQ0KICAgICAgICB7DQogICAgICAgICAgICBwb3NpemlvbmlfZ2lvY2F0b3JlLkFkZCh7WyYqJl19bmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDE1MCwgNDUwKSk7DQogICAgICAgICAgICBwb3NpemlvbmlfZ2lvY2F0b3JlLkFkZChuZXtbJiomXX13IFN5c3RlbS5EcmF3aW5nLlBvaW50KDM1MCwgNDUwKSk7DQogICAgICAgICAgICBwb3NpemlvbmlfZ2lvY2F0b3JlLkFkZChuZXcge1smKiZdfVN5c3RlbS5EcmF3aW5nLlBvaW50KDU1MCwgNDUwKSk7DQoNCiAgICAgICAgICAgIHBvc2l6aW9uaV9tb2dsaWUuQWRkKG5ldyBTeXN7WyYqJl19dGVtLkRyYXdpbmcuUG9pbnQoMTUxLCA3MSkpOw0KICAgICAgICAgICAgcG9zaXppb25pX21vZ2xpZS5BZGQobmV3IFN5c3RlbS5EcntbJiomXX1hd2luZy5Qb2ludCgzNTEsIDcxKSk7DQogICAgICAgICAgICBwb3NpemlvbmlfbW9nbGllLkFkZChuZXcgU3lzdGVtLkRyYXdpbmcue1smKiZdfVBvaW50KDU1MSwgNzEpKTsNCg0KICAgICAgICAgICAgcG9zaXppb25pX2ZpbmVzdHJhMS5BZGQobmV3IFN5c3RlbS5EcmF3aW5nLlB7WyYqJl19b2ludCgxNTEsIDE2MSkpOw0KICAgICAgICAgICAgcG9zaXppb25pX2ZpbmVzdHJhMS5BZGQobmV3IFN5c3RlbS5EcmF3aW5nLlBvaXtbJiomXX1udCgxNTEsIDI1NSkpOw0KICAgICAgICAgICAgcG9zaXppb25pX2ZpbmVzdHJhMS5BZGQobmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50e1smKiZdfSgxNTEsIDM1MSkpOw0KDQogICAgICAgICAgICBwb3NpemlvbmlfZmluZXN0cmEyLkFkZChuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnR7WyYqJl19KDM1MSwgMTYxKSk7DQogICAgICAgICAgICBwb3NpemlvbmlfZmluZXN0cmEyLkFkZChuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoM3tbJiomXX01MSwgMjU1KSk7DQogICAgICAgICAgICBwb3NpemlvbmlfZmluZXN0cmEyLkFkZChuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoMzUxe1smKiZdfSwgMzUxKSk7DQoNCiAgICAgICAgICAgIHBvc2l6aW9uaV9maW5lc3RyYTMuQWRkKG5ldyBTeXN0ZW0uRHJhd2luZy5Qb2ludCg1NTF7WyYqJl19LCAxNjEpKTsNCiAgICAgICAgICAgIHBvc2l6aW9uaV9maW5lc3RyYTMuQWRkKG5ldyBTeXN0ZW0uRHJhd2luZy5Qb2ludCg1NTEsIHtbJiomXX0yNTUpKTsNCiAgICAgICAgICAgIHBvc2l6aW9uaV9maW5lc3RyYTMuQWRkKG5ldyBTeXN0ZW0uRHJhd2luZy5Qb2ludCg1NTEsIDM1e1smKiZdfTEpKTsNCiAgICAgICAgfQ0KXCI7e1smKiZdfQ==[[/code]

Che diamine vuol dire tutto questo compendio di numeri e punti? E' presto detto: vi ricordate quando vi ho spiegato che marito, moglie ed oggetti sarebbero stati sempre disegnati in punti ben precisi, proprio come i vecchi giochini?

Pensate agli insiemi. Per quanto riguarda "posizione_giocatore" stiamo aggiungendo alla lista tutte le posizioni possibili che egli può assumere durante il gioco. Ad intuito è facile capire che lo spostamento avviene solo sull'asse X e non su Y, che rimane fissa a 450. Idem per quanto riguarda la moglie, che si sposta da 151 a 551 ma rimanendo sempre ad Y = 71.

Il discorso inverso invece viene fatto per gli oggetti: come potete vedere la X rimane uguale per ogni finestra (la finestra 1 avrà un x pari a 151, la finestra 2 invece pari a 351 e.c.c). Se ci fate caso, ovviamente la x delle finestre è uguale a alle x della moglie: questo perchè disegneremo i nostri oggetti sotto la moglie, in quanto lanciati da lei.

Dopo aver assegnato i punti proseguiamo con lo studio del nostro codice: adesso soffermiamoci un minuto sul metodo Main:

[code]]czo5NTpcIg0KW1NUQVRocmVhZF0NCnB1YmxpYyBzdGF0aWMgdm9pZCBNYWluKCkNCnsNCglUZW1wbGF0ZSBnYW1lID0gbmV3IFRlbXB7WyYqJl19bGF0ZSgpOw0KCWdhbWUuR28oKTsNCn0NClwiO3tbJiomXX0=[[/code]

Questo metodo fa due cose, e anche molto semplici: crea un'istanza della classe Template (quella che stiamo scrivendo) ed avvia il metodo Go() di questa. Il “[STAThread]” prima del metodo specifica il modello di Threading da utilizzare. Il metodo Main in se, comunque, è molto importante perchè è il punto d'ingresso della nostra applicazione: tutto parte da qua.

Adesso passiamo a Go():

[code]]czozNzI6XCINCnB1YmxpYyB2b2lkIEdvKCkNCnsNCiAgICAgICAgICAgIFZpZGVvLldpbmRvd0NhcHRpb24gPSBcIkEgR2FtZSBBYm91e1smKiZdfXQgV2lmZVwncyBGdXJ5LlwiOw0KICAgICAgICAgICAgVmlkZW8uU2V0VmlkZW9Nb2RlKDgwMCwgNjAwKTsNCiAgICAgICAgICAgIEV2ZXtbJiomXX1udHMuRnBzID0gNjA7DQoNCiAgICAgICAgICAgIHNvdHRvZm9uZG8uVm9sdW1lID0gMTA7DQogICAgICAgICAgICBzMS5Wb2x1bWUge1smKiZdfT0gMTA7DQogICAgICAgICAgICBzMi5Wb2x1bWUgPSAxMDsNCg0KICAgICAgICAgICAgc290dG9mb25kby5QbGF5KHRydWUpOw0KICB7WyYqJl19ICAgICAgICAgIA0KICAgICAgICAgICAgdGhpcy5BZGRIYW5kbGVycygpOw0KICAgICAgICAgICAgRXZlbnRzLlJ1bigpOw0KfQ0KXCJ7WyYqJl19O3tbJiomXX0=[[/code]

Bene, qui troviamo un po di cosette da analizzare: innanzitutto parliamo delle prime due istruzioni, dedicate all'oggetto Video: nella prima impostiamo il valore della proprietà WindowCaption, ovvero il titolo. Fin qui dovrebbe essere tranquillo. Altrettanto tranquillo per la seconda istruzione: con SetVideoMode impostiamo la risoluzione ad 800 x 600, passando come parametro i due interi che ci interessano.

Ecco un'istruzione importantissima:

Events.Fps = 60;

Mi raccomando, è indispensabile che gli Fps siano impostati a 60, dato che in precedenza abbiamo fatto tutto il discorso sulle variabili frames e framelimit. Assegnando il valore 60 a questa proprietà faremo in modo che il sistema generi precisamente 60 frames al secondo, proprio quello di cui abbiamo bisogno.

Dopo ciò troviamo tre istruzioni dedicate al sonoro: in questo caso ho impostato a 10 il valore sia della musica di sottofondo che delle variabili s1 ed s2, quelle che mantengono le informazioni dei suoni del bip e del bip più basso, usato in caso di perdita di una vita.

Domanda forse lecita: se avevi problemi con il volume della musica e non hai utilizzato una variabile di tipo Music, perchè mai ora hai impostato tutto a 10 e bon?

Risposta: semplicemente perchè a mio parere mi sembrava tutto troppo alto e quindi fastidioso ;) una volta provata la seconda strada, ovvero l'usare Sound anziché Music, il livello del volume era accettabile :)

Andando avanti, incontriamo l'istruzione “sottofondo.Play(true);”. Qui abbiamo semplicemente detto al programma di avviare la riproduzione della musica, in quanto verrà utilizzata in tutto il gioco fino alla sua chiusura. Il “true” passato come parametro indica che il loop del suono sarà infinito. E' la nostra unica musica, quindi la useremo sempre e senza complicarci la vita.

Dopo aver impostato tutti questi “piccoli” aspetti, possiamo procedere all'esecuzione di due metodi molto importanti. Con AddHandlers() gestiremo tutti gli eventi e con Run() daremo il via al main loop del sistema. In questo preciso istante il gioco è partito.

Tra gli altri metodi degni di nota troviamo ResetGame():

[code]]czoxNjk6XCINCnByaXZhdGUgdm9pZCBSZXNldEdhbWUoKQ0KICAgICAgICB7DQogICAgICAgICAgICBwb3NpemlvbmVfZ2lvY2F0b3J7WyYqJl19ZSA9IDA7DQogICAgICAgICAgICB2aXRlID0gMzsNCiAgICAgICAgICAgIHB1bnRlZ2dpbyA9IDA7DQogICAgICAgICAgICBmcmFtZXtbJiomXX1saW1pdCA9IDYwOw0KICAgICAgICB9DQpcIjt7WyYqJl19[[/code]

questo codice, di facile comprensione, verrà eseguito ogni volta che l'utente vorrà iniziare una nuova partita, reimpostando i punti a 0 e le vite a 3.

  • Share/Bookmark

L’Importanza delle Enumerazioni. – Creazione di un gioco con SDL.NET

Nell’articolo precedente, illustrando le variabili utilizzate nel gioco, avevo parlato di due di queste, rispettivamente “stato” e “stato_p”. stato è una variabile di tipo GameState. stato_p, invece, è una variabile di tipo StatoPartita. Questi due “tipi” sono delle enumerazioni.

Cos’è un enumerazione, e perchè è così importante? In realtà se state dando un occhiata alla programmazione di giochi dovreste saperlo, però due paroline le spendo volentieri. Stando alla cara MSDN, ecco una dritta:

C# consente di creare il proprio set di costanti denominate utilizzando la parola chiave enum. Questi tipi di dati consentono di dichiarare un set di nomi o di altri valori letterali che definiscono tutti i valori possibili assegnabili a una variabile.

Questo è un ottimo modo per rendere l’idea degli Stati in cui il gioco si trova. Nel mio caso, per esempio, ecco il codice delle enumerazioni, contenute nel file Utils.Cs.

[code]]czoyNzc6XCINCnVzaW5nIFN5c3RlbTsNCnVzaW5nIFN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljOw0KdXNpbmcgU3lzdGVtLlRleHR7WyYqJl19Ow0KDQpuYW1lc3BhY2UgQUdBV0YNCnsNCiAgICBwdWJsaWMgZW51bSBHYW1lU3RhdGUNCiAgICB7DQogICAgICAgIEludHJvLA0KIHtbJiomXX0gICAgICAgR2FtZSwNCiAgICAgICAgRXhpdA0KICAgIH0NCg0KICAgIHB1YmxpYyBlbnVtIFN0YXRvUGFydGl0YQ0KICAgIHsNCiAge1smKiZdfSAgICAgIFByaW1hLA0KICAgICAgICBJbkNvcnNvLA0KICAgICAgICBGaW5lDQogICAgfQ0KfQ0KXCI7e1smKiZdfQ==[[/code]

Come potete vedere, ho creato due enumerazioni. Una per lo stato del gioco (il gioco comprende sia la schermata iniziale che la schermata di gioco) ed una per lo stato della partita (che serve a determinare se la partita sta per iniziare, è in corso o è finita).

In questo modo, passando da un valore all'altro di un enumerazione, potremo scegliere cosa disegnare a schermo e cosa no. Per fare un esempio pratico, in GameState.Game non disegneremo mai la schermata di introduzione. Con lo stesso ragionamento, per esempio, non ci sogneremmo mai di disegnare a schermo il testo “Premi Invio per Iniziare” una volta che la partita è in corso.

Un aiuto non indifferente, sempre meglio che utilizzare degli int per indicare gli stati come facevo nei programmi del terzo superiore :D

  • Share/Bookmark

Definiamo le nostre variabili. – Creazione di un gioco con SDL.NET

Per la prima volta in questa guida iniziamo a vedere un pò di codice. Era pure ora. Per prima cosa apriamo il nostro ambiente di sviluppo e, se usiamo il caro Visual C# Express, possiamo anche utilizzare il comodo template di progetto che ho caricato sul sito qualche giorno fa.

Con la nostra finestra aperta e il sistema pronto, possiamo iniziare ad aggiungere queste righe di codice appena dopo l’inizio della classe principale, ovvero:

[code]]czoyMjU6XCINCnVzaW5nIFN5c3RlbTsNCnVzaW5nIFN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljOw0KDQp1c2luZyBTZGxEb3ROZXR7WyYqJl19LkNvcmU7DQp1c2luZyBTZGxEb3ROZXQuR3JhcGhpY3M7DQp1c2luZyBTZGxEb3ROZXQuSW5wdXQ7DQp1c2luZyBTZGxEb3ROZXQuQXtbJiomXX11ZGlvOw0KDQpuYW1lc3BhY2UgQUdBV0YNCnsNCiAgICBwdWJsaWMgY2xhc3MgVGVtcGxhdGUNCiAgICB7DQoJU0NSSVZJQU1PIFFVe1smKiZdfUkhDQpcIjt7WyYqJl19[[/code]

Ecco le nostre prime istruzioni:

GameState stato = GameState.Intro;

StatoPartita stato_p = StatoPartita.Prima;

Surface intro = new Surface("immagini/intro.jpg");

Surface backg = new Surface("immagini/back.jpg");

Surface gioc1 = new Surface("immagini/gioc1.png");

Surface gioc2 = new Surface("immagini/gioc2.png");

Surface gioc3 = new Surface("immagini/gioc3.png");

Surface wife = new Surface("immagini/wife.png");

Sound sottofondo = new Sound("musica/music1.ogg");

Sound s1 = new Sound("suoni/s1.wav");

Sound s2 = new Sound("suoni/s2.wav");

int frames = 0;

int framelimit = 60;

Font f1 = new Font("fonts/font1.ttf", 12);

int vite, punteggio;

List<System.Drawing.Point> posizioni_giocatore = new List<System.Drawing.Point>();

int posizione_giocatore = 0;

List<System.Drawing.Point> posizioni_moglie = new List<System.Drawing.Point>();

int posizione_moglie = 0;

List<System.Drawing.Point> posizioni_finestra1 = new List<System.Drawing.Point>();

List<System.Drawing.Point> posizioni_finestra2 = new List<System.Drawing.Point>();

List<System.Drawing.Point> posizioni_finestra3 = new List<System.Drawing.Point>();

List<OggettoCadente> oggetti = new List<OggettoCadente>();

Random r1 = new Random();

Non vi preoccupate se non capite qualcosa! Piano piano analizzeremo riga per riga e vedremo a cosa serve. Iniziamo dalle prime due: abbiamo le variabili stato e stato_p, che come specificato precedentemente serviranno a visualizzare e ad impostare lo stato attuale del gioco e della partita. Le due variabili sono di tipo GameState e StatoPartita: dopo questo articolo daremo un occhiata per bene all'utilità di queste enumerazioni.

Dopodichè abbiamo le seguenti istruzioni:

Surface intro = new Surface("immagini/intro.jpg");

Surface backg = new Surface("immagini/back.jpg");

Surface gioc1 = new Surface("immagini/gioc1.png");

Surface gioc2 = new Surface("immagini/gioc2.png");

Surface gioc3 = new Surface("immagini/gioc3.png");

Surface wife = new Surface("immagini/wife.png");


In questo blocco ho messo tutte le immagini che mi potevano servire per il gioco. La prima, intro, si collega ad intro.jpg nella cartella immagini. Lo stesso ragionamento vale per le altre ed in questo modo abbiamo in memoria tutti i files grafici che ci servono. Due parole vanno spese per le variabili gioc1, gioc2, gioc3. In realtà avrei potuto usare altri metodi per gestire il giocatore. Magari usare una sola variabile e ad ogni spostamento caricare il file corrispondente. Stavolta, però, ho voluto “sprecare” spazio ed utilizzare tre variabili distinte.

Si noti inoltre che a volte (non in questo caso ovviamente) caricare un memoria un file grafico può essere pesante. Non tutti i files grafici dei giochi, infatti, sono leggeri come questi!

Ora dobbiamo caricare in memoria i suoni: ecco le istruzioni che ci servono per rendere ciò possibile:

Sound sottofondo = new Sound("musica/music1.ogg");

Sound s1 = new Sound("suoni/s1.wav");

Sound s2 = new Sound("suoni/s2.wav");

In sottofondo abbiamo messo la musichetta di sottofondo e in s1 ed s2 i due effetti sonori, il bip e il bip del personaggio che perde una vita. Può sorgere spontanea una domanda: perchè, se esiste la classe Music, ho messo un file musicale come Sound? Semplicemente perchè possiamo deciderne il volume.

Nelle prime prove, infatti, avevo usato la classe Music, ma ben presto mi ero accorto che il volume della musica sovrastava il suono del bip di troppo e diventava quasi fastidioso. Usando invece la classe Sound potevo gestire il volume a mio piacimento. A domanda risposta :)

int frames = 0;

int framelimit = 60;


Queste due variabili di tipo Int sono il cuore del “timer” che avevo anticipato negli appunti. La variabile “frames” provvederà a contare il numero di frames passati e “framelimit”, come suggerisce il nome, rappresenta il valore numerico che la variabile “frames” deve raggiungere. A quel punto scateneremo gli eventi rispettivi.

Ma di questo parleremo più avanti: per adesso vediamo il codice delle definizioni delle variabili.

Per questo c'è bisogno di poche presentazioni:

Font f1 = new Font("fonts/font1.ttf", 12);


Abbiamo già visto nella guida base che questa è una semplice istruzione per caricare in memoria un Font chiamato f1, situato nella cartella fonts. La grandezza del carattere è specificata nel secondo parametro del costruttore (nel nostro caso 12).

Le variabili:

int vite, punteggio;

sono, rispettivamente, il numero delle vite del personaggio (che avranno un valore possibile da 3 a 0) e il punteggio del giocatore.

List<System.Drawing.Point> posizioni_giocatore = new List<System.Drawing.Point>();

int posizione_giocatore = 0;

Come detto in fase di “appunti” i personaggi che disegneremo verranno disegnati su punti dalle coordinate ben precise. A questo serve la nostra List “posizioni_giocatore”. In questo modo potremo memorizzare le coordinate che ci servono e, usando una variabile indice (guarda caso l'intero “posizione_giocatore”) disegneremo di volta in volta il giocatore in base alla sua posizione.

Stesso discorso, ovviamente, vale per la moglie: anche in questo caso infatti abbiamo dichiarato le variabili

List<System.Drawing.Point> posizioni_moglie = new List<System.Drawing.Point>();

int posizione_moglie = 0;

La prima consentirà di specificare tutte le posizioni della moglie (ambigua questa) mentre la seconda ci permetterà di volta in volta di disegnare in modo preciso la Surface. Il come tutto ciò avviene ve lo spiegherò dopo: guardate innanzitutto per bene il tipo di variabili che utilizziamo.

Abbiamo ancora alcuni punti da definire, e per questo motivo definiamo altre tre liste che ci accompagneranno nello sviluppo del gioco:

List<System.Drawing.Point> posizioni_finestra1 = new List<System.Drawing.Point>();

List<System.Drawing.Point> posizioni_finestra2 = new List<System.Drawing.Point>();

List<System.Drawing.Point> posizioni_finestra3 = new List<System.Drawing.Point>();

queste variabili servono a definire tutte le possibili posizioni che ogni oggetto cadente potrà assumere nel gioco. “posizioni_finestra1”, per esempio, servirà a specificare tutti i possibili punti che verranno utilizzati per disegnare un oggetto in caduta sotto la finestra numero uno (quella più a sinistra, per intenderci). Lo stesso discorso ovviamente vale per le altre finestre.

Infine, dobbiamo pure mettere da qualche parte i nostri oggetti. Ecco quindi che in soccorso arriva un'altra lista:

List<OggettoCadente> oggetti = new List<OggettoCadente>();

Come potete vedere in questo caso non abbiamo un tipo già definito di oggetto, in questa lista. Per l'oggetto che cade, infatti, avevamo deciso di utilizzare una classe a parte, chiamata OggettoCadente. Avevamo dato un occhiata ad una struttura generica di come poteva essere, ora vi mostro il codice per intero.

[code]]czo0NTk6XCINCnVzaW5nIFN5c3RlbTsNCnVzaW5nIFN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljOw0KDQp1c2luZyBTZGxEb3ROZXR7WyYqJl19LkNvcmU7DQp1c2luZyBTZGxEb3ROZXQuR3JhcGhpY3M7DQoNCm5hbWVzcGFjZSBBR0FXRg0Kew0KICAgIGNsYXNzIE9nZ2V0dG9DYXtbJiomXX1kZW50ZQ0KICAgIHsNCiAgICAgICAgcHVibGljIFN1cmZhY2UgQ2FudmFzOw0KDQogICAgICAgIHB1YmxpYyBpbnQgcG9zaXppb25le1smKiZdfTsNCiAgICAgICAgcHVibGljIGludCBmaW5lc3RyYTsNCg0KICAgICAgICBwdWJsaWMgT2dnZXR0b0NhZGVudGUoaW50IGYsaW50IHR7WyYqJl19KQ0KICAgICAgICB7DQogICAgICAgICAgICBDYW52YXMgPSBuZXcgU3VyZmFjZShcImltbWFnaW5pL1wiICsgdC5Ub1N0cmluZygpICsge1smKiZdfVwiLnBuZ1wiKTsNCiAgICAgICAgICAgIHBvc2l6aW9uZSA9IDA7DQogICAgICAgICAgICBmaW5lc3RyYSA9IGY7DQogICAgICAgIH0NCntbJiomXX0gICAgfQ0KfQ0KXCI7e1smKiZdfQ==[[/code]

Com'è facile notare non c'è molto di complicato, in realtà. Ogni istanza di questa classe avrà una propria Surface (che sarà quello che disegneremo), una finestra (in questo modo sapremo sotto quale finestra dovremo disegnare l'oggetto) e la posizione (in modo tale da gestire la caduta).

Il metodo costruttore, infine, che prende come parametri sia il tipo che il numero della finestra, esegue poche e semplici operazioni: carica in memoria il file corrispondente partendo dal tipo, assegna il valore 0 alla variabile posizione (l'oggetto sempre dall'alto dovrà partire) ed infine il numero della finestra.

Nota: il codice di OggettoCadente si trova in un file a parte, OggettoCadente.cs.

Infine, ecco l'ultima variabile che ci si presenta:

Random r1 = new Random();

Come ben sappiamo, la finestra da cui ogni oggetto dovrà cadere sarà decisa a caso di volta in volta. Per questo motivo ci è molto utile (per non dire indispensabile) una variabile di tipo Random.

Detto questo abbiamo terminato il nostro discorso sulle variabili. Come abbiamo visto, alcune volte potevamo fare delle ottimizzazioni: non c'è bisogno che vi dica di effettuare sempre scelte oculate in termini di variabili.

Certo, in un caso come questo una variabile in più o in meno non fa mai tutta questa differenza. Ma pensate ad un grande progetto, in cui centinaia e centiniaia di variabili vengono cambiate ogni millisecondo. Lì bisogna stare attentissimi a ciò che si fa.

Con ciò vi saluto e... al prossimo articolo!

  • Share/Bookmark