You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript贪吃蛇游戏:苹果重置后无法碰撞重生问题求助

贪吃蛇游戏碰撞检测异常问题排查

问题现象

首次通过drawApple绘制的苹果,collisionCheck函数可正常检测碰撞;但苹果重置到新位置后,无法再触发碰撞,苹果也不会再次重生。

核心原因分析

  1. 碰撞检测逻辑错误:当前用appleX == xc && appleY == yc判断碰撞,要求玩家矩形和苹果矩形的左上角坐标完全一致才能触发碰撞,但玩家位置每次以snakeSpeed=10的步长移动,重置后的苹果坐标是随机生成的整数,几乎不可能刚好和玩家坐标完全匹配,导致后续无法触发碰撞。
  2. 苹果位置未做边界处理:随机生成的苹果坐标可能超出画布范围,导致苹果部分或完全不可见,进一步加剧“无法重生”的视觉错觉。
  3. 游戏循环顺序不合理:当前先绘制元素再更新玩家位置,碰撞检测用的是更新前的旧坐标,逻辑不符合“移动后判断碰撞”的常规游戏流程。

修复方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 22:10:34