venerdì 29 luglio 2011

L’importanza di chiamarsi Editor

Quando ci si cimenta nella programmazione di un videogioco le cose da fare e a cui pensare sono veramente tante. Ci sono le meccaniche, il gameplay, la GUI, le possibili configurazioni hardware, la grafica, l’audio; e poi i contenuti, come dialoghi, storia (eventuale), personaggi, mondi, mappe.

In mezzo a questo meraviglioso marasma l’idea di infilarci pure dell’altro sembra francamente poco felice, specie se questo altro prevede lo sviluppo di nuovo codice che non apporta nulla al gioco. Stiamo parlando di lui, del pezzo di software più discusso, sopravvalutato (da alcuni), sottovalutato (da molti) e, solitamente, trascurato dell’intero processo di sviluppo di un titolo ludico: l’editor dei livelli.

E’ veramente così importante? Dipende.

Dipende innanzitutto dal tipo di gioco a cui state pensando. Per un puzzle game potrebbe servire, ma anche no; per un platform ne avrete quasi certamente bisogno, al contrario che per un roguelike, che fa della generazione procedurale il suo punto di forza. Insomma, la natura del gioco influenza certamente questa scelta. Possiamo dire che vi servirà se il vostro mondo di gioco cambia significativamente con il progredire del gioco stesso.

Quindi la regola è che se sto facendo un platform o un RPG o un qualsiasi altro gioco con varietà di scenari mi serve un editor dei livelli? Dipende.

Dipende dal tuo livello di preparazione, dal tempo e dalla passione che intendi infondere nel progetto. La progettazione e lo sviluppo dell’editor ti porterà via tempo e risorse che potresti altrimenti dedicare al progetto vero e proprio. Non è propriamente un lavoro per principianti.

Pensaci. Con poco sforzo potresti mettere in piedi dei dati innestati direttamente nel codice. E’ il modo grezzo, sporco, duro, ma che ti porta ad avere in breve un (piccolo) mondo (spoglio) pronto ad accogliere il personaggio. Vuoi prototipare? Vuoi sperimentare un’idea, per capire quanta presa può avere su di te? Capire se ti diverte, se ti coinvolge, se ti può invogliare a prenderla più seriamente? Hardcoda tutto. Una bella matrice di int con dati fissi è brutta, ma può salvarti la vita.

Oppure potresti essere un attimo più raffinato e decidere di fare tu stesso da editor producendo dei file esterni con i dati utili a descrivere il tuo mondo. Questo metodo è un po’ più lungo perchè ti chiede di scrivere anche il parser per questi file (il level loader) ma ti permette sicuramente maggiore flessibilità rispetto alla soluzione precedente. E mondi più grandi e interessanti... magari ancora un po’ spogli, un po’ privi di eventi, un po’ ripetitivi, ma va bene lo stesso, no?

…?

Ok, non ti ho convinto. Sei un duro e tieni al tuo progetto. Vuoi qualcosa di più. Vuoi veramente che dia l’esperienza di gioco di un Super Mario. Vuoi veramente che le tue mappe siano ricche e interessanti, che il tuo mondo dia l’idea di un qualcosa di vivo, in cui potercisi perdere. Allora non attendere oltre: vai e scrivi il tuo editor!

Scrivere l’editor significa spiegare al computer la tua idea. E’ qui la vera difficoltà, ma anche il suo punto di forza. Già, perchè ti costringe a ragionare sul tuo gioco, a definirne gli elementi e a prevederne le meccaniche e, soprattutto, a stabilire le strutture dati necessarie.

Sviluppare l’editor è anche un ottimo modo per capire quanto veramente tieni al progetto, poichè ti occuperà per le prime settimane, proprio il periodo in cui si registrano il maggior numero di decessi neonatali dei progetti amatoriali. Ti fornisce anche un buon metro di giudizio sul tuo gioco: se non riesci a scriverne l’editor, difficilmente saresti riuscito a terminarlo. Forse era troppo complesso, o forse non avevi le idee chiare su cosa fare (figurati sul come).

In definitiva, se hai intenzioni serie e un progetto un po’ più complesso di Pong, scrivi il tuo editor. Scrivilo bene. Non importa che sia bello, ma cerca di farlo robusto e con pochi bachi e ti darà soddisfazioni.

Male che vada avrai abbandonato un editor. Scusate il cinismo, ma la reputo una perdita meno dolorosa che abbandonare un gioco.

mercoledì 27 luglio 2011

Tutorial: scriviamo un gioco in C++... Tris! (parte 3)

Dopo aver visto come impostare l'ambiente di sviluppo e linkare correttamente le librerie scelte, passiamo finalmente a fare sul serio!

In questa terza parte, infatti, imbastiremo il codice che ci porterà a visualizzare la title screen e ad uscire premendo ESC.

Le prime classi


Vogliamo sviluppare il progetto con un modello a oggetti, quindi è il momento di fare una ragionata sulla struttura di classi che potrebbero servirci.

Faccio una piccola premessa a riguardo: lo scopo di questo tutorial è scrivere un gioco. E’ vero che molti componenti si possono (si devono!) riutilizzare tra diversi giochi, ma qui non faremo distinzioni nette tra componenti di engine e componenti di gioco. Scriveremo semplicemente il nostro Tris.

Avremo una classe principale, Tris, che governerà il gioco. Tale classe non sarà però un monolite che racchiude in sè tutti gli aspetti del sistema, bensì un saggio e illuminato sovrano che delegherà ad altri compiti ben specifici, limitandosi a coordinare ed organizzare il lavoro altrui.

Il primo dei compiti da delegare riguarda la creazione, l’inizializzazione e la distruzione del contesto grafico di SDL. Di questo si occuperà una classe apposita, che chiameremo SDLManager. La classe Tris utilizzerà SDLManager per impostare il sistema e per rilasciare le risorse a fine gioco. SDLManager sarà poi una ottima candidata al riutilizzo, ma non pensiamoci più di tanto per ora: restiamo concentrati sul gioco!

L’obiettivo dichiarato di questa terza puntata è però il disegno della title screen e la gestione della tastiera, in particolare della pressione del tasto ESC. Quel che dobbiamo fare è impostare una prima, grezza, versione del game loop, ovvero di quel ciclo infinito che prevede la rilevazione dell’input (la pressione del tasto ESC), l’aggiornamento delle entità coinvolte nel gioco (per ora nulla) e il disegno del frame corrente (la title screen).

Classe SDLManager


Bene, partiamo con la classe SDLManager. La definisco in questo modo:

#ifndef SDLMANAGER_H
#define SDLMANAGER_H

#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>

class SDLManager
{
public:
SDLManager();
virtual ~SDLManager();

/** Inizializza il sistema SDL e SDL_ttf.
* \return 0 in caso di successo o un codice di errore.
*/
int Init();

/** Termina l'esecuzione di SDL e SDL_ttf liberando le risorse */
void Quit();

protected:
private:
};

#endif // SDLMANAGER_H

La sua implementazione è piuttosto diretta. Non facciamo altro che spostare nei due metodi Init e Quit le chiamate già messe nel main di test dipendenze, aggiungendo qualche controllo.

#include "../include/SDLManager.h"
#include <iostream>

using namespace std;

//-----------------------------------------------------------------------------
SDLManager::SDLManager()
{
//ctor
}

//-----------------------------------------------------------------------------
SDLManager::~SDLManager()
{
//dtor
}

//-----------------------------------------------------------------------------
int
SDLManager::Init()
{
if(SDL_Init(SDL_INIT_VIDEO) < 0)
{
cerr << "Errore inizializzando SDL: " << SDL_GetError() << endl;
return -1;
}

if(TTF_Init() < 0)
{
cerr << "Errore inizializzando SDL_ttf: " << TTF_GetError() << endl;
return -2;
}

if(SDL_SetVideoMode(320, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF) == 0)
{
cerr << "Impossibile impostare il video a 320x480, 32bpp, double buffer" << endl;
return -3;
}

return 0;
}

//-----------------------------------------------------------------------------
void
SDLManager::Quit()
{
if(TTF_WasInit())
{
TTF_Quit();
}

if(SDL_WasInit(SDL_INIT_VIDEO))
{
SDL_Quit();
}
}

Ho scelto come risoluzione un 320x480 per due ragioni: tris è un gioco occasionale, da ufficio, da partitella mordi e fuggi e, possibilmente, da potersi nascondere rapidamente ;). Dovendo optare per dimensioni piccole ho preferito utilizzare una risoluzione tipica da smartphone... vuoi mai che un domani non lo si voglia portare su una piattaforma mobile?

Classe Tris


Possiamo ora definire la classe principale del gioco: Tris. Dovrà usare opportunamente SDLManager e gestire il game loop. Io la imposto così:

#ifndef TRIS_H
#define TRIS_H

#include "SDLManager.h"

class Tris
{
public:
Tris();
virtual ~Tris();

// gestione sistema

int StartApplication();
void StopApplication();

// controllo stato

bool IsRunning();

// main loop

void HandleInput();
void Update();
void DrawAll();

protected:
private:

SDLManager* m_pSDLManager;
bool m_IsRunning;
};

#endif // TRIS_H

e la sua implementazione:

#include "../include/Tris.h"
#include <iostream>

using namespace std;

//-----------------------------------------------------------------------------
Tris::Tris()
{
//ctor

m_pSDLManager = 0;
m_IsRunning = false;
}

//-----------------------------------------------------------------------------
Tris::~Tris()
{
//dtor

delete m_pSDLManager;
m_pSDLManager = 0;

SDL_FreeSurface(tmp_TitleScreen);
}

//-----------------------------------------------------------------------------
int
Tris::StartApplication()
{
m_pSDLManager = new SDLManager();

int result = m_pSDLManager->Init();

if(result < 0)
{
return 1; // errore
}

m_IsRunning = true;

// carico l'immagine della titleScreen

SDL_Surface* pTitleImage = SDL_LoadBMP("title_screen.bmp");
if(!pTitleImage)
{
cerr << "Impossibile caricare title_screen.bmp" << endl;
return 2; // errore
}

tmp_TitleScreen = SDL_DisplayFormat(pTitleImage);

if(!tmp_TitleScreen)
{
// non riesco a creare la versione ottimizzata della surface... poco male, uso quella originale
tmp_TitleScreen = pTitleImage;
}
else
{
SDL_FreeSurface(pTitleImage);
}

return 0; // tutto ok
}

//-----------------------------------------------------------------------------
void
Tris::StopApplication()
{
m_pSDLManager->Quit();
}

//-----------------------------------------------------------------------------
bool
Tris::IsRunning()
{
return m_IsRunning;
}

//-----------------------------------------------------------------------------
void
Tris::HandleInput()
{
SDL_Event event;

while(SDL_PollEvent(&event))
{
switch(event.type)
{

case SDL_KEYDOWN:
if(event.key.keysym.sym == SDLK_ESCAPE)
{
m_IsRunning = false;
}
break;

}
}
}

//-----------------------------------------------------------------------------
void
Tris::Update()
{
// per ora nulla
}

//-----------------------------------------------------------------------------
void
Tris::DrawAll()
{
SDL_Surface* screen = SDL_GetVideoSurface();

// pulisco lo schermo
SDL_FillRect(screen, &screen->clip_rect, 0x000000);

// disegno la title screen
SDL_BlitSurface(tmp_TitleScreen, 0, screen, 0);

// svuoto il buffer (invio le istruzioni per il disegno reale a schermo)
SDL_Flip(screen);
}

Il Main


Rimane solo da modificare opportunamente il main:

#include "include/Tris.h"

int main(int argc, char** argv)
{
Tris game;

if(game.StartApplication() < 0)
{
game.StopApplication();
exit(1);
}

while(game.IsRunning())
{
game.HandleInput();
game.Update();
game.DrawAll();
}

game.StopApplication();

return 0;
}

Gestione degli errori


Se qualcosa va storto durante l’avvio del gioco il programma esce con codice 1. Sembra poco per una diagnostica efficace, ma abbiamo in realtà inserito delle chiamate a std::cerr nei punti in cui l’errore si è generato. SDL intercetta per noi lo stream di error standard e crea il file stderr.txt nella directory dell’eseguibile. Se il programma terminerà in modo inatteso non dovremo fare altro che leggere il file per un log di quanto è accaduto.

NB: Allo stesso modo, lo stream di output standard std::cout viene reindirizzato nel file stdout.txt.

Bene, compiliamo ed eseguiamo e questo è il risultato:


Nel prossimo post introdurremo lo stato di gioco e faremo un opportuno refactor per gestire in modo più strutturato la title screen.

Buon coding!

Articoli collegati:


  1. Tutorial: scriviamo un gioco in C++... Tris!
  2. Tutorial: scriviamo un gioco in C++... Tris! (parte 2)

lunedì 25 luglio 2011

Tutorial: scriviamo un gioco in C++... Tris! (parte 2)

Nella prima parte abbiamo stabilito il gioco da sviluppare, Tris, e le caratteristiche fondamentali che dovrà avere. Abbiamo inoltre stilato una lista di requisiti, una sorta di scaletta delle cose da implementare.

In questa seconda parte metteremo a punto i tool da utilizzare e inizieremo ad imbastire il codice che ci porterà a testare tutte le dipendenze.

Io svilupperò questo progetto sotto Windows avendo riguardo di utilizzare tecnologie portabili senza intoppi particolari anche sotto Linux e, immagino, Mac.

Come editor la mia scelta (personale) ricade su CodeBlocks. Le librerie saranno SDL e la sua estensione SDL_ttf per la gestione dei font e del testo. Non gestiremo l’audio, veramente inutile (se non dannoso) in un gioco da ufficio. Per limitare al massimo le dipendenze e la relativa complessità di gestione ci faremo andar bene il supporto ai formati grafici di SDL, cioè il bitmap (.bmp). Esiste comunque l’estensione SDL_image per il supporto a praticamente tutti gli altri formati.

Preparare l’ambiente


Ci sono molti modi per impostare l’ambiente e le librerie: sotto Windows preferisco copiare tutti gli include, le lib e le dll direttamente nella struttura di MinGW (il compilatore) presente dentro la directory di installazione di CodeBlocks.

Questo passaggio non dovrebbe creare particolari problemi tranne per SDL_ttf e la mancanza nelle lib del file di libreria statica .a. Al suo posto troviamo, almeno al momento in cui scrivo, solo il file SDL_ttf.lib, ovvero il file di libreria per Visual Studio. MinGW lavora invece con i .a, ma niente paura. Nonostante il panico che serpeggia in diversi siti è sufficiente rinominare SDL_ttf.lib in libSDL_ttf.a e il gioco è fatto.

(PS: se qualcuno avesse la bontà di spiegarmi questa piccola magia, scoperta molto più per frustrazione che per intuizione, gliene sarei grato)

Lanciamo CodeBlocks e creiamo il nostro progetto C++ con il template Applicazione Console. Io l’ho chiamato TrisLick. CodeBlocks crea per noi il file main.cpp con un tranquillizzante e minimale hello world. Volendo avremmo potuto anche sfruttare il template per applicazioni SDL, ma preferisco sempre partire da una base pulita e aggiungere. Voi fate pure come credete meglio.

Ora dobbiamo includere i file header e linkare le rispettive librerie statiche dei nostri componenti di terze parti. Tasto destro sul titolo del progetto nell’albero di sinistra e Build Options. Nella scheda Linker settings specifichiamo i collegamenti alle librerie:



Se non abbiamo copiato i file nella struttura di MinGW sarà opportuno anche specificare i percorsi sul nostro disco perchè il compilatore trovi le risorse: lo possiamo fare dalla scheda Search directories di Build Options.

Testare le dipendenze


Modifichiamo leggermente il nostro hello world perchè testi il corretto collegamento con SDL:

#include <iostream>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>

using namespace std;

int main(int argc, char** argv)
{
SDL_Init(SDL_INIT_VIDEO);
TTF_Init();

cout << "Hello world!" << endl;

TTF_Quit();
SDL_Quit();

return 0;
}

Compiliamo ed eseguiamo. Se abbiamo fatto tutto bene il programma parte e termina senza errori. Ora siamo veramente pronti per partire!

venerdì 22 luglio 2011

Tutorial: scriviamo un gioco in C++... Tris!

In questo tutorial vedremo come scrivere un gioco in C++ dalla A alla Z utilizzando le librerie SDL e la loro estensione SDL_ttf (per la gestione del testo). Cercheremo di mettere in pratica i 5 consigli già illustrati, partendo quindi dall’idea di un gioco molto semplice: Tris, o Tic Tac Toe.

Il gioco in sè può sembrare banale, ma attenzione: nasconde più insidie di quanto sembri!

Per prima cosa occorre fare una ragionata su quel che vogliamo ottenere, e magari buttare giù una lista di caratteristiche/requisiti che ci guideranno durante lo sviluppo.

Parlando di Tris non c’è veramente tanto da pensare, a parte la scelta tra giocatore singolo (contro il computer) o multiplayer (contro un altro giocatore). Entrambe le opzioni richiedono implementazioni con caratteristiche proprie. Nel caso del single player dovremo sviluppare una AI, una Intelligenza Artificiale, in grado di impegnare il giocatore umano e dargli l’illusione di confrontarsi con un altro essere pensante. Nel caso del multiplayer dovremo invece gestire diversamente l’input di mouse e tastiera, discriminando a quale dei due giocatori appartiene.

Naturalmente si potrebbe pensare di implementare entrambe le soluzioni, ma per lo scopo di questo tutorial faremo i lupi solitari e ci concentreremo sulla modalità a giocatore singolo e, quindi, sullo sviluppo di una AI. Tuttavia, non porremo enfasi sul cosa farà l'AI, bensì sul come organizzare un sistema di AI. Il cosa riguarda un campo talmente ampio che esistono corsi di laurea apposta, e io non sono un professore! Sul come invece ci possiamo ragionare.

Molto bene, le idee ci sono. Figuriamoci per un istante come sarà il gioco, come se lo avessimo già finito e lo stessimo lanciando. Apparirà una title screen, con nome e versione del gioco, i crediti e i due pulsanti Play e Quit. Play ci porterà nella schermata di gioco, dove sfideremo il computer a colpi di mouse sulla ben nota griglia 3x3. Al termine della partita, dopo un messaggio di Game Over / Game Win verremo riportati alla title screen, da cui potremo decidere se giocare ancora o uscire tramite Quit.

Una cosa carina che potremmo inserire è il conteggio delle partite vinte dal giocatore e dal computer, e mostrarlo nella title screen. Questa feature, apparentemente innocua, nasconde l’esigenza di salvare (e rileggere) il dato da qualche parte. Facciamo le cose semplici e serviamoci di un file binario.

Ok, ci siamo già lasciati prendere la mano e il primo consiglio sta già sbiadendo... recuperiamo il controllo su noi stessi e diamo un cartellino giallo alla pericolosissima fantasia con un bel lavorino noioso: ordiniamo i pensieri appena espressi in una lista.

  • Title screen (serve un’immagine)
  • Pulsante Play
  • Pulsante Quit
  • Schermata di gioco (servono due immagini per ‘X’ e ‘O’)
  • AI
  • Gestione mouse
  • Messaggio di esito partita
  • Salvataggio e rilettura storico punteggi

Questa è la nostra lista della spesa, e sarà il nostro punto di riferimento durante lo sviluppo del nostro splendido Tris!

Nel prossimo post ci preoccuperemo di procurarci tutti i tool necessari, configurare l’ambiente e produrre i primi risultati a video.

Buon coding!

mercoledì 20 luglio 2011

Il gioco indie che forse non conoscete: The Haunted Ruins!

Siete cresciuti a pane e Eye of the Beholder? Rimpiangete i gloriosi tempi dell’Amiga e di quei titoli tanto innovativi ed emozionanti? Bene, spolverate spada e libro degli incantesimi e preparatevi a sguazzare nuovamente in sotterranei pieni di mostri ostili con questo titolo indie: The Haunted Ruins!

Il nome non brilla certo per originalità, e il gioco non smentisce questa sensazione. Ma che volete da un hack'n'slash? Quel po’ di storia che viene raccontata all’inizio dal solito vecchietto-capo-villaggio di turno può subire un solo destino: skip! E allora via, dentro le rovine senza nemmeno passare da fabbro o negozio, che ci sarà tempo più avanti per fare shopping.

Il gioco tenta di ricreare la grafica simil 3D di Eye of the Beholder, e questo è un punto a favore su quello che, altrimenti, sarebbe nientemeno che un comune roguelike (sempre che esistano comuni roguelike). Di mostri ce ne sono a bizzeffe, anche se per incontrarne di forti ed essere messi in difficoltà occorre scendere di almeno una quindicina di livelli. A quel punto però il giocatore medio si è stufato.

Buona l’idea di seminare i vari livelli del dungeon con un mago che di tanto in tanto ci insegna un nuovo incantesimo e un topino che ci da preziose dritte e qualche utile oggetto, il tutto ad interrompere la nota sensazione di deja vù di questo tipo di giochi. Il sistema di combattimento è semplice e molto comodo: in ogni momento abbiamo a disposizione tutto il nostro arsenale senza dover navigare attraverso menu e sottomenu di inventario.

The Haunted Ruins potrebbe diventare un buon gioco se l’autore ne migliorasse un po’ la grafica (ok retrò, ma anche solo texturizzare le pareti sarebbe un grosso passo avanti!) e ne vivacizzasse il ritmo, inserendo mostri più difficili fin da subito, o boss di fine livello. Allo stato attuale, purtroppo, può rapidamente portare alla morte, ma solo per noia.

venerdì 15 luglio 2011

Le 10 frasi che un programmatore non dovrebbe mai dire

Ci sono frasi che un programmatore non dovrebbe mai pronunciare quando si trova al telefono con il cliente che ha un problema (e il cliente al telefono ha sempre un problema). Eccone alcune:

Fase del rifiuto

  1. "Da me funziona..."
  2. "Deve essere un problema di rete"
  3. "Non è fatto per essere usato così"

Fase del dialogo costruttivo

  1. "... e cliccando su Ignora?"
  2. "Come ci siete riusciti?!?"
  3. "C'è Full Control Everyone su tutto il disco?"

Fase dello scarica barile

  1. "E' sicuramente il firewall..."
  2. "Succede solo da voi"

Disperazione

  1. "Gli altri programmi vanno?"

Ultima spiaggia

  1. "Provate a riavviare"

mercoledì 13 luglio 2011

HowTo: svuotare correttamente un STL Container (C++)

La libreria standard del C++ offre numerosi container, oggetti creati per contenere e organizzare altri oggetti. Il più famoso ed utilizzato è probabilmente vector. Molte documentazioni utilizzano però una frase sibillina nello spiegare la funzione del metodo clear() dei container:

il metodo clear elimina tutti gli oggetti contenuti, chiama i rispettivi distruttori, e pone a 0 la size del contenitore.

Una tale definizione sembra mettere al riparo da ogni problema il programmatore che decida (saggiamente) di usare un vector (o una list, un map, o queue) al posto di un comune array o di un oggetto custom. E invece non è così.

Facciamo qui un paio di esempi utilizzando due classi: Enemy e Level. Level conterrà una collezione di oggetti Enemy (anzi, di puntatori ad oggetti di tipo Enemy). Scopo dei due esempi è mostrare come svuotare correttamente tale collezione.

#include <iostream>
#include <vector>

//---------------------------------------------------------------
class Enemy
{
public:
   Enemy();
   ~Enemy();
};

Enemy::Enemy()
{
   std::cout << "Eseguo costruttore Enemy" << std::endl; 
}  

Enemy::~Enemy() 
{     
   std::cout << "Eseguo distruttore Enemy" << std::endl; 
}  

//---------------------------------------------------------------

class Level 
{ 
public:     
   Level();     
   ~Level();  
private:     
   std::vector<Enemy*> m_Enemies;
};

Level::Level()
{
   // creo e inserisco tre elementi Enemy nel vector     
   std::cout << "Eseguo costruttore Level" << std::endl;     

   m_Enemies.push_back(new Enemy());     
   m_Enemies.push_back(new Enemy());     
   m_Enemies.push_back(new Enemy()); 
}  

Level::~Level() 
{     
   // ripulisco il vector
   std::cout << "Eseguo distruttore Level" << std::endl;           
   m_Enemies.clear(); 
}  

//-------------------------------------------------------------- 

int main() 
{     
   Level* lev = new Level();      
   delete lev;      
   return 0; 
}  

Evidenziamo la classe Level, in particolare il suo distruttore. Notiamo che m_Enemies viene pulito tramite il metodo clear(). Sembra logico. Quel che ci attende però dopo l’esecuzione è questo output:



Mancano le chiamate al distruttore di Enemy, il che significa che clear() non ha correttamente liberato la memoria. La documentazione non è sbagliata, solo poco chiara. Se avessimo riempito il nostro vector con degli oggetti concreti anzichè con dei puntatori, il distruttore sarebbe stato chiamato correttamente. Ma, come nella maggior dei casi reali, abbiamo usato puntatori, e il programma non sa quale distruttore andare a pescare per distruggere l’oggetto puntato. Dobbiamo andargli un po’ incontro noi e modificare il distruttore di Level così:

Level::~Level() 
{     
   // ripulisco il vector     
   std::cout << "Eseguo distruttore Level" << std::endl;   
   
   while(!m_Enemies.empty())
   {
      Enemy* obj = m_Enemies.front();
      delete obj;
      m_Enemies.erase(m_Enemies.begin());
   }
}

E’ un po’ più lungo ma l’effetto è garantito. Quel che facciamo è ciclare sul vector finchè non risulta vuoto e ogni volta prendiamo l’elemento in testa. Chiamiamo esplicitamente il distruttore dell’oggetto con la delete ed eliminiamo il puntatore dal vector tramite erase, che diminuisce anche la size, impedendoci di entrare in loop con la guardia del while.

L’esecuzione darà ora questo risultato:



Buon coding!

lunedì 11 luglio 2011

Il gioco indie che forse non conoscete: Canabalt!

Il panorama indie è molto più sviluppato di quanto mediamente si creda. Non solo. E’ anche molto più evoluto, con giochi anche di alta qualità. Alcuni fanno della semplicità il loro punto di forza, ma attenzione: è una semplicità solo apparente, quasi più una ricerca estetica del minimale che un obbligo per mancanza di risorse.


Un valido esempio è Canabalt, un gioco flash giocabile via browser, sicuramente conosciuto a chi già segue la scena indie. L’idea di base di Canabalt è semplice e geniale. Voi siete un omino, e correte. Correte senza potervi fermare. Potete solo sperare di accelerare, ma fermarvi mai. 

Lo scenario è un grigio 2d urbano. Sulla vostra strada troverete ostacoli di ogni tipo, da evitare saltando. Salterete di tetto in tetto, entrerete negli edifici sfondando in volo le finestre, ruzzolando sul pavimento, in una breve illusione di pacifica staticità, solo per rialzarvi e rimettervi a correre.

Avrete una sola vita e la giocherete usando un solo tasto. Dal momento in cui inizierete a correre, sapete che potrà finire solo in due modi: o cadendo in un baratro, o schiantandovi contro un muro. Il vostro successo sarà calcolato in base a quanta strada avrete percorso. L'autore ha evidentemente una visione piuttosto tetra dell'esistenza... o forse è semplicemente realista?

Già, perchè non c’è tempo per riflettere. Si trattiene il respiro. I toni di grigio diventano la tavolozza ideale, anzi l’unica possibile, per dipingere sullo schermo Canabalt... che è poi la metafora del vivere d’oggi.


venerdì 8 luglio 2011

5 consigli pratici per sviluppare videogiochi (parte 5)

Nei quattro articoli precedenti abbiamo visto altrettanti consigli su come affrontare lo sviluppo di un videogioco, in particolare:

  1. Come gestire le idee
  2. Come scegliere gli strumenti da utilizzare
  3. Quali competenze possedere
  4. Da dove partire per fare esperienza e ricevere ispirazione

Il quinto consiglio arriva non a caso come ultimo. Si tratta del più importante, quello che in qualche modo li racchiude tutti, ma anche del più difficile da seguire.

Consiglio #5: Termina quel che inizi


Non c’è nulla di più difficile, veramente. Gli hard disk di migliaia di aspiranti game developer sono zeppi di progetti abbandonati, mentre i giochi finiti sono infinitamente meno. Il mio, naturalmente, non fa eccezione. Dopotutto qualcuno (Oscar Wilde?) diceva di poter accettare prediche solo da chi aveva (molto) peccato.

Ma perchè un progetto viene abbandonato? In fondo siete partiti con una grande idea, l’avete curata e limata a dovere, avete configurato tutto l’ambiente per poterla sviluppare, avete studiato per colmare le vostre lacune o per risolvere i problemi inattesi. Quindi? Perchè mai uno vorrebbe gettare via tutto questo lavoro? Sembra illogico, invece è purtroppo più la regola che l’eccezione.

Esistono in realtà diversi motivi, e forse analizzarli può aiutare a comprenderli e, se possibile, evitarli.

Il Dubbioso
Forse la vostra idea iniziale, che tanto vi ha entusiasmato nelle sue prime 24 o 48 ore di vita, non era poi un gran che. Ve ne siete resi conto solo dopo, magari in seguito ai primi sviluppi, quando avete iniziato a capire veramente quale sforzo comportava realizzarla. A che scopo?, ecco la domanda assassina che vi è balzata in testa. Crudele, ma forse necessaria. Non cercate l’accanimento terapeutico e lasciate morire in pace il progetto. Il vostro prossimo tentativo terrà maggiormente in considerazione il primo consiglio.

L'Ottimista
Forse avete fatto i conti senza l’oste. Le features a cui avevate pensato erano apparse molto più alla vostra portata. Sulla carta tutto sembrava più semplice, anche perchè forse alcune problematiche pratiche sono semplicemente emerse dopo. Non sapevate di non sapere. Poco male, avete perso il progetto (per ora), ma avete guadagnato molto a livello personale: possedete ora una maggiore percezione dei vostri limiti e di quel che occorre per creare un gioco. Sapete in quali campi applicarvi per studiare e colmare le mancanze. Fatelo!

Il Disorientato
Forse siete stati colti da noia improvvisa. L’idea era buona, era anche tutto sommato realizzabile, ma dopo due mesi ancora non siete arrivati a qualcosa di giocabile, e quel momento sembra perso in un orizzonte indefinito. Se potete stringete i denti, tagliate qualcosa e cercate di terminare in un altro paio di giorni (al massimo). Se questo non è possibile vuol dire che vi siete imbarcati in un progetto troppo grande per voi, non tanto per difficoltà ma proprio per dimensioni. Piccolo errore di valutazione iniziale, ma la prossima volta andrà meglio.

Il Curioso
Forse vi siete distratti. Avete visto qualcosa che luccicava di più, avete sospeso momentaneamente i lavori (promettendo a voi stessi che non stavate abbandonando nulla, che poi mi ci rimetto!) e vi siete lanciati nello studio di questa nuova tecnologia/libreria. Riprendere in mano il progetto, man mano che passano i giorni, diventa sempre più un’utopia. Siate almeno onesti con voi stessi. Non lasciate i lavori nel limbo. Tagliate quel cordone ombelicale e andate avanti. Forse la nuova cosa che ha rubato la vostra attenzione merita veramente un tentativo.

Come vedete, di motivi ce ne possono essere veramente tanti. Analizzandoli, si possono trarre delle interessanti conclusioni su come non cadere in tali trappole. In particolare si evince che:


  • I progetti vincenti sono quelli che potete realizzare, non quelli che volete realizzare
  • I progetti vincenti sono quelli fattibili in x giorni, dove x è un numero discreto, ben noto a priori, variabile a seconda del soggetto (voi). Determinarlo è un processo a tentativi. Datevi delle scadenze.
  • Restate focalizzati. Mantenete l’attenzione su cosa state facendo per gli x giorni che servono a farlo. Abbiate fretta di giungere a qualcosa di completo. Non pensate a ottimizzazioni o a particolari abbellimenti. Fate il vostro gioco. Fatelo grezzo. Ma fatelo.
  • Se x sono i vostri giorni per completare il progetto, spenderne la metà o più in analisi, meditazioni e pianificazioni non vi porterà da nessuna parte. Pianificate l’inizio, decidete la rotta e poi partite immediatamente. Tanto non finirete dove avevate pensato nemmeno con un documento di design di 100 pagine.
  • Le dimensioni del precedente progetto vi hanno costretto ad abbandonarlo? Il prossimo pianificatelo per essere il 50% del precedente.
  • Avete già fallito in n progetti precedenti? Continuate a fallire per via degli stessi motivi? Apparentemente non imparate nulla dai vostri errori? Errare è umano, ma perseverare è proprio da programmatori. Siete finiti in un loop mentale. Reset. Anzi, break.


Soprattutto non scoraggiatevi e non demoralizzatevi. Mai. Ci sono fior fior di illustri personaggi che sono passati (e che ci passano ancora!) da questi tristi lidi. Eppure producono giochi di successo.

Vi lascio con un recente post di Thomas Biskup, il creatore di ADOM, uno storico roguelike. Attualmente (già da un po’ di tempo, a dire il vero... eh Tom?) è alle prese con JADE, una sorta di porting Java di ADOM, più moderno e ricco di features. Ma anche, evidentemente, più complesso. Ecco un suo post a proposito, che riprende un po' le conclusioni a cui siamo arrivati noi.

Concludendo...

Non so se questa serie di post aiuterà realmente qualcuno a realizzare il proprio ambizioso desiderio di scrivere un videogioco. Se lo farà, ne sarò felice. Ma lo sarò anche solo se qualcuno si sentirà motivato e invogliato a provarci!

Buon coding!

Post correlati:

mercoledì 6 luglio 2011

5 consigli pratici per sviluppare videogiochi (parte 4)

Ci siamo quasi. Abbiamo le idee chiare, i tool giusti e tutte le abilità necessarie per fare un videogioco. Ci manca solo un po’ di esperienza, di palestra.

Programmare è una abilità che, come tutte le abilità, migliora con la pratica. Difficilmente produrremo il nostro capolavoro al primo tentativo. Occorre invece fare molto e sbagliare molto, oltre naturalmente alla capacità di riconoscere e correggere i propri errori.

Alcuni errori, tuttavia, non si palesano immediatamente come tali. Anzi, spesso ci appaiono come nostre particolari, addirittura geniali, interpretazioni di una regola. Nella programmazione questo accade frequentemente, essendo una attività creativa e quindi volta al problem solving e alla costante ricerca di modi diversi per fare qualcosa.

In quasi ogni progetto, ad esempio, implemento in modo diverso la gestione dei messaggi tra i componenti dell’applicazione. Ogni volta termino con un certo orgoglio, felice della complicata massa di classi e pattern che si interfacciano in modi sempre più sofisticati. Salvo poi riaprire il progetto qualche giorno dopo, e capire di non capirci più nulla. Ogni volta la conclusione è la stessa: la prossima volta devo semplificare qualcosa.

Programmare è una questione di raffinamenti successivi, roba per cui occorre esperienza. Non esiste altra via.

Ma per fare esperienza occorre, indovina un po’... programmare! Dopotutto siamo informatici e la ricorsione non dovrebbe spaventarci. Quindi...

Consiglio #4: Copia i Classici


Il consiglio di questo articolo riguarda proprio il come fare esperienza. Potete lanciarvi subito nell’implementare le vostre idee, certo, ma forse non è il modo migliore. Quando scrivete il vostro gioco siete impegnati su molti fronti, primo fra tutti l’analisi e la ricerca di meccaniche avvincenti e divertenti, oltre che semplici da implementare. Fare esercizio dovrebbe invece essere una attività più focalizzata. Ci si esercita meglio e con maggior profitto se l’area su cui si vuole lavorare è contenuta. Esistono centinaia (migliaia?) di giochi già consolidati e dal provato valore di intrattenimento. Sapreste riprodurli?

Prendiamo Tetris. Tutti lo conosciamo e ci abbiamo giocato. Non ci occorre ragionarci per più di un paio di minuti per focalizzarne gli aspetti principali del gameplay. Dovremmo anche riuscire a vederne mentalmente il codice e capire di quali componenti è fatto:


  • l’area di gioco (una semplice sezione rettangolare dello schermo, nera o con un’immagine di sfondo);
  • l’anteprima del blocco successivo
  • l’area dei punteggi
  • il blocco corrente, unico elemento che si muove (in automatico verso il basso e con l’interazione del giocatore per le rotazioni e i movimenti laterali)
  • il muro che si forma
  • le righe complete (da riconoscere ed eliminare in tempo reale, abbassando il muro)
  • il muro che raggiunge la cima dell’area di gioco (condizione di gameover)
  • il numero di righe completate che raggiunge il numero stabilito dal livello (condizione di vittoria)



Una volta fatta questa lista avete messo nero su bianco le caratteristiche fondamentali di Tetris. Potete considerarla il documento di analisi e design, o la lista dei requisiti! Il gioco funziona, non dovete preoccuparvi di aspetti quali regole e giocabilità. Dovete solo scriverlo.

Tetris è, naturalmente, solo un esempio, ma non l’ho citato a caso. E’ in effetti uno dei primi giochi che andrebbero scritti, perchè fornisce una ottima palestra per comprendere i meccanismi di base di un videogioco: il game loop (ricevi ed elabora l’input, aggiorna lo stato degli oggetti, disegna il frame), le collisioni e la gestione della logica (punteggi, passaggi di livello).


Altri giochi sono l’immancabile Pong, Breakout (Arkanoid), Space Invaders, Asteroids... una cosa molto divertente e stimolante che potete fare è scaricarvi e installarvi il progetto Mame32, che contiene una vera miniera di giochi arcade anni ‘80-’90. (Ri-)Scopriteli e giocateli. Riuscirete a trovarne decine e decine da clonare per esercizio e, perchè no, anche da migliorare con vostre aggiunte.

Quando avrete implementato un paio di questi classici non solo avrete acquisito molte conoscenze indispensabili, ma avrete affinato il vostro stile di programmazione e la vostra mente. Sarete in grado di guardare ai giochi più complessi con maggior senso critico. Capirete (e riconoscerete in altri giochi, anche più recenti) le meccaniche di gameplay caratteristiche dei vari generi (platform, puzzle, shooter...).

E, cosa più importante, potrete finalmente dire: io scrivo videogiochi!



Post correlati:


 

lunedì 4 luglio 2011

Il gioco indie che forse non conoscete: Appy 1000mg!

Ludum Dare è un concorso online, ormai storico e giunto alla 20esima edizione, che sfida i partecipanti a produrre un gioco funzionante in 48 ore. Il gioco viene poi valutato secondo molti aspetti, ma non è del Ludum Dare che intendo parlare, bensì del vincitore di questa 20esima edizione: Appy 1000mg.

Un titolo curioso, come da tradizione indie, ma che si svelerà nel suo terribile significato solo finendo il (breve) gioco. Per la cronaca si tratta di un platform, ma questa definizione lascia il tempo che trova. Sarebbe più opportuno etichettarlo come Poesia. Una poesia molto triste, tra l’altro. Appy 1000mg si lascia finire in meno di 10 minuti ma può arrivare a commuovere i più sensibili! Ancora una volta lo sviluppo indie ha dimostrato la sua incredibile capacità di descrivere la realtà, soprattutto nei suoi aspetti più oscuri.


Il gameplay è essenziale. All’inizio del gioco possiamo solo saltare, vagando per un ambiente bucolico fatto di erba verde e bubbies, simpatiche e innocue creaturine rosa. Nelle fasi più avanzate avremo a disposizione anche uno zainetto jet col quale compiere piccoli voli e raggiungere così piattaforme altrimenti impossibili.

La grafica è eccellente e sembra quasi incredibile che sia stata concepita e realizzata in appena 48 ore! Non è semplicemente ben curata, ma riesce a dare un vero tocco di personalità all'intero gioco, aumentando il senso di immersione nel piccolo mondo. Il blog di sviluppo (eh già, questo ragazzo ha anche avuto il tempo di scrivere post!) svela qualche dettaglio relativo al sistema utilizzato per il rendering della mappa, che inizialmente doveva essere random ma che poi, per motivi di praticità, è stato declassato a semi-random. Le informazioni sul terreno di gioco sarebbero lette da una bitmap di 400x100 pixel, scalata a 4x e creata tramite un algoritmo basato sul deposito di sedimenti. Anche il particle engine è stato sviluppato ad hoc per l'occasione, e il risultato è veramente notevole.

L’autore ha prodotto anche una seconda versione del gioco, fuori concorso, nella quale intende migliorare il gameplay aggiungendo elementi ed ampliando il mondo (e magari inserendo una colonna sonora). Ma la sensazione è che quel che Appy 1000mg aveva veramente da dire, l’abbia detta. E’ sicuramente possibile migliorare il gioco, ma la scintilla d’arte che c’è dentro è già perfetta.


Appy 1000mg è un gioco che difficilmente vi lascerà indifferenti. Nella piccola manciata di minuti che vi ruberà riuscirà a farvi sorridere, preoccupare, divertire e, soprattutto, riflettere. Già, perchè il senso di inquietudine con cui affronterete l'ultima fase non sarà dato solo dall'improvvisa svolta della trama e dall'uso di pixel dai colori più scuri, ma dal fatto di riconoscere situazioni tristemente reali, e di mescolare la pietà con l'orrore.