Подтвердить что ты не робот

Лучший способ для простой игровой петли в Javascript?

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

onTimerTick() {
  // update game state
}
4b9b3361

Ответ 1

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

function onTimerTick() {
    // Do stuff.
}

Ответ 2

Существует множество способов достижения этого с помощью 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);

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

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>

Game.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/

Ответ 3

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

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

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

Мне не нравится публиковать сообщения в старых потоках, но это отличный результат Google для "javacript game loop", поэтому действительно нужно включить 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

Ответ 4

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

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

Ответ 5

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

setInterval(updateGameState, 1000 / 25);

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

Ответ 7

Многие из этих ответов устарели и неоптимальны. Рекомендуемый способ сделать это сейчас - использовать requestAnimationFrame.

Например:

let previousTime = 0.0;

const loop = time => {
  // Compute the delta-time against the previous time
  const dt = time - previousTime;

  // Update the previous time
  previousTime = time;

  // Update your game
  update(dt);

  // Render your game
  render();

  // Repeat
  window.requestAnimationFrame(loop);
};

// Launch
window.requestAnimationFrame(time => {
  previousTime = time;
  loop(time);
});