在requestAnimationFrame游戏循环中跟踪时间/帧的最佳方法是什么?

时间:2019-10-30 15:16:58

标签: javascript html animation canvas sprite-sheet

我已经编写了非常简单的游戏教程,其中使用了一个简单的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();

现在,我想学习如何从Spritesheet动画化诸如步行周期之类的东西,而不是仅对静态对象的运动进行动画处理。我认为这需要首先学习如何跟踪渲染一帧所需的时间,或者您在哪个帧上。我的印象是,如果您使用RAF而不是setInterval或setTimeout(糟糕!),则不需要这样做。我见过人们使用Date对象,requestAnimationFrame时间戳和Performance.now,尽管我还不了解代码。如果我的目标是从Spritesheets进行动画处理,并确保无论特定玩家获得多少fps,游戏中的运动速度都是相同的,那么哪个是使用requestAnimationFrame开发游戏的最佳选择?我读过,您必须将游戏中的所有速度乘以时间因子,但不知道如何。否则,只有30 fps的慢速计算机正在浏览游戏,并且与60 fps左右的快速计算机gettine相比,以一半的速度射击子弹,对吧?

请向我展示如何在游戏循环中实现时间/帧跟踪代码,以及实现此目标的不同方法的利弊。

冒着听起来像我想要教程的风险,请忽略原始问题的以下部分

  

也很高兴看到您将如何使用该代码来制作动画,例如从Spritesheet上行走或拍打翅膀,以及如何将移动速度乘以时间因子,以便每个人都能获得相同的游戏体验。

1 个答案:

答案 0 :(得分:0)

requestAnimationFrame进行回调。该回调自页面启动以来经过的时间(以毫秒为单位)。因此,您可以使用该时间从上一个requestAnimationFrame回调的时间中减去该时间,以计算出帧速率,并使用“ deltaTime”通过速度将多个其他值可变。

通常,您基于deltaTime移动事物。如果您的deltaTime以秒为单位,则可以很容易地基于每秒的delta进行任何操作。例如,每帧每秒移动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>