JavaScript贪吃蛇游戏:苹果重置后无法碰撞重生问题求助
贪吃蛇游戏碰撞检测异常问题排查
问题现象
首次通过drawApple绘制的苹果,collisionCheck函数可正常检测碰撞;但苹果重置到新位置后,无法再触发碰撞,苹果也不会再次重生。
核心原因分析
- 碰撞检测逻辑错误:当前用
appleX == xc && appleY == yc判断碰撞,要求玩家矩形和苹果矩形的左上角坐标完全一致才能触发碰撞,但玩家位置每次以snakeSpeed=10的步长移动,重置后的苹果坐标是随机生成的整数,几乎不可能刚好和玩家坐标完全匹配,导致后续无法触发碰撞。 - 苹果位置未做边界处理:随机生成的苹果坐标可能超出画布范围,导致苹果部分或完全不可见,进一步加剧“无法重生”的视觉错觉。
- 游戏循环顺序不合理:当前先绘制元素再更新玩家位置,碰撞检测用的是更新前的旧坐标,逻辑不符合“移动后判断碰撞”的常规游戏流程。
修复方案
1. 替换为矩形碰撞检测逻辑
判断两个矩形是否重叠的标准逻辑:两个矩形在x轴和y轴上的投影均有重叠。针对25x25的玩家和苹果,碰撞条件应为:
xc < appleX + 25 && xc + 25 > appleX && yc < appleY +25 && yc +25 > appleY
2. 优化苹果重置逻辑
生成的苹果坐标需确保整个苹果矩形在画布内,即appleX范围为0到drawSection.width -25,appleY同理;可选让坐标对齐玩家移动步长,避免苹果出现在玩家无法到达的位置。
3. 修复按键冗余代码
按键处理中spdX = spdX = 0属于冗余赋值,改为直接spdX = 0即可;同时调整按键逻辑为直接赋值速度,避免多次按键叠加速度异常。
4. 调整游戏循环顺序
先更新玩家位置,再绘制元素、检测碰撞,逻辑更符合游戏流程。
修复后的完整代码
"use strict"; let canvas = document.getElementsByTagName("canvas"); for (let drawSection of canvas) { // 给canvas设置默认宽高,避免默认尺寸过小 drawSection.width = drawSection.width || 400; drawSection.height = drawSection.height || 400; let gamefield = drawSection.getContext("2d"); let speed = 7; let snakeSpeed = 10; let gamerX = drawSection.width / 2; let gamerY = drawSection.height / 2; let spdX = 0; let spdY = 0; let appleX = 20; let appleY = 20; function game() { // 限制速度最大值,简化代码 spdY = Math.max(-snakeSpeed, Math.min(snakeSpeed, spdY)); spdX = Math.max(-snakeSpeed, Math.min(snakeSpeed, spdX)); // 先更新玩家位置 gamerX += spdX; gamerY += spdY; // 绘制游戏元素 screenColor(); drawPlayer(); drawApple(); // 检测碰撞 collisionCheck(gamerX, gamerY); setTimeout(game, 1000 / speed); } document.addEventListener("keydown", keydown); function screenColor() { gamefield.fillStyle = "black"; gamefield.fillRect(0, 0, drawSection.width, drawSection.height); } function drawPlayer() { gamefield.fillStyle = "purple"; gamefield.fillRect(gamerX, gamerY, 25, 25); } function drawApple() { gamefield.fillStyle = "red"; gamefield.fillRect(appleX, appleY, 25, 25); } function collisionCheck(xc, yc) { // 矩形碰撞检测逻辑 if (xc < appleX + 25 && xc + 25 > appleX && yc < appleY + 25 && yc + 25 > appleY) { // 生成在画布内的苹果坐标,确保完整显示 appleX = Math.floor(Math.random() * (drawSection.width - 25)); appleY = Math.floor(Math.random() * (drawSection.height - 25)); // 可选:让苹果坐标对齐步长,和玩家移动节奏匹配 // appleX = Math.floor(Math.random() * ((drawSection.width -25)/snakeSpeed)) * snakeSpeed; // appleY = Math.floor(Math.random() * ((drawSection.height -25)/snakeSpeed)) * snakeSpeed; console.log(appleX, appleY); } } function keydown(event) { if (event.keyCode == 38) { spdY = -snakeSpeed; spdX = 0; } if (event.keyCode == 40) { spdY = snakeSpeed; spdX = 0; } if (event.keyCode == 37) { spdX = -snakeSpeed; spdY = 0; } if (event.keyCode == 39) { spdX = snakeSpeed; spdY = 0; } } game(); }
<canvas></canvas>
额外说明
- 给canvas添加了默认宽高,避免使用浏览器默认的小尺寸画布;
- 简化了速度限制代码,用
Math.max和Math.min替代多个if判断,逻辑更简洁; - 按键处理改为直接赋值速度,避免多次按键叠加导致速度异常。
内容的提问来源于stack exchange,提问作者Marco1314
相关产品推荐
相关产品推荐

