将状态变化与绘图分开
使用 requestAnimationFrame 请求下一次画面更新,并把它提供的时间戳传给一个简洁的循环协调器。根据经过的时间更新位置、计时器和规则,然后绘制当前状态。渲染器不应负责判断是否发生碰撞,碰撞检测也不应取决于精灵是否恰好被绘制出来。
做一个简单的平台移动测试:让标记以选定的世界单位/秒水平移动,分别在 60 Hz 显示器和高刷新率显示器上运行同一路径。如果在相同的实际时间内,标记在某个显示器上移动得更远,说明仍有部分移动量是按每次回调计算的,而非按时间计算。
选择延迟帧的恢复策略
长时间暂停会产生很大的时间间隔。对于轻量的实时游戏,应限制这段时间,避免玩家回来时看到角色瞬间穿过多个障碍。对于确定性模拟,应使用带累加器的固定更新步长,限制追赶更新的次数;如果插值有帮助,可以在模拟状态之间绘制画面。
在可重复的障碍路线中记录最长的回调间隔,然后切换到其他页面再返回,检查游戏状态。明确决定游戏应暂停、少量追赶时间,还是从上一个状态继续;不要让一次延迟的回调替你做决定。
关于浏览器游戏循环的常见问题
我应该使用 setInterval 进行绘图吗?
使用 requestAnimationFrame 进行视觉工作,因为它安排在重绘附近,并且通常会因隐藏文档而暂停。计时器仍然可以安排非视觉任务,但不应将其视为显示时钟。
帧时间戳从何而来?
requestAnimationFrame 回调提供高分辨率时间戳。 MDN 记录了其时间安排和一次性行为;使用该时间戳来计算进度而不是计算回调。
延伸阅读
发现信息过时了? 报告需要更正的内容