There isn’t much new math in here. Just some semi-practical things we can do with it.
Sometimes we only want a simple 360-degree spin, usually around y.
The simplest way to do that is hand-moving our own y variable, then setting rotation from it:
This method also works if the player can use arrow keys to add and subtract from
y. A nice thing is we could decide to manually make y wrap around at -180 and
+180, even though Unity prefers 0-360.
If we want to spin to a certain number of degrees, there’s a problem – we have to account for wrap-around.
Say we’re at 50 degrees now and want to rotate to face 330. We should subtract.
It’s only 80 degrees backwards (50 down to 0, 30 more from 360 to 330). It turns out
that 50 should spin forwards for any angle up to 230 (180+50), otherwise
backwards.
The solution involves a few if’s, but Unity has a common built-in that takes does it for us:
MoveTowardsAngle(d1, d2, 4) adds or subtracts 4 from d1 to move it closer to d2, accounting for wrap-around. Like MoveTowards, it also won’t overshoot. For example MoveTowardsAngle(50,330,4) is 46.
This code uses it. We set targYspin and it spins the shortest way to it:
Like MoveTowards and friends, 30*Time.deltaTime means it spins 30 degrees/second.
It won’t snap the numbers. For 50 and 330, it moves 50 down to 0, then past to -30 (which is the same angle as 330).
Sometimes we’re free-spinning (LookAt, rotating over funny axis, or just rolling on
the ground), and want to find our compass facing.
It so happens transform.eulerAngles.y is always the correct 0-360 y-facing.
That’s because of the funny way Unity stores them. Suppose we enter (170,5,0) for
rotation. y=5 is basically North, but 170 on x flips us over to South. But the
Inspector is lying. Unity really stores that as (10,185,180). y correctly says we’re
facing basically South.
Another way of getting our basic compass facing is to take the forward z-arrow of the rotation, flatten it, find the angle between it and +z, then figure out left vs. right:
Here’s a more trig-like version that uses atan2. The main use is to convince you that using real trig is very error-prone:
Trying to read transform.eulerAngles.x doesn’t work as well. For one thing, -10 is the natural way to say “10 degrees up”, but Unity stores that as 350. For another, x’s past 90 are fixed. If you set x to 100, it becomes 80 (and y and z flip by 180).
When a game character walks over uneven ground they generally stay straight up.
But sometimes we like it when things tilt with the ground: maybe it rides on
treads.
The secret to applying ground tilt is getting the normal. For flat ground, the normal is up. Tilted ground’s normals are mostly up, but tilted a little. The plan is the find the rotation between those two arrows. Then we can place our object for flat ground, and hit it with the ground tilt.
Getting the normal is in a previous chapter (it depends on how the ground is
made). Computing the difference between arrows is the rare FromToRotation.
This code plants a tree tilted with the ground. The tree is first randomly spun on y, to make it more interesting:
The deluxe version of this trick is when the tree is using some fancy math to spin in place. Keep the tree’s “flat” rotation stored, change it however, then re-apply the ground tilt every time.
Sometimes we want to partly tilt with the ground. If you remember,
groundTilt*0.3f isn’t legal, but Quaternion.Lerp can do it. This tilts us only 30%
with the ground:
If we use this while walking around, we get a common problem – our rotation will tend to snap. For example, as we cross the top of a ridge. The standard trick is to compute the rotation we want, but then use MoveRotatation to smooth into it:
A simple orbit camera spins around us in a half-sphere (the top half), always looking
at us. Looking at us is the easy part – the LookAt shortcut will do that. For the
half-sphere part, we can imagine a very long selfy-stick, spinning on x&y the usual
way.
When we use rotations to spin an arrow, it helps to pick a good base arrow. For a camera, we want it to start behind us. Our base arrow should go backwards. y=0 looks forward and y=180 looks backwards (the camera will be ahead of us, looking at us, which is backwards). x=0 looks flat, with x up to 90 putting the camera higher up, looking down:
A neat thing about using a backwards arrow is how +x finally tilts up instead of
down like it normally does.
We probably also want this to spin with the player. As the player turns, a behind camera should stay a behind camera. We can do that by adding the player’s rotation. A little trial and error says it goes as a global, so is in front:
Sometimes the camera might be temporarily controlled by something else. We’d like it to smoothly zoom back into the orbit spot, instead of an ugly snap.
We’ll compute the camera position as normal, then use MoveTowards to go there:
These two lines would replace the one theCam.position= line:
mvSpd should be large enough that normal spins are instant, but a camera sent out to Cleveland would take a second or two to zoom back.
We can change arrow lengths pretty well with normalizing and scaling. But some
things always confuse me, and they also have a shortcut.
Suppose we have a length 7 arrow and want it to be length 5. We’re pretty good at cutting them in half, or making them 10% longer. But this is different.
The long solution, which we know, is to normalize it, then take it times 5. Turning 7 into 5 is hard, but turning it into 1 then 5 is easy.
A cool shortcut is doing both at once. This makes it so arrows can’t be longer than 5:
Dividing by len normalizes it, then multiplying by 5 makes it that long.
arrow*(wantLen/currentLen); resizes any arrow to the length you want.
A similar problem is adding or subtracting from the length of a line – making it
0.5 longer. We can do it like:
arrow*=(len+0.5f)/len;.
If len was 6, this makes it length 1, then multiplies that by 6.5.
The main thing is that we’re used to plus/minus being easier than multiplication. But for arrows, it’s the other way around.
To get a line between two points, take a length one object, put it exactly between the points, aim it at the second one, and stretch it to be that distance.
The object you use should be set-up so its z forward axis is the stretchable part. For example a Unity cylinder has height 2 and runs along +y. We’d use the parent trick to aim it along +z and cut the length in half.
The code to make a line between me and a ball:
That plan makes the line go center-to-center (really, origin to origin). It will tend to go inside of them. That might be fine, or not. Suppose we want a line that goes from our eyes to the surface of a sphere. We can adjust the endpoints:
Getting the eyes was easier, since it’s a fixed point from us. Going to the edge of
the sphere is more complicated because it’s not one fixed spot – it’s whatever part of
the sphere is facing us. The best way I could do it was by shrinking the
arrow.
There’s one final trick. If your line is just a stretched 2D square then the z-spin will make a big difference. If the face is aimed +y then it will normally be pointing straight up. From the side we’ll only see the edge, which will look bad. We’d like to z-spin the stretched square to always face us (it probably can’t face us exactly, but as close as possible).
Assume the flat part is aimed up (if it’s not, the parent trick can make it that way). We can use the 3rd “head this way” input to aim the face at the camera:
This is known as an Axis-Aligned Billboard. A square that perfectly aims only at us is a regular billboard.
Suppose we have blocks with plug-ins on the sides. Each plug-in is an empty child, rotated to show how it fits. +z is is where it plugs in and +y is how they must be rotated. Another block needs its +z facing ours, spun to have the +y’s lined up.
This shows two copies of the same block, with A from the first lined up with B from the second:
The problem is figuring out how to place and rotate the second block so B fits
correctly into A. The first block could be anywhere, spun any way.
This will take a while. First we need to find the two plugs: A from block 1 and B from block 2. They’re children:
Now the problem is a little simpler. We’re only trying to get block 2 to line up
with A.
Since B goes in the same spot as A, the only thing left is to follow the arrow, backwards, from B to the center of its block. But which direction? We need to account for A’s rotation, flipped 180 degrees, then account for B’s rotation on its block. Yikes!
It took me a few tries to get right:
This took a ton of testing. The first testing step was to try to get block2’s
rotation correct, without trying to move it yet.
An alternate way of doing all of that is doing a little dance with parents and children. If we can set it up correctly, we’ll be able to move the B connector to where it should go, letting it drag block2 with it.
We can temporarily make B the parent of block2. Then we can place B on A and let Unity do the work figuring out where block2 is. We still have to use A’s rotation, flipped 180:
This version might be easier to visualize. It’s not any faster – Unity has to do all of the math from the first version to set all the children.