0

我试图在我的 XNA 游戏中投掷箭,但我很难尝试实现一条好的抛物线。

我需要的:

  • 你握得越Enter强,箭头越走。
  • 箭头角度将始终相同,为45 度

这是我已经拥有的:

private float velocityHeld = 1f;
protected override void Update(GameTime gameTime)
{

    if (Keyboard.GetState().IsKeyDown(Keys.Enter) && !released)
    {
        timeHeld += velocityHeld;
        holding = true;
    }
    else
    {
        if (holding)
        {
            released = true;
            holding = false;
            lastTimeHeld = timeHeld;
        }
    }


    if (released && timeHeld > 0)
    {
        float alpha = MathHelper.ToRadians(45f);
        double vy = timeHeld * Math.Sin(alpha);
        double vx = timeHeld * Math.Cos(alpha);

        ShadowPosition.Y -= (int)vy;
        ShadowPosition.X += (int)vx;
        timeHeld -= velocityHeld;
    }
    else
    {
        released = false;
    }
}

我的问题是,我需要做些什么才能使箭头在失去速度(timeHeld)以形成完美抛物线时落到底部?

4

2 回答 2

1

上面讨论的解决方案依赖于您在每次迭代中计算新的速度,然后计算与先前位置的增量(变化)以确定当前位置。

这符合游戏循环的正常逻辑。然而,它在计算上比它需要的更复杂,并且可能由于舍入误差而不稳定。

更简单和更稳定的解决方案是确定抛物线方程,并用它直接计算发射后 t 时刻的位置。

让箭头从 x=0,y=0 开始。设发射速度为 v。在发射后的时间 t,箭头的 a 坐标为 x = kt,其中 k = v*cos(45) = v/sqrt(2)。

y 位置是二次方的,y = at^2 + bt + c,其中我们不知道 a、b、c。

但是当 t=0, y=0 所以 c=0

当 t=0 时,初始向下速度为 v*sin(45) = v/sqrt(2)

使用一点点微积分(微分位置以获得速度),在 t=0

v/sqrt(2) = 2at + b = b

将速度微分得到加速度,我们得到t=0时的初始加速度为2a。但唯一的加速度是由于重力,所以 2a=-g 其中 g 是你的重力常数。

将这两个方程放在一起,在时间 t

x(t) = v/sqrt(2); y(t) = -(g/2)t^2 + vt/sqrt(2)

你知道 v 和 t,你定义了 g,所以你可以直接从这个方程计算出时间 t 的 x 和 y 坐标。

这是一种更直接且更稳健的方法(舍入误差不会累积)。这是我个人的做法。我的手榴弹总是遵循完美的抛物线弧线,并且以计算效率高的方式进行。

于 2013-06-29T06:02:55.950 回答
0

注意:直到现在我才听说过 XNA,但我确实使用 C#。让我知道这是否不起作用,尽管它的要点应该在那里。

在您的最后一个 if 语句中,在 Enter 键被释放后,您希望每次调用 Update 时将向下速度增加一定的(小常数)量(我假设增加 y 坐标会使事物在屏幕上“向下”移动) . 为此,只要 Enter 被释放,而不是double vy = timeHeld * Math.Sin(alpha)每次都调用,而是将结果保存到稍后可以引用的变量中,然后使用 bool 值来跟踪何时计算该值,这仅在 Enter 之后释放。

换句话说,它是这样的:

// extra variables
bool justReleased = false;
double vy, vx;
...
protected override void Update(GameTime gameTime)
{
    // ...
        if (holding)
        {
            released = true;
            holding = false;
            lastTimeHeld = timeHeld;
            justReleased = true; // add this here
        }
    // ...

    if (released && timeHeld > 0)
    {
        float alpha = MathHelper.ToRadians(45f);

        // not local variables anymore. Also I flipped the sign - my intention is that initial vertical velocity is "upwards"
        if(justReleased)
        {
            vy = -1 * timeHeld * Math.Sin(alpha); 
            vx = timeHeld * Math.Cos(alpha);
            justReleased = false;
        }

        ShadowPosition.Y += (int)vy; // Note: I flipped this operator
        ShadowPosition.X += (int)vx;
        timeHeld -= velocityHeld;

        // increase the downward velocity
        vy += 2; // or some constant. I can't test this. :\
    }
    else
    {
        released = false;
    }
}

希望这可行,尽管可能有更有效的方法来做到这一点。尽管这不是物理站点,但请参阅站点以供参考;-)

于 2013-06-26T21:09:42.433 回答