Каков наилучший способ отслеживать время / кадры в игровом цикле requestAnimationFrame? - PullRequest
0 голосов
/ 30 октября 2019

Я написал очень простые игровые руководства, в которых используется простой игровой цикл requestAnimationFrame. Им не нужно отслеживать прошедшее время или частоту кадров:

var canvas = document.querySelector("#canvas");
var ctx = canvas.getContext("2d");

function gameLoop() {
  ctx.clearRect(0,0,canvas.width,canvas.height);
  //Drawing, etc.
  var myRAF = requestAnimationFrame(gameLoop);
}
gameLoop();

Теперь я хочу научиться анимировать такие вещи, как циклы ходьбы, из таблицы спрайтов, а не только анимировать движение статического объекта. Я считаю, что для этого необходимо сначала изучить, как отслеживать, сколько времени понадобилось для рендеринга фрейма или на каком фрейме вы находитесь. У меня сложилось впечатление, что в этом нет необходимости, если вы используете RAF вместо setInterval или setTimeout (упс!). Я видел, как люди используют объект Date, метку времени requestAnimationFrame и performance.now, хотя я пока не понимаю код. Каков наилучший выбор для разработки игр с requestAnimationFrame, если мои цели состоят в том, чтобы анимировать из спрайтов и обеспечить, чтобы движение в игре было с одинаковой скоростью, независимо от того, сколько кадров в секунду набирает тот или иной игрок? Я читал, что вам нужно умножить все скорости в игре на фактор времени, но не знаю как. В противном случае медленный компьютер, который получает только 30 кадров в секунду, проходит через игру и стреляет пулями с половинной скоростью по сравнению с быстрыми машинами, получающими скорость около 60 кадров в секунду, верно?

Пожалуйста, покажите мне, как бы я реализовал код отслеживания времени / кадра вигровой цикл, в дополнение к плюсам и минусам различных способов достижения этой цели.

С риском звучать так, будто я хочу обучающую программу, пожалуйста, проигнорируйте следующую часть из исходного вопроса

1 Ответ

0 голосов
/ 30 октября 2019

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

Обычно вы перемещаете вещи на основеDeltaTime. Если ваш deltaTime находится в секундах, то легко сделать что-либо на основе дельта-в-секунду. Например, чтобы перемещать 10 единиц в секунду каждый кадр, вы делаете что-то вроде

const unitsPerSecond = 10;
x = x + unitsPerSecond * deltaTimeInSeconds

Что касается подсчета кадров, вы просто держите свой собственный счетчик

const ctx = document.querySelector('canvas').getContext('2d');

let x = 0;
let y = 0;
const speed = 120;  // 120 units per second

let frameNumber = 0;
let previousTime = 0;
function render(currentTime) {
  // keep track of frames
  ++frameNumber;
  
  // convert time to seconds
  currentTime *= 0.001;
  
  // compute how much time passed since the last frame
  const deltaTime = currentTime - previousTime;
  
  // remember the current time for next frame
  previousTime = currentTime;
  
  // move some object frame rate independently
  x += speed * deltaTime;
  y += speed * deltaTime;
  
  // keep x and y on screen
  x = x % ctx.canvas.width;
  y = y % ctx.canvas.height;
  
  // clear the canvas
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  
  // draw something
  ctx.fillStyle = (frameNumber & 0x1) ? 'red' : 'blue'; // change color based on frame
  ctx.fillRect(x - 5, y - 5, 11, 11);
  
  // draw something else based on time
  ctx.fillStyle = 'green';
  ctx.fillRect(
     145 + Math.cos(currentTime) * 50, 
     75 + Math.sin(currentTime) * 50,
     10, 10);
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<canvas></canvas>
...