requestAnimationFrame: adiós al setInterval.
Hasta hace poco la única forma que teníamos de ejecutar nuestro game loop era a través del setInterval. Por suerte ahora disponemos de una nueva función más adecuada para la ocasión: requestAnimationFrame.
¿Por qué cambiarlo si iba bien?
Pues porqué realmente no iba “tan bien”.
El mayor problema del setInterval es que va a mandar peticiones para ejecutar nuestro game loop cada X milisegundos indiferentemente de si el sistema está ocupado o no. Por lo tanto, si pasados los X milisegundos el game loop no ha terminado, setInterval igualmente va a mandar una nueva petición. Como el sistema estará aún ocupado con el game loop anterior, éste pondrá dicha petición en “cola de espera”.
Imaginad que el game loop necesita 60ms para ejecutarse, pero setInterval está llamando al game loop cada 30ms… ¡por cada ejecución real del game loop estaríamos añadiendo 2 peticiones en la “cola de espera”! Y cómo podéis imaginar eso supone un gasto innecesario de memoria.
El segundo problema con setInterval, es que seguirá emitiendo peticiones aunque no tengamos el foco en el juego: Si minimizamos la ventana o cambiamos de tab, setInterval seguirá mandando peticiones. Una vez más, un gasto innecesario de memoria… ¡y de batería en dispositivos móviles!.
¿Cómo va el requestAnimationFrame?
requestAnimationFrame espera recibir un sólo parámetro: La función que se quiere ejecutar. A diferencia de setInterval, una vez finalizado el game loop tenemos que volver a llamar a la función requestAnimationFrame pasándole nuevamente la función a ejecutar. Veamos un ejemplo simple:
var gameLoop = function () { update(); draw(); // Una vez hayamos terminado de ejecutar el game loop, // volvemos a utilizar la función requestAnimationFrame. requestAnimationFrame(gameLoop); }; // Llamamos el gameLoop por primera vez. requestAnimationFrame(gameLoop); |
Con requestAnimationFrame los exploradores van a intentar correr nuestra aplicación a 60 FPS. En caso de no conseguirlo porqué nuestro game loop es muy complejo, NO va a generar una “cola de espera” como pasaba con el setInterval.
La história de siempre: Browsers
Supongo que no os sorprenderá si os digo que actualmente cada explorador tiene su propia implementación de dicha función. Así que, hasta que no se pongan de acuerdo, deberemos hacer un pequeño apaño para obtener la función “requestAnimationFrame“:
var getRequestAnimationFrame = function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function ( callback ){ window.setTimeout(enroute, 1 / 60 * 1000); }; }; |
Con esto, el código de ejemplo que he puesto antes quedaría de la siguiente forma:
var fpAnimationFrame = getRequestAnimationFrame(); var gameLoop = function () { update(); draw(); // Una vez hayamos terminado de ejecutar el game loop, // volvemos a utilizar la función requestAnimationFrame. fpAnimationFrame(gameLoop); }; // Llamamos el gameLoop por primera vez. fpAnimationFrame(gameLoop); |
¡Listos!
holaaas
funciona perfecto, pero en Opera 12.01
envia error:
Unhandled Error: Undefined variable: enroute
en
function ( callback ){
window.setTimeout(enroute, 1 / 60 * 1000);
};
};
y lo solucione asi
function ( callback ){
window.setTimeout(callback, 1 / 60 * 1000);
};
};
pasandole el callback al setTimeout
saludos
Una preguntita como integras esto al framework http://www.project-cyan.com/2012-02-crear-framework-para-juegos-javascript-canvas-html5-lightcyan/ vengo siguiendo tus tutoriales que estan muy bien pero intente adaptarlo al framework y nada mas no jalo jeje me quede con la opcion viejita para seguir trabajando, si tienes un ejemplo estaria excelnte muchas gracias