Tempo di giocare

Ricordo tempo fa di giochi che se eseguiti su sistemi piu' recenti rispetto al loro anno di nascita' schizzavano a velocita' pazzesche fino ad essere ingiocabili. Il problema risiedeva in una dimenticanza o non curanza dei programmatori di un aspetto molto importante.
Il "timing", in italiano si potrebbe tradurre letteralmente in temporizzazione.

Un qualsiasi programma per quanto semplice sia, verra' eseguito alla velocita' massima consentita dalla macchina e dal sistema ospitante, cio' vuol dire che piu' il nostro pc sara' veloce piu' veloce sara' la nostra applicazione nell'elaborazione dei dati e del suo lavoro.
I giochi essendo di base "applicazioni" come un qualsiasi altro programma, possono essere composti da cicli tipo for o while che verranno eseguiti alla massima velocita' fornita dal processore.

Un gioco tipo ha uno schema di base simile al seguente:

int main()
{
//main game loop
while()
{
//meccanismi di gioco
muoviGiocatore();
aggiornaPunteggio();
Disegna();
}
return 0;
}

Una funzione da cui parte tutto e un ciclo principale detto "main game loop", entro cui il gioco vive.
In questo ciclo vengono ripetute sistematicamente tutte le funzioni relative ai vari aspetti del gioco, tipo il movimento dei personaggi e assegnazione punteggi o il semplice disegno sullo schermo delle immagini.
Nell'entrare nella funzione di "muoviGiocatore()" si effettua un controllo sulla tastiera per controllare se un dato tasto e premuto, nel caso lo sia si muove il nostro personaggio di 1 pixel nella direzione voluta, in caso contrario si continua il ciclo.
Tenendo conto che il ciclo verra' eseguito N volte in base alla velocita' del processore, la pressione del tasto nell'arco di un appena un secondo verra' letta piu' di quanto ci si aspetti. Si vedrebbe il nostro personaggio schizzare letteralmente fuori dal campo di gioco ad una velocita' pari quasi a quella del processore. Impossibile seguire l'azione ad occhio nudo.

Per ovviare a questo semplice problema, si dovrebbe campionare "1 secondo" reale nel programma ed eseguire le azioni una volta per ogni secondo "reale".
I computer gia' a livello di hardware sono dotati di sistemi per il conteggio del tempo, in elettronica tutto cio' e' detto "cloacking" e molti linguaggi di programmazione mettono a disposizione strumenti per campionare il clocking e gestire il tempo.

Esistono funzioni nei linguaggi di programmazione che ci permettono di sapere quanti millisecondi sono passati dall'avio del programma, e' il caso della funzione clock_gettime() presente nella glibc ed utilizzato anche nella libreria SDL per fornire supporto nella gestione del tempo nei nostri giochi. L'esempio qui di seguito utilizza la funzione SDL_GetTicks(), questa funzione ci restituisce il numero di millisecondi passati fin da quanto la libreria e' stata inizializzata. In altre parole ci restituisce il tempo passato in millisecondi dalla partenza del programma

int fps_sync (void)
{
static int t, tl = 0, frequency = 1000 / 100, temp;

t = SDL_GetTicks (); //Tempo trascorso dall'inizio del programma

if (t - tl >= frequency)
{
temp = (t - tl) / frequency;
tl += temp * frequency;
return temp;
}
else
{
sleep (frequency - (t - tl));
tl += frequency;
return 1;
}
}

La funzione qui sopra aggiorna la variabile T (Tempo atutale) con il numero di millisecondi restituiti da SDL_GetTicks() e controlla che tra T e TL (Tick last) vi sia una differenza di almeno 10 millisecondi, rappresentati dalla frequenza e viene restituita una variabile che successivamente verra' usata nel main game loop.
Se non vi e' questa differenza tra T e TL, viene messo a dormire il processo sulla macchina per un tempo variabile tra 0 e 10 millisecondi e succesivamente si incrementa TL di 10 millisecondi in modo da recuperare il conteggio perduto nello sleep.

Questa funzione torna utile nel nostro main game loop in quanto ci permettera' di limitare l'uso della cpu in modo dinamico.

while()
{
repeat = fps_sync ();

for (i = 0; i < repeat; i ++)
{
muoviGiocatore();
aggiornaPunteggio();
}
Disegna();
}

In un lasso di tempo (frequenza) verranno eseguite le azioni di gioco all'interno di un ciclo limitato dalla variabile temp rilasciata dalla funzione fps_sync().
Il tutto in modo dinamico, garantendo la stessa velocita' su diversi computer a discapito della velocita' del processore.
Questo modo di gestire gli FPS rispetto a molti altri vanta la possibilita' di poter limitare solo alcune parti del programma garantendo allo stesso tempo la massima velocita' nella resa grafica, a differenza di molti altri metodi usati da molti che mettono a dormire l'intero programma indistintamente.

Questo articolo prende spunto dall'articolo presentato dal sito di Loser Juegos da cui si trova ben poco riguardo i dettagli di funzionamento dell'algoritmo intero, qui ho provato a spiegarlo ed esporlo al meglio.
Se non vi sara' chiaro al primo colpo, non vi scoraggiate, provate a pacioccare un po' con le varie variabili e vedere i vari effetti sul gioco.

Nessun commento :

Posta un commento

Related Posts Plugin for WordPress, Blogger...