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
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.
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!
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!
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.
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)
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.
Now if you want a seeker that can't turn on a dime, but must curve around, things get tricky...