
本文详解如何修复 html 赛车游戏中“每5秒增速+1(起始为5)”和“仅当玩家车尾完全超越敌车车尾时加10分”两大核心逻辑,解决重启异常、误判得分、速度失控等常见 bug。
在开发基于 HTML/CSS/JavaScript 的横向卷轴赛车游戏时,速度动态增长与精准得分判定是提升游戏挑战性与公平性的关键。原始代码存在多个严重逻辑缺陷:player.speed 在 initializeGame() 中未重置为初始值 5;player.score++ 被错误地置于 runGame() 主循环末尾,导致帧率依赖型无效累加;更关键的是,得分条件完全缺失——未通过 DOM 元素边界比对实现“车尾超越”判断,而是用无意义的全局自增替代。
✅ 正确实现速度递增(每5秒 +1,起始为5)
需引入时间戳追踪机制,避免使用 setInterval(易因帧率波动或多次初始化导致计时错乱)。在 runGame() 中记录上一次增速时间,并在每次执行时检查是否已过 5000ms:
let lastSpeedIncreaseTime = 0;
function updateSpeed() {
const now = Date.now();
if (now - lastSpeedIncreaseTime >= 5000) {
player.speed++;
lastSpeedIncreaseTime = now;
}
}并在 runGame() 的主逻辑中调用:
if (player.start) {
updateSpeed(); // ← 新增:精准控制增速节奏
moveLines();
moveEnemy(car);
// ...其余移动逻辑
}同时,必须在 initializeGame() 中显式重置所有状态:
立即学习“前端免费学习笔记(深入)”;
function initializeGame() {
startScreen.classList.add('hide');
gameArea.innerHTML = "";
player.start = true;
player.score = 0;
player.speed = 5; // ✅ 强制重置起始速度
player.x = 0;
player.y = 0;
lastSpeedIncreaseTime = Date.now(); // ✅ 重置计时起点
// ...创建道路线、玩家车、敌车等
}✅ 精准实现“车尾超越”得分逻辑(+10 分/次)
原始代码中 player.score++ 是纯计数器,与游戏物理完全脱钩。正确做法是:每一帧检测每个敌车是否满足 myCar.bottom ≤ enemyCar.bottom → myCar 已完全越过敌车,且该敌车此前未被计分。
为此需为每个敌车元素添加自定义属性标记(如 data-scored="false"),并在 moveEnemy() 中进行判定:
function moveEnemy(myCar) {
const myRect = myCar.getBoundingClientRect();
const myCarBottom = myRect.bottom;
document.querySelectorAll('.enemyCar').forEach(enemyCar => {
const enemyRect = enemyCar.getBoundingClientRect();
const enemyCarBottom = enemyRect.bottom;
// ✅ 核心条件:玩家车尾 ≤ 敌车车尾 → 完全超越
if (myCarBottom <= enemyCarBottom && !enemyCar.dataset.scored) {
player.score += 10;
enemyCar.dataset.scored = "true"; // 防止重复计分
score.innerText = `Score: ${player.score}\nSpeed: ${player.speed}`;
}
// 敌车移出屏幕后重置(并清除计分标记)
if (enemyRect.top > window.innerHeight) {
enemyCar.style.top = "-100px";
enemyCar.style.left = Math.floor(Math.random() * 350) + "px";
enemyCar.dataset.scored = "false"; // 重置可计分状态
}
// 更新敌车位置(按当前 player.speed)
enemyCar.style.top = (parseFloat(enemyCar.style.top) || 0) + player.speed + "px";
});
}⚠️ 注意:getBoundingClientRect() 返回的是视口坐标,确保 无意外滚动偏移;若游戏区域有 transform 或 position: fixed,需统一使用 offsetTop + clientHeight 计算相对位置。
? 其他关键修复点
- 移除冗余 player.score++:原始代码中该行会导致每帧强制 +1,彻底删除;
- 修正 requestAnimationFrame 调用位置:应在 runGame() 末尾递归调用,而非在 initializeGame() 中单次调用(否则重启后动画停止);
- 防止多次初始化冲突:为 startScreen.addEventListener 添加 .once('click') 或在 initializeGame() 开头检查 player.start 状态;
- 优化性能:querySelectorAll 和 getBoundingClientRect() 开销较大,可缓存元素引用或使用 offsetTop/offsetHeight 替代(若布局固定)。
✅ 最终效果验证清单
| 问题 | 是否修复 | 验证方式 |
|---|---|---|
| 起始速度恒为 5 | ✅ | 重启后 console.log(player.speed) 输出 5 |
| 每 5 秒准确 +1 | ✅ | 启动后等待 5s,观察 speed 变为 6;再等 5s 变为 7 |
| 得分仅触发于“车尾超越” | ✅ | 敌车从下方接近时,仅当黄色车完全盖过绿色车底部才 +10 |
| 重启不累积计时器 | ✅ | 连续重启 5 次,每次均从 5 开始,5s 后变 6 |
| 无重复/漏计分 | ✅ | 敌车反复刷出,每次仅首次超越计分 |
通过以上结构化重构,游戏逻辑从“视觉动画演示”升级为具备严谨物理判定与可控成长曲线的完整小游戏。核心在于:用时间戳替代 setInterval 实现稳定节奏,用 DOM 边界比对替代臆测条件实现精准事件触发——这是前端游戏开发中复用性与健壮性的基石实践。











