2009-12-23 2 views
17

Есть ли простой способ создать игровой цикл в JavaScript? что-то вроде ...Лучший способ для простой игровой петли в Javascript?

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

В событийном мире JavaScript вы, вероятно, не нужно, чтобы сделать это. Почему, по-вашему, вам это нужно? –

+10

@Jon, как вы собираетесь обновлять игру, когда событие не было запущено? Многие игры делают вещи, даже если вы не ... – James

ответ

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

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

Возможно, вы захотите изменить порядок первой строки и остальных. ;) – Amber

+1

Продолжайте, сделайте это 33 мс ... ;-) –

+1

Done;) ... @Dav, почему? – James

4

Угу. Вы хотите setInterval:

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

Будет ли это делать?

setInterval(updateGameState, 1000/25); 

Где 25 - ваш желаемый FPS. Вы также можете установить количество миллисекунд между кадрами, которое при 25 кадрах в секунду будет составлять 40 мс (1000/25 = 40).

+1

Вы очень хотите передать функцию, а не ее результат. –

+1

Вы правы, исправлены. –

+0

не справляется с дрейфом и вызовет латентность. Не использует webkit –

28

Есть разнообразное количество способов достижения этого, используя JavaScript в зависимости от приложения. Оператор setInterval() или даже с while() будет делать трюк. Это не будет работать для игрового цикла. JavaScript интерпретируется браузером, поэтому он подвержен прерываниям. Прерывания сделают игру вашей игры неудобной.

Свойства webkitRequestAnimationFrame для CSS3 направлены на исправление этого путем управления самим циклом рендеринга. Однако это еще не самый эффективный способ сделать это и будет подвержен колебаниям, если у вас много обновляемых объектов.

Это хороший сайт, чтобы начать с:

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

Этот сайт имеет некоторую полезную информацию по основам сделать цикл игры. Он никак не затрагивает какой-либо объектно-ориентированный дизайн. Самый точный способ достижения точных таймингов - использовать функцию даты.

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

Это не учитывает, как setTimeout дрейфует на высоких частотах. Это также приведет к тому, что ситуация не синхронизируется и становится нервной. JavaScript будет дрейфовать +/- 18 мс в секунду.

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); 

Теперь давайте поместим все это в рабочий пример. Мы хотим внедрить наш игровой цикл в управляемый цикл рендеринга WebKit. Это поможет сгладить визуализированную графику. Мы также хотим разделить функции рисования и обновления. Это позволит обновить объекты в нашей области рендеринга перед вычислением при следующем рисовании следующего кадра. Игровой цикл должен также пропускать кадры рисования, если обновление длится долго.

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> 

игры.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; 
})(); 

Даже больше информации

Вы можете найти полный рабочий пример, и весь код здесь. Я преобразовал этот ответ в загружаемую фреймворк javascript, с которой вы можете строить свои игры.

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

+2

-1 Это плохо отформатированный, плохо выраженный беспорядочный беспорядок ответа с бесчисленными орфографическими и грамматическими ошибками. Пожалуйста, прочитайте [Ответ на вопросник] (http://stackoverflow.com/help/how-to-answer). – Joe

+0

Я исправил грамматику и добавил лучший пример кода. –

+1

Ваш ответ является самым правильным и актуальным на странице, но теперь у вас слишком много примеров кода. Будущим читателям это не понравится макет HTML, world.js, player.js, camera.js или tile.js. Пожалуйста, укажите только те элементы вашего ответа, которые относятся к обработке времени для игрового цикла с использованием requestAnimationFrame. –

10

requestAnimationFrame является прекрасной альтернативой доступны в большинстве браузеров в настоящее время. Он делает то, что вы делаете с setInterval, но в испеченном порядке. Вероятно, самое приятное в этом заключается в том, что он работает только во время вашей вкладки. Это может быть причиной не, чтобы использовать его, если вы хотите, чтобы что-то работало в фоновом режиме, но часто (особенно для циклов рендеринга, которые имеют значение только при просмотре), здорово не использовать ресурсы, когда вкладка не активна.

Использование довольно просто:

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

Я ненавижу размещать на старых нитей, но это топ результат Google для «JavaScript игры цикла» так что это действительно необходимо включить requestAnimationFrame.

Pollyfill для старых браузеров, скопированных из 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); 
     }; 
}()); 

более глубокое объяснение и использование советы по creativeJS

Смежные вопросы