Programming a smooth change of thrust from current velocity vector to a target vector

后端 未结 7 1683
北海茫月
北海茫月 2021-02-04 16:47

TL;dr: \"I am not sure how to calculate a smooth transition of thrust between one vector and another.\"

I am programming a simple game where an enemy chases after the pl

相关标签:
7条回答
  • 2021-02-04 17:41

    It all comes back to Newton's equations:

    F = m * a
    s = s_o + v * t + a * t^2 / 2
    v = v_o + a * t
    

    In this case F is the force (thrust), a is the acceleration, and m is the mass of the ship. s is the current location, s_o is the original location, v is the velocity, and t is the current time.

    Of course this is along a straight line, so if you want to convert to two or three dimensions you'll have to do some math. F, s, v, and a are all vectors, meaning that their direction is equally important. Technically t is also a vector, but since time generally only goes one direction, we don't have to worry about that.

    2d:
    F^2 = F_x^2 + F_y^2 (use Pythagorean theorem to split force into components)
    F_x = m * a_x
    F_y = m * a_y
    s_x = s_o_x + v_x * t + a_x * t^2 / 2
    s_y = s_o_y + v_y * t + a_y * t^2 / 2
    v_x = v_o_x + a_x * t
    v_y = v_o_y + a_y * t
    
    3d:
    F^2 = F_x^2 + F_y^2 + F_z^2 (surprisingly, this works)
    F_x = m * a_x
    F_y = m * a_y
    F_z = m * a_z
    s_x = s_o_x + v_x * t + a_x * t^2 / 2
    s_y = s_o_y + v_y * t + a_y * t^2 / 2
    s_z = s_o_z + v_z * t + a_z * t^2 / 2
    v_x = v_o_x + a_x * t
    v_y = v_o_y + a_y * t
    v_z = v_o_z + a_z * t
    

    Now to adjust your velocity to the direction of the player, you've got a fixed total force (F) in order to change your current velocity toward the player. In physics things don't happen instantaneously, but your goal should be to minimize the time in which the change happens ('t').

    This gives you an equation in terms of your current location ((s_o_x,s_o_y) or (s_o_x,s_o_y,s_o_z)) and your opponent's current location or your target location ((s_x,s_y) or (s_x,s_y,s_z)), for your target velocity (ignores acceleration).

    v_x = (s_x - s_o_x) / t
    v_y = (s_y - s_o_y) / t
    
    v_x = (s_x - s_o_x) / t
    v_y = (s_y - s_o_y) / t
    v_z = (s_z - z_o_y) / t
    

    We can substitute this for our other equation:

    (s_x - s_o_x) / t = v_o_x + a_x * t
    (s_y - s_o_y) / t = v_o_y + a_y * t
    
    (s_x - s_o_x) / t = v_o_x + a_x * t
    (s_y - s_o_y) / t = v_o_y + a_y * t
    (s_z - z_o_y) / t = v_o_z + a_z * t
    

    We then solve for the acceleration (this is related to the force, which is what we are trying to calculate).

    (s_x - s_o_x) / t^2 - v_o_x / t = a_x
    (s_y - s_o_y) / t^2 - v_o_y / t = a_y
    
    (s_x - s_o_x) / t^2 - v_o_x / t = a_x
    (s_y - s_o_y) / t^2 - v_o_y / t = a_y
    (s_z - z_o_y) / t^2 - v_o_z / t = a_z
    

    Plug this into the force equation:

    F_x = m * (s_x - s_o_x) / t^2 - m * v_o_x / t
    F_y = m * (s_y - s_o_y) / t^2 - m * v_o_y / t
    
    F_x = m * (s_x - s_o_x) / t^2 - m * v_o_x / t
    F_y = m * (s_y - s_o_y) / t^2 - m * v_o_y / t
    F_z = m * (s_z - z_o_y) / t^2 - m * v_o_z / t
    

    Now solve for t:

    t = (-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
    t = (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
    
    t = (-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
    t = (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
    t = (-m * v_o_z +/- sqrt(m^2 * v_o_z^2 - 4 * F_z * m * (s_z - s_o_z))) / 2 / F_z
    

    The times should converge, so the times will be equal! This gives us a system of equations for each coordinate (plane and sphere). Note that there are multiple possible values, but some involve imaginary numbers so you'll have to eliminate those solutions:

    (-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
    = (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
    F^2 = F_x^2 + F_y^2
    
    (-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
    = (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
    = (-m * v_o_z +/- sqrt(m^2 * v_o_z^2 - 4 * F_z * m * (s_z - s_o_z))) / 2 / F_z
    F^2 = F_x^2 + F_y^2 + F_z^2
    

    Solve for the (F_x,F_y) or (F_x,F_y,F_z) coordinates and you've got the force you need.

    Let me know if you have any questions or if you find mistakes in my math.

    0 讨论(0)
  • 2021-02-04 17:43

    You may get the effect you want by ensuring a smooth change in velocity, rather than thrust. That way, if the enemy overshoots the player, it can immediately reverse its acceleration, which will slow it down and eventually reverse its direction of travel.

    You can accomplish this by changing the velocity during each iteration, by a small amount that's based on the distance from the enemy to the player:

    while (game_in_progress)
    {
        // Distance from enemy to player.  The larger the
        // distance, the greater the acceleration will be.
        delta.x = player.x - enemy.x
        delta.y = player.y - enemy.y
    
        // Accelerate by changing velocity based on distance,
        // where 'scale' is sufficiently small. (Limit v to
        // some maximum if you choose; likely to be unnecessary.)
        v.x += delta.x * scale
        v.y += delta.y * scale
    
        // Update the enemy's position.
        enemy.x += v.x
        enemy.y += v.y
    }
    

    By calculating the x and y values independently, you can save yourself the headache of dealing with vectors, angles, and simultaneous equiations.

    Similarly, by recognizing that acceleration (thrust) is simply a change in velocity, which in turn is a change in position, you can create a discrete-time simulation using only simple algebra instead of calculus.

    Have fun!

    0 讨论(0)
  • 2021-02-04 17:43

    I wrote a simple asteroids game a while back that had an "ally" ship which would hunt down asteroids and shoot at them for you. Basically it found the closest asteroid then started smoothly turning towards it and going after it. Sadly i don't have the code anymore but if memory serves, i think i tured the ship a little each turn, then if the asteroid was far away i accelerated but if it was close i tried to match the asteroid's speed. It was pretty cool actually, and a minimum of algebra involved.

    The best way to do it would be to take 2 radian values and lerp between them, handling wrapping. (perhaps by adding or subtracting 2pi where necessary). Then convert it to a unit vector. Subsequently multiply that by the speed you want the ship to accelerate, and there you go!

    0 讨论(0)
  • 2021-02-04 17:43

    One simple way (not proper physics) is to calculate your enemy's "desired velocity" and then adjust enemy's currently velocity towards that, minding whatever limits on top, or minimum speed it has.

    For instance, in a little 2d game I wrote (http://wordwarvi.sourceforge.net) there are "heat seeking missiles." It looks pretty weird if the missiles stop in midair to turn around. So what I did was as follows: I calculate a "desired velocity" which is towards the player. This is just done by "similar triangles". I find the distance to the player in X, and in Y, and whichver is greater, I make the "desired (x or y) velocity be the maximum possible, and then scale the other one to fit the "similar triangle." Note, that's just the "desired velocity," not the current velocity. I take the current velocity and adjust it slowly (by a little bit per frame) towards the "desired" velocity (though the desired velocity is recalculated per frame as well,) mindimg minimums on vx and vy to keep them from stopping mid-air.

    Dumb algorithm, but it works ok (nobody's complained that they are too easy, too hard, or too unrealistic.)

    Edit: on re-reading the question, my answer is probably not what you're after.

    0 讨论(0)
  • 2021-02-04 17:45

    You need to think in proper physics terms. You have a velocity, and you want to add an acceleration. That's all there is to it - the acceleration is a gradual change in velocity that will draw the enemy towards to player, allow it to overshoot, slow down (or turn) and then head back towards the player.

    Acceleration is measured as d(velocity)/time. You want to accelerate towards the player at any point in time, so every interval (second, hundredth of a second or whatever you choose) you need to add the vector between enemy and player, multiplied by some constant, to your velocity.

    Velocity = Velocity + c * (Player-Enemy vector)
    

    Constant c will depend on how fast you want to accelerate towards the player, and how often you are updating your velocity.

    If you want to 'cap' the maximum speed of the enemy so that it doesn't continue to increase the magnitude of its velocity indefinitely, you can also do so.

    Velocity = Velocity * (Maximum magniture / |Velocity|)
    

    EDIT: to clarify further, adding a Velocity simply means adding the component vectors. So

    Vx = Vx + c * Ax
    Vy = Vy + c * Ay
    

    where V is velocity and A is acceleration. Magnitude is measured as sqrt(Vx^2 + Vy^2), ie the hypotenuse of a right triangle. So if you want maximum speed of the enemy to be m,

    Vx = Vx * ( m / sqrt(Vx^2 + Vy^2)
    Vy = Vy * ( m / sqrt(Vx^2 + Vy^2)
    
    0 讨论(0)
  • 2021-02-04 17:46

    I've solved problems like this professionally, and I advise you to start with simple versions and work up. Make sure you get expected behavior at each step before you try the next.

    1. The seeker seeks a stationary target at the origin in one dimension. That's right, one dimension. It can thrust back and forth on the x-axis and it's trying to get to x=0. The thruster has no throttle (like a solid rocket) but the seeker can point it in either direction. If you program this right, the seeker will oscillate around x=0, overshooting every time.
    2. The same, but the target is stationary somewhere other than x=0. Just make x relative, not absolute (that is, the seeker cares about the difference in x, not the target's x).
    3. Now the target is moving (or jumping). The seeker should be able to follow it around. The oscillation will grow or shrink depending on how the target moves -- you'll see what I mean.
    4. Now two dimensions. The seeker always thrusts directly toward the target, which means you must divide the thrust into x and y components by simple trig. If you move the target, the seeker may go into orbit around it.
    5. Back to one dimension and a stationary target, but now the seeker is trying a rendezvous, not a flyby. This is the hard part. The goal is to have distance and velocity become zero at the same time, no overshooting, so the seeker must know its own braking ability. When x is less than v^2/2a, the seeker must reverse thrust, thrusting away from the target in order to slow down and meet it. It's nice to allow the seeker to stop thrusting when it's very close to the target.
    6. The target starts moving again. This is easy; just make x and v relative, not absolute.
    7. Multiple dimensions. This is remarkably easy; the x, y, and z parts are independent.

    Now if you want a seeker that can't turn on a dime, but must curve around, things get tricky...

    0 讨论(0)
提交回复
热议问题