2009-12-23 11 views
17

¿Hay una manera simple de hacer un bucle de juego en JavaScript? algo así como ...¿La mejor forma de juego simple en Javascript?

onTimerTick() { 
    // update game state 
} 
+0

En el mundo orientado a eventos de JavaScript es probable que no necesita hacer esto. ¿Por qué crees que necesitas esto? –

+10

@Jon, ¿cómo vas a actualizar un juego cuando no se ha activado ningún evento? Muchos juegos están haciendo cosas incluso cuando no ... – James

Respuesta

22
setInterval(onTimerTick, 33); // 33 milliseconds = ~ 30 frames per sec 

function onTimerTick() { 
    // Do stuff. 
} 
+0

Probablemente quieras invertir el orden de la primera línea y el resto. ;) – Amber

+1

Continúa, hazlo 33ms ... ;-) –

+1

Hecho;) ... @Dav, ¿por qué? – James

4

Sip. Usted quiere setInterval:

function myMainLoop() { 
    // do stuff... 
} 
setInterval(myMainLoop, 30); 
3

Would this do?

setInterval(updateGameState, 1000/25); 

Donde 25 es su FPS deseado. También podría poner allí la cantidad de milisegundos entre fotogramas, que a 25 fps sería de 40 ms (1000/25 = 40).

+1

usted prolly quiere pasar la función, no es su resultado –

+1

Tiene razón, corregido. –

+0

no maneja deriva y causará latencia. No usa webkit –

28

Hay una cantidad variada de maneras de lograr esto usando JavaScript dependiendo de su aplicación. Un setInterval() o incluso con una sentencia while() haría el truco. Esto no funcionará para un bucle de juego. JavaScript interpretado por el navegador, por lo que es propenso a interrupciones. Las interrupciones harán que la reproducción de tu juego se sienta nerviosa.

Las propiedades webkitRequestAnimationFrame de CSS3 intentan corregir esto administrando el bucle de renderizado. Sin embargo, esta no es la manera más eficiente de hacer esto y será propenso a los nervios si tiene muchos de los objetos que se actualizan.

Este es un buen sitio para empezar con:

http://nokarma.org/2011/02/02/javascript-game-development-the-game-loop/index.html

Este sitio tiene una buena información sobre los fundamentos de hacer un bucle de juego. No toca ningún tipo de diseño orientado a objetos de ninguna manera. La forma más precisa de lograr tiempos precisos es mediante el uso de la función de fecha.

while ((new Date).getTime() > nextGameTick && loops < maxFrameSkip) { 
    Game.update(); 
    nextGameTick += skipTicks; 
    loops++; 
} 

Esto no tiene en cuenta cómo setTimeout se desplaza a altas frecuencias. Esto también llevará a que las cosas se desincronicen y se pongan nerviosas. JavaScript deriva +/- 18 ms por segundo.

var start, tick = 0; 
var f = function() { 
    if (!start) start = new Date().getTime(); 
    var now = new Date().getTime(); 
    if (now < start + tick*1000) { 
     setTimeout(f, 0); 
    } else { 
     tick++; 
     var diff = now - start; 
     var drift = diff % 1000; 
     $('<li>').text(drift + "ms").appendTo('#results'); 
     setTimeout(f, 990); 
    } 
}; 

setTimeout(f, 990); 

Ahora pongamos todo esto en un ejemplo de trabajo. Queremos inyectar nuestro bucle de juego en el bucle de representación gestionado de WebKit. Esto ayudará a suavizar los gráficos renderizados. También queremos dividir las funciones de dibujo y actualización. Esto actualizará los objetos en nuestra escena de renderizado antes de calcular cuándo se dibujará el siguiente cuadro. El bucle del juego también debe omitir los cuadros de sorteo si la actualización es demasiado larga.

Index.HTML

<html> 
    <head> 
     <!--load scripts--> 
    </head> 
    <!-- 
     render canvas into body, alternative you can use div, but disable 
     right click and hide cursor on parent div 
    --> 
    <body oncontextmenu="return false" style="overflow:hidden;cursor:none;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;"> 
     <script type="text/javascript" charset="utf-8"> 
      Game.initialize(); 
      window.onEachFrame(Game.run); 
     </script> 
    </body> 
</html> 

juego.js

var Game = {}; 

Game.fps = 60; 
Game.maxFrameSkip = 10; 
Game.skipTicks = 1000/Game.fps; 

Game.initialize = function() { 
    this.entities = []; 
    this.viewport = document.body; 

    this.input = new Input(); 

    this.debug = new Debug(); 
    this.debug.initialize(this.viewport); 

    this.screen = new Screen(); 
    this.screen.initialize(this.viewport); 
    this.screen.setWorld(new World()); 
}; 

Game.update = function(tick) { 
    Game.tick = tick; 
    this.input.update(); 
    this.debug.update(); 
    this.screen.update(); 
}; 

Game.draw = function() { 
    this.debug.draw(); 
    this.screen.clear(); 
    this.screen.draw(); 
}; 

Game.pause = function() { 
    this.paused = (this.paused) ? false : true; 
}; 

/* 
* Runs the actual loop inside browser 
*/ 
Game.run = (function() { 
    var loops = 0; 
    var nextGameTick = (new Date).getTime(); 
    var startTime = (new Date).getTime(); 
    return function() { 
     loops = 0; 
     while (!Game.paused && (new Date).getTime() > nextGameTick && loops < Game.maxFrameSkip) { 
      Game.update(nextGameTick - startTime); 
      nextGameTick += Game.skipTicks; 
      loops++; 
     } 
     Game.draw(); 
    }; 
})(); 

(function() { 
    var onEachFrame; 
    if (window.requestAnimationFrame) { 
     onEachFrame = function(cb) { 
      var _cb = function() { 
       cb(); 
      requestAnimationFrame(_cb); 
      }; 
      _cb(); 
     }; 
    } else if (window.webkitRequestAnimationFrame) { 
     onEachFrame = function(cb) { 
      var _cb = function() { 
      cb(); 
      webkitRequestAnimationFrame(_cb); 
      }; 
      _cb(); 
     }; 
    } else if (window.mozRequestAnimationFrame) { 
     onEachFrame = function(cb) { 
      var _cb = function() { 
       cb(); 
       mozRequestAnimationFrame(_cb); 
      }; 
      _cb(); 
     }; 
    } else { 
     onEachFrame = function(cb) { 
      setInterval(cb, Game.skipTicks); 
     }; 
    } 

    window.onEachFrame = onEachFrame; 
})(); 

aún más información

puede encontrar un ejemplo de trabajo completo, y todo el código aquí. He convertido esta respuesta en un marco descargable de JavaScript desde donde puedes construir tus juegos.

https://code.google.com/p/twod-js/

+2

-1 Este es un lío poco estructurado y mal expresado de una respuesta con innumerables errores ortográficos y gramaticales. Lea [Cómo responder a procedimientos] (http://stackoverflow.com/help/how-to-answer). – Joe

+0

arreglé la gramática y agregué un mejor código de ejemplo. –

+1

Su respuesta es la más correcta y actualizada en la página, pero ahora tiene demasiado código de ejemplo. Los futuros lectores no se preocuparán por el diseño HTML, world.js, player.js, camera.js o tile.js. Por favor, solo incluya los elementos de su respuesta relevantes para manejar el tiempo para un bucle de juego usando requestAnimationFrame. –

10

requestAnimationFrame es una gran alternativa disponible en la mayoría de los navegadores de hoy. Hace lo que estás haciendo con setInterval, pero de una manera cocida. Probablemente lo mejor de todo es que solo se ejecuta mientras su pestaña está enfocada. Esa podría ser una razón no para usarla si desea que las cosas se ejecuten en segundo plano, pero a menudo (especialmente para los bucles de renderizado que solo importan cuando se ve) es genial no usar recursos cuando la pestaña no está activa.

El uso es bastante simple:

function gameLoop(){ 
    window.requestAnimationFrame(gameLoop); 
    Game.update(); 
} 

Odio escribir en hilos de edad, pero esto es un resultado superior para Google "bucle Javascript juego" por lo que realmente necesita incluir requestAnimationFrame.

Pollyfill para navegadores antiguos copiados de paulirish:

(function() { 
    var lastTime = 0; 
    var vendors = ['ms', 'moz', 'webkit', 'o']; 
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 
     window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; 
     window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] 
            || window[vendors[x]+'CancelRequestAnimationFrame']; 
    } 

    if (!window.requestAnimationFrame) 
     window.requestAnimationFrame = function(callback, element) { 
      var currTime = new Date().getTime(); 
      var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 
      var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 
       timeToCall); 
      lastTime = currTime + timeToCall; 
      return id; 
     }; 

    if (!window.cancelAnimationFrame) 
     window.cancelAnimationFrame = function(id) { 
      clearTimeout(id); 
     }; 
}()); 

más profundos consejos explicación y de uso en creativeJS

Cuestiones relacionadas