JavaScript弓箭射击游戏问题:等待射击越久箭头初始位置越远
问题根源分析与修复方案
你遇到的问题核心在于箭头的位置计算逻辑错误,以及可能存在的蓄力变量未正确处理的问题,具体拆解如下:
1. 变量顺序错误导致绘制位置异常
在你的DrawArrows函数中,你先调用了ctx.translate(x, y),但x和y变量是在绘制之后才定义的:
ctx.translate( x, y ); // x和y此时未定义,会使用全局变量或undefined // ...绘制代码 var x = arrows[i].x + vel * Math.cos(-(arrows[i].angle)) * arrows[i].timer; var y = arrows[i].y + bow.height/2 - vel * Math.sin(-(arrows[i].angle)) * arrows[i].timer + acel / 2 * arrows[i].timer * arrows[i].timer;
这会导致箭头的初始绘制位置完全不符合预期,甚至可能引用了全局的鼠标坐标变量,造成“等待越久位置越远”的错觉。
2. 位移计算方式错误:使用总时间而非帧增量
你当前用arrows[i].timer(总帧数)直接计算总位移,并且没有将计算后的x/y赋值回箭头对象:
var x = arrows[i].x + vel * Math.cos(-(arrows[i].angle)) * arrows[i].timer; // 没有执行 arrows[i].x = x;
如果timer的初始值不是0(比如你原本想用timeload记录蓄力时间,但不小心把它赋值给了timer),箭头一创建就会直接跳到总时间对应的位置,表现为“等待越久位置越远”。
修复方案
第一步:修正DrawArrows函数的逻辑
调整变量顺序,改用帧增量计算位移,并正确更新箭头的位置:
function DrawArrows() { const deltaTime = 1; // 假设每帧为1单位时间,可根据实际帧率调整 for (let i = arrows.length - 1; i >= 0; i--) { // 倒序循环避免splice删除元素时索引混乱 const arrow = arrows[i]; arrow.timer += deltaTime; // 计算当前帧的位移增量 const moveX = arrow.vel * Math.cos(-arrow.angle) * deltaTime; // 包含重力加速度的y方向位移 const moveY = (-arrow.vel * Math.sin(-arrow.angle) + acel * arrow.timer) * deltaTime; // 更新箭头的实际位置 arrow.x += moveX; arrow.y += moveY; // 绘制箭头(修正锚点,确保箭头从弓箭位置出发) ctx.save(); ctx.translate(arrow.x, arrow.y + arrow.imagem.height / 2); ctx.rotate(arrow.angle); ctx.translate(0, -arrow.imagem.height / 2); ctx.drawImage(arrow.imagem, 0, 0); ctx.restore(); // 移除超出画布的箭头 if (arrow.x > larg || arrow.y > alt) { arrows.splice(i, 1); } } }
第二步:正确处理蓄力逻辑(如果需要)
如果你原本想实现“按住越久,箭头速度越快”的蓄力效果,需要用timeload控制初始速度,而非直接赋值给timer:
var timeload = 0; function Play() { if (mouseIsDown) { shoot = true; timeload++; // 按住时累积蓄力时间 } else if (mouseIsUp && shoot) { SpawnArrow(timeload); shoot = false; timeload = 0; // 重置蓄力时间 } DrawArrows(); } function SpawnArrow(chargeTime) { // 根据蓄力时间计算初始速度,限制最大速度避免失控 const initialVel = Math.min(10 + chargeTime * 0.1, 30); arrows.push({ x: xBow, y: yBow, imagem: arrow, angle: angleBow, timer: 0, // 初始时间必须为0,确保箭头从弓箭位置出发 vel: initialVel // 保存初始速度 }); }
第三步:修复其他细节问题
- 原代码中
arrows[i].height应改为arrows[i].imagem.height,否则会出现未定义错误; - 用
let/const替代var,避免全局变量污染; - 倒序循环遍历箭头数组,防止
splice删除元素时跳过后续箭头。
这样修改后,箭头会从弓箭位置正确出发,按住鼠标的时间只会影响箭头的初始速度,而不会让箭头直接出现在远处。
内容的提问来源于stack exchange,提问作者xShadoWalkeR
相关产品推荐
相关产品推荐

