- Home /
Set speed by distance from object
I have transform that moves along a path of set points and at each point the transform gets its speed set to value given at point.
The transform moves from point 1 to point 2. Point 1 has speed value of 0.1 and point 2 has speed value of 10
What I need is that my transform moves from point 1 to point 2 and same time increases its speed from 0.1 to 10 depending on how much is left to the point 2
At 50% of the way the speed should be = 5.05
At 75% of the way the speed should be = 7.575 and so on.
The transform moves using the Vector3.MoveTowards() function...
I think I need to use Mathf.MoveTowards() to change speed...
All this is done in update function
But, I just can't seem to figure out the right way to calculate the speed change. Any help appreciated. Thank you!
Answer by JVene · Sep 03, 2018 at 10:31 PM
@Timze You want linear interpolation of velocity. You've almost expressed the formula in your example. There are lots of options, including Mathf.lerp for a scalar, but it depends on your existing method of calculating motion. You might also prefer slerp, the spherical linear interpolation (Google provides background), but it is a matter of preference and performance (slerps are slower than lerps, but for one object it won't matter).
MoveTowards is a type of lerp on Vector3, but it interpolates distance from a start point to and endpoint using lerp. That's not velocity. You still have to scale the ratio according to velocity, but at that point the meaning is lost and the calculation is mind bending.
The 'classical' way of applying a velocity to a Vector3 is to fashion a Vector3 that points in the intended direction, then setting it's length to represent speed, then multiply that speed by Time.deltaTime to get the portion of travel for the current time frame. The only twist for your case is that the velocity is changing in every frame, and you may as well express that as a scalar (a float), as you already have in your description.
For each suggestion that follows I'm posting pseudo code - untested but probably valid code you must interpret for your situation.
At each segment you have a starting point and ending point, both Vector3's. Fashion a unit vector that points in the correct direction with something like this:
Vector3 dir = (p2 - p1).normalized;
This creates a vector that 'points toward' p2 form the perspective of p1. The 'normalized' property of the implied temporary Vector3 makes it a 'unit' vector, or a vector with a length of 1. This specialized form is common for a wide range of uses, but in this case it is used to multiply by a scalar (a float) to produce a vector pointing in the same direction of any arbitrary length. For example:
Vector3 v1 = dir * 0.1;
Vector3 v2 = dir * 1.5;
Vector3 v3 = dir * 10;
This code snippet shows how the direction established previously can be used to fashion 3 vectors, where v1 is of length 0.1, v2 is of length 1.5 and v3 is of length 10. Each of these could express a velocity and a direction at the same time. In order to move a point, say position (initialized to equal p1), toward p2 for a distance of 1.5, the following would suffice:
position += v2;
This is because v2 was multiplied by the unit vector dir, already pointing toward p2, by 1.5, making v2 a distance of 1.5, and so this increment moved position toward p2 by 1.5 units. I'll leave it to you to discover the remaining distance from the new position to p2, and to determine if p1 has passed p2 (or reached it).
Now, given this, what now? In Update, you can express a velocity as a scalar, let's say speed = 2.25f. To advance position (initialized to equal p1) toward p2 at that speed, this combination would suffice, assuming dir has already been initialized:
Vector3 step_toward = ( dir * speed ) * Time.deltaTime;
position += step_toward;
Of course, in your case, speed may change from frame to frame, so how is that done? It is a lerp or slerp created out of the two speed ranges based on the ratio of the current distance from p1 to p2 divided by the distance from p1 to p2. When you calculate dir as suggested above, you also calculate a vector from p1 to p2, but in that format you've lost the temporary vector. Perhaps this is better:
Vector3 vp1p2 = p2 - p1;
Vector3 dir = vp1p2.normalized;
float distancep1p2 = vp1p2.magnitude;
Now, you have the dir and the distance between p1 and p2. Keep them handy for use in Update (distancep1p2, or whatever you prefer to call it, should be a member variable, as should dir).
Within each update, you need to calculate the value supplied to lerp (or slerp) as the third parameter. It is a ratio, ranging from 0 to 1. In each update, you need the distance from the original p1 to the current position. So
float distance_traveled = ( position - p1 ).magnitude;
The value lerp or slerp requires for the 3rd parameter is
float ratio = distance_traveled / distancep1p2;
Now, let's take your values, the starting velocity at p1 is 0.1f (speed1 = 0.1f), and the velocity at p2 is 10f (speed2 = 10f). At any point along the way you can calculate the velocity for each update with something like:
float frame_distance = Mathf.lerp( speed1, speed2, ratio ) * Time.deltaTime;
This 'lerp' gives you that speed between speed1 and speed2 that is some portion of the way between the two based on ratio, which was fashioned based on the distance already traveled. frame_distance is that which must be traveled in the direction of dir for the current frame, so:
Vector3 v = dir * frame_distance;
position += v;
Should to the trick for one frame. However, I have not dealt with what happens if position is moved beyond p2 in your description. I'll leave that to you for now, as the tools to do that have already been illustrated (you just have to think about it a bit).
This was exactly what I was looking for. Such a great answer in such a short time. Well explained. Thank you.