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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:25:39