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
相关产品推荐
相关产品推荐

