We already know the math to use points and offsets to gradually move things. Unity has some standard built-in commands that do the same thing.
We know how to move simple rotations – spinning around y. There are more standard quaternion commands that let us gradually move rotations in more ways.
If we want nice movement we need a better way of saying how fast we go. “Add 0.05 each update, and we’re going whatever speed that works out to” is unreliable. It turns out the best way is to set speed-per-second, adding the correct fraction a little at a time.
The math seems easy enough – there are 60 updates/second, so we divide by 60. But sometimes there are 30/second. Even worse, that could change mid-program, or even mid second.
So what we do is forget about frame-rate and updates per second. Instead we time how long each update takes. If an update takes 1/10th of a second, we add 1/10th of the movement. If the next update takes 1/60th, we add 1/60th.
Since we have no way of knowing how long the current update will take, we use the time passed since the last one. At the start of each update, Unity computes that and saves it in Time.deltaTime. A typical value is 0.167 (which is 1/60th).
This code adds 3.5 to x every second:
This moves us forward at 3.5 units/second. In our minds
transform.forward*3.5f; is our speed for second, and *Time.deltaTime is the
trick to add it a little at a time:
This next code moves the red cube towards us at whatever speed-per-second we enter:
An interesting thing – when it arrives it will buzz back and forth. It overshoots, then come back towards us and overshoots again.
Unity has a helpful function that does the same thing as that last example. As a bonus, it won’t overshoot. The form is MoveTowards(startPoint, endPoint, moveAmount);. For example, this moves the red ball towards us by 2 units:
Like any normal function, it doesn’t actually move anything. It computes the position. But the form we use is almost always A=MoveTowards(A,target,amt);, which moves A. The distance is in real units, not adjusted for anything. To move at 2 units/second we need the deltaTime trick:
The most fun thing above MoveTowards is how we can easily write it using the vector math we already know. It’s a fun review:
There’s nothing wrong with using MoveTowards as a shortcut. It’s a
function, with a nice name, for a common calculation. But if we need it to
work a little differently, or are fighting with it, we can always go back to the
basics.
MoveTowards movement can seem robotic, but only if we use the same speed all the time. We can easily change it. This sets the speed to 1/2 the distance to the target, per second, but at least 1:
Or we could start at speed 0, increasing by 3 per second, to a maximum of 8. Whoever sets new targets would should also reset speed to 0:
MoveTowards is good when:
There are lots of ways to move. MoveTowards does a nice job handling one way: “speed per second to a point”.
Lerp stands for linear interpolation, which is the math way to say “a spot between 2 points based on a smooth 0-1”. C=Vector3.Lerp(A,B,0.25f); sets C to a spot 25% between A and B, going along the line between them.
You may have noticed that we could do that back in the first chapter. Lerp is very simple. Written out:
A practical Lerp example is picking a random spot on a line. We give the end points and a random 0-1:
Lerp is a nice 1-line way to say things like “10% of the way from A to
B”.
Lerp isn’t really a movement command, but it’s sometimes used that way. This will move the green cube from us to the red one over 2.5 seconds:
Notice how it’s up to us to gradually move pct0to1. We can call the same
MoveTowards over and over, and it will work. But Lerp only works if we increase the
last input.
There’s a common Lerp hack to get a zingy fast-then-slow movement. This zips a cherry, from whereever it is now, to a fixed point. Notice how cherry is the starting spot, and the thing we move:
It says to move yourself 5% of the way each time, which is less as you get closer. It looks very zippy. 5% doesn’t seem like much, but it adds up. You can’t control the speed or the time it takes. This trick is really only useful for quick visual effects.
So far, we know how to gradually rotate by changing the numbers inside of a Quaternion.Euler(x,y,z). There are more useful ways to spin gradually. We can spin arrows into other arrows, spin quaternions into other quaternions, or take fractions of quaternions.
We can rotate an arrow without needing to make a quaternion or use degrees. Give it the final arrow and gradually spin to match. Like most arrow math, you have to imagine both arrows coming from the same point.
The basic code is like a MoveTowards. The third input is the speed, in radians per second(yikes!), and we’ll ignore the 4th input for now:
The really cool thing is how it computes the best way to rotate. Depending on
how endArrow aims, arrow might spin right, up, or diagonal. It makes a straight spin
in whichever direction is best.
Here’s an example showing it in use. The ‘A’ key puts the ball 4 units in front of us. That could be anywhere. Then it automatically spins that arrow to 4 units straight up:
If you press ‘A’, you’ll see the red cube is on a curved path. It’s at the tip of a
rotating arrow. If it doesn’t have far to move, the curve is small, but it’s still
there.
The 4th input is how fast the arrow changes length. If the target arrow is longer or shorter than you are, you can shrink and grow to match it. 0 means not to change length. That seems pretty cool, but it’s a pain since they don’t auto-synch. You’ll probably finish the rotation, then grow in place until you’re long enough. Or vice-versa.
Rotations have a similar move-towards-like function. You give it the current rotation, the target rotation, and how much to move (in degrees, this time.) Here’s a simple example of spinning us towards a target:
This spins us from our current facing to aiming straight up, at 60 degrees/second.
Try it with starting in various odd directions. It rotates us the closest way, along a
nice, straight curve.
It also does something lines don’t – it matches the z-roll. If you’re on your back,
this will roll you head-up. The degrees per second counts z. If you’re aimed mostly
the correct way but have the exact opposite z-roll, this will take several seconds as if
rolls you over.
Here’s a practical example. We want to look at the red cube by smoothly spinning. We get the rotation to it and smoothly spin ourself to match it:
LookAt would make us always face red. This will have us track it, with the
possibility it could “out run” us for a while.
This next one is really cute. Pretend our main game is walking around, spinning only on y. We move by changing the float yFacing. But sometimes we get knocked down. When that happens we activate our rigidbody, which lets it fall and roll around.
To get up, we turn our rigidbody back off and use RotateTowards to get up:
The last line is a trick to check for standing. transform.up is (0,1,0) when we’re perfectly straight. If we’re the least bit crooked in any way, the y-value will be less than 1.
It looks a little fake, like we have some sort of gyro guidance system to straighten up. But it’s also really cool how it gets right back up.
Rotations have their own Lerp. It works like the one we’ve seen for vectors. This finds the rotation 1/2-way between q1 and q2:
For example, this faces us 1/2-way between the red and green cube. If finds the rotation to each and averages them:
That example isn’t super useful, since we can already look between 2 things by
finding the point in-between them.
Lerp lets us cut an angle in half, which we couldn’t do before. The secret is to start with Quaternion.identity. We’re averaging ourself with 000:
We can also use the Lerp hack to make a fast-then-slow rotation:
Notice how it’s the same form: we’re the starting point, and we also move, and we always move 3% of the way.
This can be nice for transitions. Instead of having the camera’s angle snap somewhere, this makes it very quickly aim.
Vector3.MoveTowards is the main, easiest way to move points in straight lines. Playing with the 3rd speed input can make it look pretty nice. Vector3.Lerp is for math. For when you think “I wonder where 30% of the way between A and B is”?
But both are just shortcuts for things we can already do.
Quaternion.RotateTowards is new, and very useful. It gives a perfect spin to any direction, in degrees per second. handles most things – gives you a smooth, constant shortest-way spin, in degrees/second.
Vector3.RotateTowards is about the same thing. Rotations and directions are similar, after all, and you can turn one into the other.
Quaternion.Lerp is useful for math – taking half a rotation. We have no other way
to do things like that.
Both Lerps (points and rotations) have an unclamped version, which means the percent can be 1.1 or -0.5. Rotations with this can be useful:
Quaternion Lerp has an alternate version, Quaternion.Slerp (spherical lerp).
They both do the same thing. Supposedly Lerp is faster, but slightly less accurate.
I’ve never seen a difference.