Besides turning a character and moving it along a set vector,
we can also turn the character and push it in the direction it is facing (or along one of its local vectors).
In Unity,
a character has 3 local vectors: z for forward, y for up and x for right, regardless of the character's
rotation, we can push along any of these local vectors with the Translate function, by specifying movement
in one (or more) of the x, y and z local axes. To move the character forward at a constant speed,
we would modify only its z-coordinate. To move a character right at a constant speed, we would modify
its x-coordinate and to move the character up,
we would modify its y-coordinate.
You can also move a character backwards, left and down by specifying a negative speed.
If you modify the rotation of an axis while rotating it, the character will move in a different world
direction, however,
it will still be moving along its local specified axis.
For example, if a character changes direction while being pushed along a z-axis, it will move around the
world,
but relative to itself, it will still be moving along its z-axis as it's the characters forward axis that
has changed in the rotation and not its movement speed.
So let's try this all out in a project.
Download the project 'PushForward', which is the resource that comes with this lecture and when you open
it up, open up the zombie scene (because everything's better with zombies) and open up and have a look
at what you've got in there. If you press Play at this point, you'll notice that you've got an animated
zombie that's not going anywhere...fast
and a cube-looking thing.
If you use the arrow keys, you'll find that you can rotate that cube and drive it around.
This cube will be our goal object and by moving it we'll be allowed to see the rotation of that zombie and how
we're going to push it forward.
All right, so let's stop playing that. The cube itself, if you're interested, has a 'Drive' code on it and
you can have a look at that script and what it does, it just drives the cube around. Again, it pushes it along
just the same way as we're doing with our zombie character and also has a rotation in there as well,
but that's not the focus of this lesson.
We're looking at moving the zombie, changing its direction (so it's always looking at this cube) and pushing
it along its z-axis.
Now if you click on the model itself and have a look, you'll see it's z-axis is facing forward
and this is what we're pushing it along.
So create yourself a new C# file called 'MoveLocal'. In that script, you're going to add this.
So it's similar to what we had in the previous lecture.
We've got a 'goal' that we will pass in (that will be our cube) and we have a speed.
Now this speed you'll need to adjust, based on the animation and whether it fits with it. Sometimes
if your speed is going too fast or too slow for the particular animation, the character will slip and
there are many ways you can fix this but we're not, again, going into all that depth with animation
either.
Right.
So add that in and then in the Update, we have a transform.LookAt to change the direction that we're
looking in for our zombie, which will be the 'goal' position and then we're going to push the zombie along
it's z-axis using our speed and multiplying that by Time.deltaTime.
Now I'm doing this in the Update
and what did I tell you before?
I always forget to put LateUpdate, so let's put that there.
Now save that.
Go back into Unity and you want to attach that 'MoveLocal' to Jill
(Jill's the name of our zombie) at the very top level. Looking in the Inspector for Jill,
she'll have a Transform, an Animator and then the script that you've added there. In the script, you'll
see the 'goal' has been exposed, so get hold of your cube and drag-and-drop that in there as the top level
goal.
Ok, so now press Play and she'll start moving towards the cube.
Now if we drive this away, because of the LookAt, she'll be constantly turning and looking at the cube.
Everybody in this scene is currently floating above the ground but you could adjust that by getting
hold of your character and driving it down into the ground and also grabbing your cube and driving it
down like this.
Ok, then have another play.
Now you can see that the character is still floating a little bit above the ground
and we have the same issue with the flickering that we have had in the previous lecture where the character
will reach the goal point and then keep trying to reach the goal point, slightly overshooting and coming
back and swapping directions,
but if you move your cube away, it will stop.
Now she's still floating in the air
and this highlights another issue with this particular code, which is that she's going to change and look at
that cube,
wherever it is. The LookAt does not discriminate
if the cube is up in the air. So let's move this cube like that and look how that LookAt is moving the
z-axis, so that it will change and always be facing towards the cube.
If we pause this and have a look at Jill, you'll see that her z-axis is being turned towards our
cube position and this will cause a lot of issues when you start moving objects that really should stay
on the ground (like a humanoid) to look at the goal position that is in 3D space and can be up in the
air.
To recap on that, what the LookAt is doing is rotating your model so the z-axis is pointing
towards the position of the goal.
So it doesn't matter where in 3D space
that goal is positioned, it will orientate the 3D model, so that he's facing in that direction
and this is a real problem for characters that need to stay grounded.
Instead what we want to do is have our character rotate in the direction of the object but only in the
x and the y plane, such that the z-axis then lines up with the position of the object on a vertical
axis from it, at the exact same height of the character.
Therefore we need the character to perform a LookAt at a point that is in the same y as the character,
but the same x and z as the goal object. To achieve this in Unity is just a minor adjustment to our
MoveLocal code. Instead of performing a LookAt at the goal (which is our cube that we're passing through),
we calculate a new goal position to look at, which will be at the same x and z position of the cube, as you can
see here,
but it's y-position is the same as our character.
Then we use this new position we've calculated (which I've called lookAtGoal and we use that inside
of the LookAt instead. So once you've updated your code, if you save that, go back to Unity and run it, you
can move your cube up in height (as you can see here, my cube's already floating) and it doesn't affect
the zombie character whatsoever.
So she's staying grounded and her z is therefore not being rotated in an upward direction.
It is staying parallel to the ground.
One last thing to complete our code is to have the zombie stop moving when it reaches the location,
so that you don't get the 'jitteriness'.
First of all, we'll need to add in an 'accuracy' value.
So this is very similar to the code that we've done before.
I'll set that to 1.0f.
in this case and you can adjust that later.
Then we want to test the accuracy before we do a Translate, so we'll still LookAt even if we're close
enough, but we won't move any closer to it. So 'if' and in this case, we don't have a direction.magnitude
to use as our distance to the goal because we've never calculated the direction, so this is a little
bit different to the previous code.
But what's really ice that comes with our vectors is that we can calculate the distance between 2 positions
and we can do that with Vector3.Distance
and we want to find the distance between our current location (which is transform.position) and
the goal position.
So goal.position
So that's your 'if' statement and in there we want to test for the distance being greater than our accuracy
which will allow us to keep moving forward,
otherwise it won't run that Translate.
So in working out this Vector3.Distance between these two positions, it is essentially
the same thing as finding the direction (or the vector) between those two values by taking one away
from the other and then using the .magnitude.
It's the same calculation, it's just different ways of working it out. So you could go in here and work
out the direction and get the magnitude of that vector, or you can use this distance function.
Right.
So save that and now we'll go back into Unity and run it...and drive around...let's drive a little bit closer.
Now we can still see, in this case, that we get 'jittering'.
The reason is that our current distance is calculating it between the distance of the actual cube
and the distance of the zombie,
but the zombie's not trying to get to the position of the cube, it's trying to get to the position that's
under the cube at the same height.
So it has very little hope (especially if this cube is right up in the air) for it being close
to it because it's not being allowed to move in an upward direction.
So we'll make that slight adjustment to our code, back in here. Instead of trying to get the distance between
our character and the goal position, which might not be possible because that goal position is up
in the air, then instead we just want to get from the LookAt.
So we'll put that in there, save it and go back and run.
Now when the zombie gets close enough to our cube, it will stop moving.
Of course the animation is still running, but we're not working with that at the moment and it doesn't matter
how high up in the air you move this and the actual distance between these two,
it's only concerned with the distance between itself and this position here that's at the same height.
So now I can drive this cube around and the zombie will still follow it but stay grounded...
and when we get close enough, it will stop.
That is how you move a character along its forward axis.