addEventListener触发时机异常:游戏首次失败后任意点击即重启
问题描述
出于某些原因,addEventListener在应该触发和不应该触发的场景下都会触发。我开发了一款游戏,玩家未失败时正常进行游戏,仅在失败时可点击屏幕重试。浏览器打开或刷新页面时游戏运行一切正常,但首次失败后,任意点击都会导致游戏从头重启,请问我的代码存在什么问题?
问题代码
function main() { setTimeout(function everyFrame() { clearCanvas(); // 游戏未结束时运行逻辑 if (!isGameOver()) { game_over = false; // 游戏画面渲染逻辑 } // 游戏结束时运行逻辑 else if (isGameOver()) { game_over = true; // 失败画面渲染逻辑 // 用户点击失败画面时重置游戏 if (game_over) { game_over = false; myCanvas.addEventListener("click", restartGame); } } main(); changingDir = false; }, 100); }
问题原因
- 核心问题是重复绑定点击事件监听器:你的帧逻辑每100ms就会执行一次,只要游戏判定为结束状态,就会给canvas新增一个
restartGame点击事件绑定,没有做去重处理。首次失败后,每100ms就多一个重启函数绑定到点击事件上,所有绑定的函数会在点击时同时触发。 - 额外的逻辑错误:你在游戏结束分支里刚把
game_over设为true,马上又在判断分支里把它重置为false,相当于每帧都会重置状态,但之前绑定的事件监听器不会自动清除,哪怕游戏重新开始,历史绑定的事件依然生效,所以任意点击都会触发重启。
修复方案
推荐使用「全局单次绑定+状态判断」的方案,逻辑最稳定:
- 把点击事件绑定移到游戏初始化逻辑里,全局只绑定一次
- 在
restartGame函数开头先判断当前是否为游戏结束状态,只有处于结束状态才执行重启逻辑,否则直接返回
修复后代码示例
// 初始化阶段只绑定一次点击事件 myCanvas.addEventListener("click", restartGame); function restartGame() { // 只有游戏结束时点击才生效 if (!isGameOver()) return // 原重启游戏逻辑 // ... } function main() { setTimeout(function everyFrame() { clearCanvas(); // 游戏未结束时运行逻辑 if (!isGameOver()) { game_over = false; // 游戏画面渲染逻辑 } // 游戏结束时运行逻辑 else { game_over = true; // 失败画面渲染逻辑 // 移除原来的事件绑定逻辑 } main(); changingDir = false; }, 100); }
如果你确实需要仅在游戏结束时才绑定事件,那一定要在绑定前移除之前的监听器,或者加绑定标记避免重复绑定:
// 加绑定标记避免重复绑定 let isRestartListenerAdded = false function main() { setTimeout(function everyFrame() { clearCanvas(); if (!isGameOver()) { game_over = false; // 游戏重置时重置绑定标记 isRestartListenerAdded = false // 游戏画面渲染逻辑 } else { game_over = true; // 失败画面渲染逻辑 if (!isRestartListenerAdded) { myCanvas.addEventListener("click", restartGame); isRestartListenerAdded = true } } main(); changingDir = false; }, 100); }
内容的提问来源于stack exchange,提问作者Angel R.
相关产品推荐
相关产品推荐

