简体   繁体   中英

Create a parabolic trajectory with fixed angle

I'm trying to throw an arrow in my XNA game, but I'm having a hard time trying to realize a good parabola.

What I need:

  • The more you hold Enter stronger the arrow goes.
  • The arrow angle will be always the same, 45 degrees .

This is what I have already have:

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;
    }
}

My question is, what do I need to do to make the arrow to go bottom as it loses velocity ( timeHeld ) to make a perfect parabola?

Note: I never heard of XNA until now, but I do use C#. Let me know if this doesn't quite work, though the gist of it should be there.

In your last if-statement, after Enter key is released, you want to increase the downward velocity by a certain (small constant) amount every time you call Update (I assume increasing the y-coordinate makes things move "down" on screen). To do this, as soon as Enter is released, instead of calling double vy = timeHeld * Math.Sin(alpha) every time, save the result into a variable you can reference later, then use a bool value to keep track of when to calculate that value, which is ONLY right after Enter is released.

In other words, it goes something like this:

// 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;
    }
}

Hopefully this works, though there might be more efficient ways to do this. Though this isn't a physics site, see this for reference ;-)

The solutions discussed above rely on you calculating a new velocity on every iteration, and then calculating the delta (change) from the previous position to determine the current position.

This fits in with the normal logic of a game loop. It is however computationally more complex than it needs to be, and is possibly unstable due to rounding errors.

The simpler and more stable solution is to determine the equation for the parabola and use this to directly work out the position at time t after launch.

Let the arrow start at x=0, y=0. Let the launch velocity be v. At time t after launch the a coordinate of the arrow is x = kt, where k = v*cos(45) = v/sqrt(2).

The y position is a quadratic, y = at^2 + bt + c where we don't know a, b, c.

But when t=0, y=0 so c=0

When t=0, the initial downward velocity is v*sin(45) = v/sqrt(2)

Using a tiny bit of calculus (differentiating position to get velocity), at t=0

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

Differentiating velocity to get acceleration, we get the initial acceleration at t=0 is 2a. But the only acceleration is due to gravity, so 2a=-g where g is your gravitational constant.

Putting these two equations together, at time t

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

You know v and t, you define g, so you can work out the x and y co-ordinates at time t directly from this equation.

This is a more straightforward approach and more robust (rounding errors will not accumulate). It is how I personally do it. My hand grenades always follow perfect parabolic arcs, and do so in a computationally efficient manner.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM