Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Game Loop e requestAnimationFrame

Impostare il ciclo in cui si aggiorna lo stato dei GameObject, verifica le collisioni ed effettua i rendering.
Impostare il ciclo in cui si aggiorna lo stato dei GameObject, verifica le collisioni ed effettua i rendering.
Link copiato negli appunti

Una caratteristica che accomuna tutti i game engine, è il Game Loop: una funzione che gestisce il ciclo del gioco: la fisica (l'aggiornamento di posizione dei nostri elementi di gioco, il controllo delle collisioni), e il Draw, ovvero il rendering degli oggetti che fanno parte del gioco (i cosiddetti GameObject).

requestAnimationFrame, gestire il refresh della scena

In HTML5 è stata inserita la funzione requestAnimationFrame che rimpiazza il vecchio metodo di animazione, che utilizzava un timer (setTimeout) per renderizzare un frame ogni tot millisecondi.

Con requestAnimationFrame possiamo indicare al motore JavaScript quale funzione lanciare la prossima volta che scatterà un frame. Ecco la sintassi:

(request ID) = requestAnimationFrame(callback)

Grazie a questa funzione nativa invece, il browser può ottimizzare il rendering nativamente mantenendo un framerate più stabile e fluido (con un massimo di 60fps), riducendo drasticamente i fastidiosi "scatti" che si notavano utilizzando i Timer (setTimeout, setInterval).

Nel caso si passasse a un'altra scheda del browser, la funzione disabiliterebbe temporaneamente il rendering , a differenza di setTimeout, che invece continuerebbe a funzionare sfruttando CPU, GPU e memoria: un utilizzo maggiore di prestazioni hardware, che nei portatili e dispositivi mobili, si traduce in uno spreco di batteria.

Un polyfill per requestAnimationFrame

Poiché HTML5 non è ancora standard (lo sarà entro il 2014), requestAnimationFrame è stato implementato temporaneamente con un prefisso negli identifier (webkit, moz, o, ms), che si riferisce ai nomi dei vari browser: definiamo quindi una funzione generica, un polyfill, che ritorni l'id della funzione corretta.

Per non dover ogni volta richiamare tutte le possibili funzioni creiamo quel che si suol dire un Polyfill, ovvero una funzione che si occuperà di richiamare la funzione giusta per ogni client.

Creiamo un nuovo file JavaScript (utils.js), in cui inserire tutte le funzioni di comodo che utilizzeremo del nostro framework. Iniziamo con questo polyfill:

window.requestAnimFrame = (function(callback) {
	return window.requestAnimationFrame ||
		   window.webkitRequestAnimationFrame ||
		   window.mozRequestAnimationFrame ||
		   window.oRequestAnimationFrame ||
		   window.msRequestAnimationFrame ||
		   function(callback) {
		      window.setTimeout(callback, 1000 / 60);
		   };
})();

Anche se la probabilità è remota, potremmo avere un browser che non supporta requestAnimationFrame, in questo caso la nostra funzione utilizzerà il vecchio metodo setTimeout.

Già che ci siamo, spostiamo l'event listener load da main.js a utils.js

window.addEventListener('load', function() {
  StartGame();
}, true);

GameLoop

Possiamo finalmente definire il GameLoop, ovvero quella funzione che ci permette di creare un ciclo infinito in cui gestire animazioni ed eventi frame dopo frame. Definiamo quindi la funzione GameLoop, all'interno dell'oggetto Game:

function Game() {
	// ... cose definite nella lezione precedente
	this.GameLoop = function() {
		if(!this.paused) {
			// aggiorna tutti gli oggetti
			this.Update();
		}
		//disegna l'intera scena a schermo
		this.Draw();
		window.requestAnimFrame(function() {
			// rilancia la funzione GameLoop ad ogni frame
			game.GameLoop();
		});
	}
}

Il cuore del loop sta nella chiamata a requestAnimFrame, in cui chiamiamo nuovamente la funzione GameLoop dell'istanza di Game, in modo che si inneschi un meccanismo ricorsivo, in cui vengono eseguiti Update() e Draw() ogni frame.

La maggior parte dei framework per videogiochi, prevede che gli oggetti nel gioco (GameObject, che stiamo per vedere) siano dotati delle funzioni Update e Draw, ciò fa di esse due tasselli fondamentali dell'engine e anche se non sono ancora definite ci impegnamo a definirle a breve.

Definiamo inoltre la variabile paused, che bloccherà la funzione Update quando sarà impostata a true.

Nota: Draw() rimane sempre attiva, così da renderizzare comunque le immagini. Nei prossimi capitoli vedremo come ottimizzare le prestazioni durante la pausa utilizzando i canvas OffScreen.

Ti consigliamo anche