Datti una mossa

Nell'ultimo articolo (Tempo di giocare) ho introdotto il concetto di timing nei giochi, in questo articolo voglio introdurre i concetti base del disegno e del movimento di oggetti sullo schermo utilizzando le librerie SDL.

Iniziamo definendo alcune costanti, la larghezza e l'altezza di un nostro ipotetico personaggio sullo schermo:


#define PLAYER_W 10 //Larghezza
#define PLAYER_H 10 //Altezza


In questo caso il nostro personaggio avra' una forma prettamente quadrata, possiamo passare ora alla definizione di una struttura atta a contenere altre informazioni utili.
Creiamo il tipo di dato "Player" ad esempio, nel modo che segue:

struct _Player
{
int x, y; //Coordinate del giocatore
} Player;

Due semplici variabili di tipo integer per gestire separatamente le coordinate del nostro giocatore nella finestra di gioco, queste coordinate saranno incrementate e decrementate dalla pressione dei tasti sulla tastiera.

Sulle coordinate del nostro personaggio verra' disegnata la sua immagine, io per comodita' ho racchiuso il tutto in una funzione:

void DrawRect(int x, int y, int width, int height, int color)
{
rect.x = x;
rect.y = y;
rect.w = width;
rect.h = height;
SDL_FillRect(screen, &rect, color);
}

Questa funzione sfrutta delle strutture gia' dichiarate all'interno delle libreria SDL, tra cui SDL_Surface ed SDL_Rect.
Possiamo dichiarare come globali delle variabili di questo tipo in cima al nostro sorgente:


SDL_Surface *screen; //L'intera area di gioco
SDL_Rect rect; //Un area per il disegno

All'interno della superfice "screen" verranno piazzate come in un collage le varie immagini rappresentate da "rect", il tutto viene effettuato dalla funzione "SDL_FillRect()".
Ora ci bastera' richiamare la funzione DrawRect in modo analogo per disegnare il nostro personaggio sullo schermo:

DrawRect(Player.x, Player.y, PLAYER_W, PLAYER_H, 0xffffff);

L'argomento color e' un semplice valore esadecimale come quelli che si usano nelle pagine html proprio per i colori, in questo caso il nostro personaggio sara' bianco.
Questa funzione verra' richiamata nel main game loop ad ogni ciclo disegnando un rettangolo colorato delle dimensioni specificate alle coordinate "attuali" del giocatore.

Per incrementare e decrementare le coordinate del nostor giocatore scriviamo quindi passo passo la nostra funzione getInput()" che verra' anch'essa usata successivamente nel main game loop:


int getInput()
{
while(SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT ||
(event.type == SDL_KEYDOWN &&
event.key.keysym.sym == SDLK_ESCAPE)) quit = 1; /* Window closed */
}

Questo ciclo esegue un "polling" su una struttura contenente una lista di eventi, ovvero controlla la presenza di un qualunque evento in questa lista, lo esegue e lo toglie dalla coda.
In questo caso controlla se vi e' la volonta' di uscire dal programma controllando l'eventuale pressione del tasto ESC, in caso di positivita' assegna ad una variabile quit il valore di 1, ritroveremo questa variabile piu' avanti nel corso del sorgente.

// Controlla lo stato della tastiera
keystate = SDL_GetKeyState( NULL );
/* Gestisce la pressione dei tasti */
if (keystate[SDLK_LEFT])
Player.x = -1;

if (keystate[SDLK_RIGHT])
Player.x = 1;

if (keystate[SDLK_UP])
Player.y = -1;

if (keystate[SDLK_DOWN])
Player.y = 1;
}

Qui troviamo la variabile "keystate" associata alla funzione SDL_GetKeyState(), questa funzione ci ritorna lo stato di un dato tasto passatogli come argomento.
I valori "SDLK_" sono delle costanti numeriche definite all'interno della libreria SDL riferiti ai valori ASCII dei tasti.
In questo caso viene controllato se un determinato tasto e' premuto, in caso positivo viene incrementata una coordinata del giocatore.
Se gli input qui elencati fossero gestiti nel ciclo di polling come per il tasto ESC, si dovrebbe continuamente ripremere il tasto per incrementare o decrementare le coordinate del giocatore.
Questo metodo di gestire gli input ci permette di avere un controllo "continuo" sulla pressione di un eventuale tasto ad ogni ciclo.

Si noti bene che le variabili keystate, quit ed event non sono definite nella funzione, andranno definita in maniera globale nel modo seguente:

SDL_Event event; // Struttura per eventi
Uint8 *keystate; // Stato della tastiera
int quit=0, repeat;

Ora che abbiamo sia la funzione di disegno che quella per la gestione dei movimenti del nostro giocatore, possiamo assemblare il tutto, inseriamo la nostra funzione di gestione del timing come nel precedente articolo:

int fps_sync ()
{
t = SDL_GetTicks ();
if (t - tl >= frequency)
{
temp = (t - tl) / frequency;
tl += temp * frequency;
return temp;
}
else
{
SDL_Delay (frequency - (t - tl));
tl += frequency;
return 1;
}
}

Ora implementiamo il nostro main game loop in una funzione apposita, successivamente lo richiameremo all'interno di un ciclo.

int mainLoop()
{
//main game loop
while(!quit)
{
repeat = fps_sync ();
for (i = 0; i < repeat ; i++){
getInput();
}

Come gia' visto nell'articolo "Tempo di giocare", limitiamo il numero di azioni di gioco eseguite al secondo. Si nota anche la presenza di un !quit come argomento nel while, finche' quit sara' uguale a 0 il ciclo continuera'.

// Cancella tutto lo schermo di gioco
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
// Disegna il personaggio
DrawRect(Player.x, Player.y, PLAYER_W, PLAYER_H, 0xffffff);
// Aggiorna lo schermo di gioco
SDL_Flip(screen);
}
}

Qui ho usato la funzione SDL_FillRect() per riempire lo schermo di nero, se non si facesse cio' si continuerebbero a vedere i disegni del giocatore nelle varie posizioni passate. Diamo come un colpo di gomma e ridisegniamo successivamente con la funzione DrawRect() il nostro personaggio.
Con SDL_Flip() aggiorniamo interamente lo schermo e lo rendiamo pronto per la visualizzazione, su superfici software in alternativa viene richiamato SDL_UpdateRect().

Ora non ci rimane che popolare la nostra funzione principale e testare il nostro prodotto.

int main()
{
// Inizializziamo la libreria SDL
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_SWSURFACE);
if(screen == NULL)
{
fprintf(stderr, "Can't initialize SDL: %s\n", SDL_GetError());
exit(-1);
}

SDL_WM_SetCaption("Esempio", "Esempio");

// Posizione di partenza del nostro giocatore
Player.x = 64;
Player.y = 64;

//main game loop
mainLoop();
SDL_Quit(); // Terminiamo l'utilizzo della libreria in modo corretto
return 0;
}

Il nostro gioco di base e' pronto, al momento non fa molto a parte muovere un quadrato bianco sullo schermo ma come base per capire i concetti di un videogame va piu' che bene.
Salvate il tutto come game.c e compilate il vostro gioco con gcc -o game game.c -lSDL, oppure impostate il vostro IDE preferito e divertitevi!

Nessun commento :

Posta un commento

Related Posts Plugin for WordPress, Blogger...