Programming a smooth change in traction from the current velocity vector to the target vector

TL; dr: "I'm not sure how to calculate a smooth traction transition between one vector and another."

I program a simple game where an enemy chases a player in open space (without walls). I calculated enemy speeds x and y independently, speeding them up if they take them towards the player and quickly slow them down if they go wrong (e.g. EnemyVelocity.x> 0 and player.x <enemy x, then EnemyVelocity.x - 2.)

While the gameplay is really funnier trying to dodge the enemy, I want the enemy to behave using the correct physics. What I am doing now is that the enemy has set his goal (think of a spaceship) based on the angle between them and the player, and their thrust is accelerated to maximum speed (by calculating the side c of the EnemyVelocity triangle). As soon as this happens, I'm not sure what the best way to set up traction. If I do not leave the maximum speed, the enemy will accelerate well, but it will be easy to exceed the player, and then it will take a long time to gain enough momentum in the direction of the player.

I would like the enemy to constantly adjust the speed on the way to the player, aiming where they are (I do not want them to predict where you will be). Then, when they missed the player, I would like their speed and acceleration formulas to adjust their speed and send them back to the player.

I think this will include two vectors: one where the enemy is currently traveling, and one where the enemy wants to travel (a vector that will lead them directly to the player). I am not sure how to calculate a smooth traction transition between one vector and another.

Any tips, formulas or questions would be greatly appreciated! Thanks, stack overflow.

+6
math vector linear-algebra physics angle
source share
7 answers

Everything returns 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 speed, and t is the current time.

Of course, this is a straight line, so if you want to convert to two or three dimensions, you will need to do the math. F , s , v and a are all vectors, which means that their direction is equally important. Technically, t also a vector, but since time usually goes in one direction only, we need not 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 the speed in the direction of the player, you have a fixed total force ( F ) to change the current speed to the player. In physics, everything does not happen instantly, but your goal should be to minimize the time at which the change ('t') occurs.

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 the current location of your opponent or your target location ( (s_x,s_y) or (s_x,s_y,s_z) ) for your target speed (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 

Then we decide the acceleration (this is due to the force that 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 

Insert this into the equation of force:

 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 decide 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 

Time must converge, so time will be equal! This gives us a system of equations for each coordinate (plane and sphere). Please note that there are several possible values, but some of them include imaginary numbers, so you will have to eliminate these 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 

Decide for the coordinates (F_x,F_y) or (F_x,F_y,F_z) , and you have the strength you need.

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

+1
source share

You can get the desired effect by providing a smooth change in speed, rather than traction. Thus, if an adversary exceeds the player, he can immediately cancel his acceleration, which will slow him down and, ultimately, change the direction of movement.

You can accomplish this by changing the speed during each iteration to a small amount based on the distance from the opponent 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.) vx += delta.x * scale vy += delta.y * scale // Update the enemy position. enemy.x += vx enemy.y += vy } 

By calculating the x and y values ​​independently, you can save yourself a headache when working with vectors, angles, and simultaneous equations.

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

Good luck

+2
source share

You need to think in the right physical terms. You have speed and you want to add acceleration. All he needs is acceleration - a gradual change in speed that will direct the enemy towards the player, allow him to overshoot, slow down (or turn), and then go back to the player.

Acceleration is measured as d (speed) / time. You want to accelerate the movement to the player at any time, so every interval (second, hundredth of a second, or any other) you must add a vector between the opponent and the player, multiplied by some constant, at your speed.

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

The constant c will depend on how fast you want to speed up to the player, and how often you update the speed.

If you want to “limit” the maximum speed of the enemy so that he does not continue to increase his speed indefinitely, you can also do this.

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

EDIT: To clarify, adding Velocity simply means adding component vectors. So,

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

where V is the speed and A is the acceleration. The value is measured as sqrt(Vx^2 + Vy^2) , that is, the hypotenuse of the right triangle. Therefore, if you want the maximum speed of the enemy to be equal to m,

 Vx = Vx * ( m / sqrt(Vx^2 + Vy^2) Vy = Vy * ( m / sqrt(Vx^2 + Vy^2) 
+2
source share

I wrote a simple asteroid game that had an “ally” ship that could track down asteroids and shoot them for you. Basically, he found that the nearest asteroid then began to smoothly turn towards him and follow him. Unfortunately, I no longer have the code, but if the memory is serviced, I think that every ship twisted the ship a little, and if the asteroid was far away, I accelerated, but if it were close, I tried to compare the speed of the asteroids. In fact, it was pretty cool, and a minimum of algebra was involved.

The best way to do this is to take 2 radian values ​​and lerp between them, process the package. (possibly by adding or subtracting 2pi, if necessary). Then convert it to a unit vector. Subsequently, multiply this by the speed you want the ship to accelerate, and there you go!

+1
source share

One simple way (not the right physics) is to calculate the "desired speed" of your enemy, and then adjust the speed of the enemy in the direction of this, taking into account any restrictions from above or the minimum speed.

For example, in the small 2nd game I wrote ( http://wordwarvi.sourceforge.net ), there are "heat-seeking rockets." It looks rather strange if the rockets stop in the air to turn around. So, I did the following: I calculate the "desired speed", which is aimed at the player. This is done only with the help of "similar triangles." I find the distance to the player in X, and in Y, and which is greater, I make the “desired (x or y) speed as high as possible, and then scale the other to fit the“ similar triangle. ”Note: only the“ desired speed ” rather than the current speed, I take the current speed and slowly correct it (a little per frame) in the direction of the "desired" speed (although the desired speed is recounted per frame) minimum minima at vx and vy so that they do not stop in the air.

A dumb algorithm, but it works fine (no one complained that they were too light, too heavy or too unrealistic.)

Edit: when re-reading the question, my answer is probably not what you need.

+1
source share

I have solved such problems professionally, and I advise you to start with simple versions and work. Make sure you get the expected behavior at each step before trying the next one.

  • The finder searches for a fixed target at the origin in one dimension. That's right, one dimension. It can pull back and forth along the x axis and tries to get to x = 0. The steering does not have a throttle (for example, a solid rocket), but the finder can point it in any direction. If you program this right, the crawler will oscillate around x = 0, re-adjusting each time.
  • The same thing, but the target is motionless somewhere except x = 0. Just make x relative and not absolute (that is, the crawler takes care of the difference in x, not the target x).
  • Now the target is moving (or jumping). The seeker should be able to follow him. The vibrations will increase or decrease depending on how the target moves - you will see what I mean.
  • Now two dimensions. The crawler always pulls directly toward the target, which means that you must split the thrust into the x and y components with a simple trigger. If you move a target, the seeker may enter orbit around it.
  • We return to one dimension and a fixed target, but now the seeker is trying to rendezvous, and not span. This is the hard part. The goal is to make distance and speed equal to zero at the same time, without overshoot, so the finder must know his own stopping power. When x is less than v ^ 2 / 2a, the seeker must cancel the traction by pulling away from the target in order to slow down and meet it. It is nice to let the seeker stop moving when he is very close to the target.
  • The target begins to move again. It is easy; just make x and v relative, not absolute.
  • Several dimensions. It is remarkably easy; the parts x, y and z are independent.

Now, if you need a finder that can't turn on a dime, but needs a curve , things get complicated ...

+1
source share

There are only a few pointers to get this right and easy. 1) it is easiest and most often to work with vectors, and write all two or three times. 2) everything will look right if you control the force (which is effectively accelerated with A = F / mass), and then dynamically develops speed and position.

Your basic loop for realistic motion looks like this: (where CAP are vectors and dt is your timestep):

 while (motion) { A = get_acceleration(X, V, A, X_target, V_targer, A_target) V += A*dt // V is the integral of A X += V*dt // X is the integral of V } 

And indeed, it is that you are a dynamic evolution.

Then you need to decide how to determine the acceleration, i.e. write get_acceleration . There are several options that depend on several factors, and real hunters use many strategies. For example, if you have a lot of traction in relation to your mass (i.e., with high acceleration), you probably just want to go straight to the target; but if you have a mass of mass relative to your traction, you probably want to do an interception. If you want to slow down when approaching the target, you can cancel the acceleration when |X-X_target| will become small (i.e., approach) and / or their speeds are close. In addition, damping can help things not hesitate, and for this add a term to speed something like -c*(V-V_target) . I suggest you play with them until you get what corresponds to the physical form and feeling that you are striving for.

+1
source share

All Articles