如何正确重置并重启基于JS和HTML Canvas构建的游戏动画循环?
重置Canvas游戏时的deltaTime计算异常问题
我开发了一款基于CanvasRenderingContext2D接口的简易2D小游戏,Game类里有个reset方法,在游戏结束后用户按下'r'键触发。目前确认这个方法能正确重置游戏对象,但重新调用动画函数时,deltaTime变量计算出现异常:首次运行时deltaTime约为16/17,从reset方法重启动画后,deltaTime等于甚至大于gameTime变量。
请问如何在重置游戏时处理时间差问题?
原动画函数代码:
function animate(timeStamp) { const deltaTime = timeStamp - lastTime lastTime = timeStamp ctx.clearRect(0, 0, canvas.width, canvas.height) game.draw(ctx) game.update(deltaTime) if (game.gameOver) game.draw(ctx) // draw gameover text on top if (!game.gameOver) requestAnimationFrame(animate) }
我曾尝试给动画函数加restart参数区分首次启动和重置启动,还试过把timeStamp或lastTime改为gameTime、0或new Date()获取的新时间,但都无效:
function animate(timeStamp, restart) { const deltaTime = timeStamp - lastTime if (restart) timeStamp = 0 lastTime = timeStamp ctx.clearRect(0, 0, canvas.width, canvas.height) game.draw(ctx) game.update(deltaTime) if (game.gameOver) game.draw(ctx) // draw gameover text on top if (!game.gameOver) requestAnimationFrame(animate) }
解决方法
核心问题是重置游戏时,lastTime变量没有同步更新:重启动画时,timeStamp是当前浏览器时间戳,而lastTime还是游戏结束时的旧值,两者差值会等于游戏暂停的时长,导致deltaTime异常偏大。
方案一:在reset方法中同步更新lastTime
触发重置时,除了重置游戏对象,直接将lastTime设为当前时间(用performance.now()和requestAnimationFrame的timeStamp保持一致):
game.reset = function() { // 重置游戏状态、对象等逻辑 this.gameOver = false; // 同步更新lastTime lastTime = performance.now(); // 启动动画循环 requestAnimationFrame(animate); };
方案二:在动画函数内处理异常时间差
不需要额外参数,只需在每一帧判断deltaTime是否超出合理范围(比如大于100ms,正常60帧间隔约16ms),如果是则强制修正:
function animate(timeStamp) { let deltaTime = timeStamp - lastTime; // 修正重启后的超大时间差 if (deltaTime > 100) { deltaTime = 16; // 设为正常帧间隔 lastTime = timeStamp - deltaTime; } lastTime = timeStamp; ctx.clearRect(0, 0, canvas.width, canvas.height); game.draw(ctx); game.update(deltaTime); if (game.gameOver) game.draw(ctx); if (!game.gameOver) requestAnimationFrame(animate); }
两种方案都能让重置后的deltaTime保持在合理范围,避免异常值影响游戏逻辑。
内容的提问来源于stack exchange,提问作者Josh Köter
相关产品推荐
相关产品推荐

