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

Canvas圆形玩家无法跟随鼠标移动问题求助

问题排查与修复:Canvas圆形玩家无法跟随鼠标移动

核心问题分析

  • 游戏循环逻辑错误:gameLoop函数中,每次执行都调用requestAnimationFrame(draw)而非requestAnimationFrame(gameLoop),导致循环无法持续迭代,玩家移动逻辑仅能执行有限次数;同时每次循环都调用setInterval(createRandomCircle, 3000),会不断创建重复定时器,导致食物疯狂生成。
  • 错误调用鼠标事件处理函数:在draw函数中直接调用mousemoveHandler(),但该函数需要鼠标事件对象作为参数,直接调用会导致e为undefined,触发报错并终止后续代码执行(包括moveTowardsMouse)。
  • 除零风险:当玩家与鼠标位置重合时,distance为0,dx/distance和dy/distance会得到NaN,导致玩家坐标变为无效值,无法继续移动。

修复后的完整代码

let cnv = document.getElementById("myCanvas");
let ctx = cnv.getContext("2d");
cnv.width = 800;
cnv.height = 600;
let mouse = { x: 0, y: 0 };
let food = [];

let player = {
  x: cnv.width / 2,
  y: cnv.height / 2,
  r: 30,
  color: "purple",
  speed: 5,
};

function createRandomCircle() {
  let circle = {
    x: Math.random() * cnv.width,
    y: Math.random() * cnv.height,
    r: Math.random() * 5 + 8,
    color: randomRGB(),
  };
  food.push(circle);
}

function draw() {
  ctx.clearRect(0, 0, cnv.width, cnv.height);

  // 绘制食物
  for (let i = 0; i < food.length; i++) {
    const circles = food[i];
    ctx.fillStyle = circles.color;
    ctx.beginPath();
    ctx.arc(circles.x, circles.y, circles.r, 0, Math.PI * 2);
    ctx.fill();
    ctx.closePath();
  }

  // 绘制玩家
  ctx.fillStyle = player.color;
  ctx.beginPath();
  ctx.arc(player.x, player.y, player.r, 0, Math.PI * 2);
  ctx.fill();
  ctx.closePath();

  moveTowardsMouse(mouse.x, mouse.y);
  collision();
}

function moveTowardsMouse(px, py) {
  let dx = px - player.x;
  let dy = py - player.y;
  let distance = Math.sqrt(dx ** 2 + dy ** 2);
  
  // 避免距离为0时的除零错误
  if (distance < player.speed) {
    player.x = px;
    player.y = py;
    return;
  }
  
  player.x += (dx / distance) * player.speed;
  player.y += (dy / distance) * player.speed;
}

function collision() {
  // 倒序遍历避免splice导致的索引错乱
  for (let i = food.length - 1; i >= 0; i--) {
    const circles = food[i];
    // 改用更准确的圆形碰撞检测公式
    let dx = player.x - circles.x;
    let dy = player.y - circles.y;
    let distance = Math.sqrt(dx ** 2 + dy ** 2);
    
    if (distance < player.r + circles.r) {
      food.splice(i, 1);
      player.r += circles.r * 0.125;
      createRandomCircle();
    }
  }
}

document.addEventListener("mousemove", mousemoveHandler);

function mousemoveHandler(e) {
  let cnvRect = cnv.getBoundingClientRect();
  mouse.x = Math.round(e.clientX - cnvRect.left);
  mouse.y = Math.round(e.clientY - cnvRect.top);
}

// 初始化生成食物
for (let i = 0; i < 10; i++) {
  createRandomCircle();
}

// 随机工具函数
function randomDec(low, high) {
  return Math.random() * (high - low) + low;
}

function randomInt(low, high) {
  return Math.floor(randomDec(low, high));
}

function randomRGB() {
  return `rgb(${randomInt(0, 256)},${randomInt(0, 256)},${randomInt(0, 256)})`;
}

// 修复游戏循环
function gameLoop() {
  draw();
  requestAnimationFrame(gameLoop);
}
gameLoop();

// 单独设置食物生成定时器,只执行一次
setInterval(createRandomCircle, 3000);

额外优化说明

  • 修复了createRandomCircle中未声明circle变量导致的全局污染问题。
  • 碰撞检测改用更准确的圆形距离公式,替代原有的轴对齐包围盒检测,避免误判。
  • 碰撞遍历改为倒序,避免splice删除元素后导致的索引跳过问题。

内容的提问来源于stack exchange,提问作者ardood

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 09:44:51