Hey and welcome back to the third person camera tutorials
and unity 3-D. In the last lesson,
we learned how to create a follow camera script by
using the characters forward vector and up vector to create
a target offset for our camera and smoothly translate to
that position. So as you can see here,
that's what we did last time. Uh This is
pretty good but as you can notice it's kind of
jerky. And the reason this is happening is because
our character is updated his position. Since he's a
physics object and has a rigid body, his position
is updated in the fixed update in order to give
values and update where we're actually moving this translation.
We need to go into the character and set his
uh rigid body to interpolate. So now as you
can see it's a lot smoother. So the difference
there is pretty strong. You can see that that's
quite a bit smoother. So a lot of people
just move their code into late update for camera but
it's not enough. You need to, you need
to set this to interpolate it as well to get
values that are accurate and update. Another big one
we can make is to add a anti antialiasing script
so that makes it look a lot smoother. And
these are standard assets that come with the pro version
of Unity. All right, cool. So it's
out there back in the editor and make sure that
they stick to our character. Great. Okay.
Next thing is, uh, if you look at
our, if you look at our camera script,
it's pretty good. But it seems if you look
in our editor view that as we hold left on
the stick, our camera is actually orbiting around our
player, Which isn't quite what we wanted. If
you remember from the Mario 64 and Zelda videos that
we showed in the introduction. Uh, the camera
actually orbited around the player, or sorry, the
player orbited around the camera here. We have the
camera orbiting around the player, which isn't quite right.
And it's kind of an unintuitive, especially as
you can see, I'm holding back on the stick
right now, my characters moving forward, so that's
not quite accurate. So how can we fix this ?
Let's have a look at how Zelda works.
Um, this is the Dolphin emulator and if you
go to the graphics mode, you can do this
little hack thing where you can do this free look
and position the camera in an arbitrary space. So
I moved it above Link. And if you see
if I was the target and then hold to the
left or to the right Link runs in a circle
which he's running in a circle of fixed distance from
our where our camera would be, which is probably
around in this area here where this grass has been
cut. And what's happening here is if you can
see, it's not totally intuitive just from looking at
it from the game view, but he's actually using
his forward run animation, so he's not actually running
left to right. Whereas our character uh if we
hold left or right, he pivots in that direction,
So as you can see, there's kind of
tilting to show that direction that's not happening here.
He's actually running forward as far as the player can
tell. So that's why it's a little hard to
detect that this is what's actually happening at first.
So let's reset the camera, have a look at
it. So, uh from this view you can
see it looks like he's running for it. But
he's definitely as you just saw running in circles and
that makes it a lot easier to run towards the
camera and to do a lot of other uh just
more, I guess user friendly camera movement. Now
, we have to look at that. Let's go
ahead and start coding it. So building off the
script we had last time, the main difference we
need to make is to take our control steak and
sort of superimposed it into our game world and make
the the control stick uh sort of in world space
. And the way they were going to do that
is using Qahtani irritation, which is sort of difficult
to understand at first. But I think with a
couple of debug vectors, it'll make a lot more
sense. So let's go ahead and start adding your
code. We're gonna make a new method that's going
to translate our stick to world space. And I'm
gonna copy in this method but it's a lot of
code but don't worry, we're gonna go through it
all. Okay, here's our new method. Let's
comment out everything that's doing anything and just have a
look at our debug vectors and we're just going to
look at The stick factor at 1st. So actually
we'll keep all these for now. They're probably fine
. So let's ignore all the code and just look
at the state direction and let's go ahead and call
this method every frame in our update. Mhm.
So what do you call it at the end and
just kind of pass punk values into it. What
does pass direction and direction will just create as a
new variable. Mhm. So create that as a
new All right, internal parameter. So we'll make
that a nice global variable of here. And we've
already got speed. So we're good there. So
you got speed and direction and horizontal and vertical.
So what we're gonna do is a here, we're
passing in these by reference passing in the game cam
, which we actually just set this, this is
a reference to our games camera. So it's out
of that variable as well. Okay, We'll make
that an inspector variable and we need to also pass
in this transform so we're passing the characters transform the
transform of our game camera and two variables that were
going to set in this method. We're going we're
not going to do anything with those variables. Is
just having a thing of the method will move this
somewhere else once we actually want to do something like
that. So now we've got this uh we want
to change the names of these variables actually, I
think it's better just to call them horizontal and vertical
. Just for clarity sake. H and V aren't
the greatest names for variables in the world and go
ahead and just add a comment that these are private
global variables only. And we do not want to
set these in the Inspector and all these serialize variables
. We definitely do want to set the Inspector.
All right. So we've got all this, um
, that looks good so far. We want to
set our game camera in this in India. We
have a bug. So direction speed, that is
another sterilized variable that's going to determine how fast you
want the character to turn, but we'll get to
that in a minute. So sorry about all the
code being added at once, but we'll definitely go
through it all in just a moment. Okay,
So now we've got this, Hopefully everything will compile
. Okay, cool. Everything compiled and we have
our new variables that appear here. Let's just drag
the game camera into this right here and let's just
run it and see what we get. So we've
got one debug right here, that's going to be
blue and it's going to be positioned on top of
our character. Let's play the game and let's free
this camera so we can actually look around. So
as we move the stick, we should be able
to see that. Get a little bit of salt
here just so we can see it a little better
. Okay, so let's run around for a minute
and just see what happens. So we've got a
vector that's corresponding to the way our characters moving just
for clarity. Let's set this to be just in
the positive Z direction. So we have a world
facing and positive Z. As you can see here
, the world is facing in this direction. Let's
turn on Ortho graphic just to make it even more
clear. So what's happening with this stick vector?
This is just our control, our controller map to
a vector and positioned in world space over top of
our character. So this is nice but we want
to actually make it in the direction that our cameras
facing and have those to be relative. So we're
going to need to use a couple methods to do
that. So that'll be our next step. So
we've got our stick factor in there and that code
is pretty simple. All you have to do is
just calculate Just like we did before you just take
back 2 3 horizontal and vertical and just create a
vector out of them. And then we just position
this over top of the character here. So these
are the only two lines of code that we actually
we're looking at right there. Now, we're passing
in the root so you want to grab the roots
forward and we want to position this vector. This
stick vector in relation to the way the cameras facing
. So you've got the camera direction here and the
way you get the direction the camera facing is just
called camera dot forward. And that's giving you this
blue vector right here. Great camera dot for it
. But we don't want to have any Y movement
in it. We just want its its value in
X and Z. Because we're translating our stick factor
, which you can see is just in is just
an X and Z. Since we just set it
right here, there's no Y value here. If
you want to kill the Y value in the camera
direction as well. So we want to have The
direction of the camera being the same plane, which
is the zero Y plane as our stick vector.
So we got that here. We just killed the
why. And now I want to use Qahtani in
from two rotation. And what this does is it
takes the camera direction and puts it in the same
vector space as vector 34 which is the vector space
that we have our camera stick in. So as
you can see here, it's pointing towards vector 3.4
, which is just the blue back world back there
, which you can see right here, This one
right here. So you can see as we just
push forward um that blue vector on top of our
character right here is pushing in the positive direction.
So we're gonna call that referential shift and we're going
to take our stick direction and multiply it by this
value that we got. And this multiplication sign isn't
a norm multiplication sign. It's the quaternary in multiplication
sign. And if you look in the unity docks
, you can actually find quarter million multiplication. Mhm
. Yeah. So here's the Quattrone in doc's page
. And if you see, we've got operator times
, this combines the two rotations. So we're taking
the rotation of our camera that we've put into this
space and we're multiplying it with our i with our
with our route direction and that's the direction of the
characters that is facing forwards. So let's have a
look at what this what this result is here.
So, we gotta move direction here that we just
said. So it's uncommon move direction and look at
what that vector appears to be. Right. So
let's put our camera back on top. Mhm.
So now let's remind gizmos here, too. Just
so you can see we have a vector that's in
relation to the camera. So as we move a
little bit to the right, I've got the stick
move just a little bit to the right. You
can see from the cameras, move just a little
bit to the right here, I'm moving just a
little bit to the left, as you can see
in this view is just a little bit to the
left of the camera. So the camera vectors this
purple one and our green one is the vector that
we're moving. So it's just keeping us an offset
from the camera based on the way they were pointing
the stick. So this is useful and you'll see
why in a second. Yeah, So I've got
this move direction vector. And what we want to
do is we want to check whether this move direction
is um when we're taking the cross product of it
, and what that means is we get a diagram
, alpha. So here's a diagram off of Wikipedia
. And as you can see if A is on
B is on the um depending on the type of
the coordinate system, whether B is on the left
side or the right side of A will determine whether
cross product is positive or negative. So that doesn't
seem immediately useful. But if you think about it
, we can tell if our characters moving right or
left whether this vector is um in relation to the
characters forward, if we're taking this factor and we're
moving it in one direction or the other, it
will show us what direction the character should be moving
. And this will give us an informed way to
change the direction of the character. So it's actually
have a look at that because it's hard to explain
in words, but it's very easy to just to
see. So we got this axis sign here.
So let's and its name that because it's going to
tell us what the sign of the angle between these
two these two factors should be. So make that
a red vector and we're going to disable the vectors
in the third person camera because I think we pretty
much understand what these two vectors are doing at this
point. Got a bug here. Um we did
not specify right now. Okay, so let's let's
have a look at what that cross product actually is
. We're taking the cross product of the characters forward
vector. And our our new green vector, which
is our controller shifted into the camera coordinate space.
So let's actually have a look at what the what
the root direction actually is doing, which is the
characters blue vector. It's therefore director. So if
you look at the character, this blue vector here
is is this route dot forward. So we're having
a look at this route dot forward in relation to
the stick on the controller put into camera space.
I know this is a lot to understand it once
, but I think you'll see it and how this
works in just a second. So here we go
. We've got let's go to top of you again
at 90 0. As you can see from this
behind the back view, if the green vector is
to the right of the purple vector, which is
the characters forward, you can see the cross product
which is red is facing down. If it moves
to the left, the cross product is facing up
, which means it's positive. You can see it
right here pretty well. The purple vector in the
green vector that you used to be looking at.
So green vector, which is the steak, is
on the left side of the characters forward have a
positive cross product. That means we need to turn
left. If it's to the right, that means
we have a negative cross product, we need to
turn right. This is almost like finding the direction
that the player is turning the steak and we want
to do here is like in Zelda, when you
turn the stick link, sort of adjusts his rotation
to match where you turn the stick to. So
you're running forward, you immediately turn the stick to
the left. It's a left turn. If you
keep turning the stick in a circle, you keep
turning left in relation to our link is facing.
So that's the idea behind this and that's how it's
different from the old version because the old version,
we're just setting the direction to the horizontal value of
the stick, which is basically just saying if the
sticks left run left, at the sticks, right
run right, which isn't what's actually happening. We're
running and adjusting to a certain angle. So we're
running to adjust to a left turn, we're running
to adjust to a right turn. It's a little
bit more complex, but it definitely feels better in
game. Okay, so let's actually have a look
at these values now. So we've got all this
logic here. We probably don't want to look at
this factor anymore. We determine what direction we need
to go. And now we can grab the angle
between these two vectors. So if we're looking in
the game, if we're looking in game we now
know what direction we need to turn based off of
this red cross product, left or right. And
we know the angle at which I guess the angle
shows the amount at which we need to turn.
So we need to turn about 45 degrees here,
about 90 degrees here, About 1 35° here and
so on. So we need to adjust to that
rotation and it's pretty easy to find the angle between
two vectors. So we just need to find the
angle between the purple vector and the green vector.
To do that. You just do, we've got
this value here called angle route to move. So
that's the amount that we want, the route to
move to adjust to an angle. And that angle
is just the difference between the root direction, which
is purple vector and move direction which is green victor
. So let's find the angle between those two.
And that'll give us the amount we want to turn
. The direction comes from the axis sign. So
we get the access sign which is the red vector
, showing us the direction we need to turn and
the amount when you deterrent. Just based off this
angle, That's pretty easy. And then to just
get it to be between left and right, we
just want to divide it by 180 which is a
hemisphere and that is it. And now we've got
this idea of having a speed to our direction and
all that means is we're multiplying the direction by something
which is going to make it just a bigger value
than it would normally be and that's just gonna make
them turn faster. Okay, cool. Let's feed
these values into our character control it. But right
now we're not doing anything, we're just training these
values here. Now, if we move it right
here, it's a big difference because we're now going
to delete our old speed calculation, we were just
doing the exact same thing. We've got speed out
here, passing by value is different, passing by
reference. Were passing by reference here, we're actually
taking whatever value we feed into this function and we're
going to modify it and return it back. So
this is just a handy way of being able to
, I guess pseudo returned to values. Uh This
is just a feature that's really great and languages like
C, sharp and C plus plus, so we're
going to do, we're going to send in the
direction and the speed. We're going to set them
in here and then we're going to send them to
our character controller. And instead of sending horizontal here
, you want to send direction. So let's give
let's have a look at what that is. Let's
go back into unity and hit play if you want
to. You can set a different value for this
direction speed, but About three I think is pretty
good. Let's have a look at what that changed
, looks like nothing. And the reason for this
is because we're still using this pretty mediocre camera that
always sits behind the characters back. And because the
camera always sits behind the characters back, it's going
to change. Uh this this referential shift that we
had in here. It's based on the camera forward
, That's always gonna be the same as the characters
forward. So that's not good. We want to
make a camera that can look at any face of
the player so this camera can look at links side
, his front is back in any side of the
character. Our camera can only look at our characters
back. How can we fix that? Well,
we need to go into our third person camera scripts
and we need to change what we're doing in late
update. So instead of calculating based off of just
this idea of moving back and moving up, let's
just calculate based off of moving a fixed distance from
the character, no matter what way he's facing.
So to do that, we want to have a
look direction. We're going to create a new variable
called Leuchter. No, that's gonna be the direction
that we're going to be looking at. We also
want to create a new variable called offset, going
to be the height from the top of the character
that we want to look at. It's just an
easy way of moving up from the character because right
now we're looking at his feet if you just set
a low value for transformed. So let's go into
this and we want to set our let's change the
name of this, it's really not what we're following
, we're following a transform. So I'm just gonna
change the name of this just to make it more
clear. Follow X form. And we want to
basically just probably move all this code down. We
want to set this character offset each, each frame
. What character offset is going to be is a
new variable that is going to just uh it's just
going to be a distance from the characters follow transform
, which is the speed plus some height. No
, I want to calculate the direction that the camera
is in relation to the player and to do that
, we're gonna use that look defector. So we
got looked her and we got the characters offset and
we want to subtract from it the position of the
camera. So this is going to give us the
vector and it's going to be from the character i
his top of his head or whatever to the camera
. We want to kill the Y. Value of
it and normalize it. And what this does is
it gives us the direction. Let's draw that ray
from where the camera is in that direction and we're
cool with that green. Let's see what would change
that made hard to see it. I'm not sure
if it drew it. One second. Mhm.
Okay. Okay, let's give this a test.
Okay. It's not drawing it right now, I
need to see what we have. Is our air
character offset minus to transform. Okay, that was
just a stupid mistake. I didn't save the file
, my bed there, so let's go ahead and
play it and there it is. So we have
our green vector right here from the camera, you
can see it poking out from right here. It
has no y value and it's facing the character.
So now we can use that vector to determine the
direction that we want to face the character instead of
always just facing from his back and uh up up
some degrees. So let's do that. We're going
to use a new value for target position. Here's
our old one grammar error in the comment there and
here's our new line of code and let's look at
how they're different. So our new one is going
to be we're going to be setting the target position
. And we want to smooth in a new function
. So we're going to create a new function that
just we can pass values too because we're gonna actually
want to do this multiple times. So we create
this new smooth position function. And instead of just
setting transformed opposition, we're going to do a smooth
position. Mhm. And instead of using these values
were just going to use the for our from value
, we're going to use this transformed opposition. So
we're gonna use the same thing and we're going to
Tween to target position and we need to decide how
to set that. That's going to be a new
variable. Let's create this new target position variable.
Mhm. Yeah. So target position is going to
be the offset plus the up times the distance up
. So that's the same. And we've got this
new look direction and a new distance away. This
was the old target position up here. As you
can see it's it's pretty similar. But we're actually
going to be doing instead of using follow X from
got forward times distance away. We're using something different
. Now we're using the look direction that we just
calculated. It's just the direction to the character rather
than just from his back. So that's handy.
We're gonna delete this and we can look at this
target position In the same way we looked at the
other one. Oh we've got some some bugs right
here, some errors in the code. I keep
saying bugs. Okay, so what's wrong here,
kim smooth. Okay. We got two new variables
that we need to be using and those two things
, our just some damping variables. This vector three
dot smooth, damp. It moves over a certain
damping time. It's just an alternative to to uh
vector$3. I think it looks a little bit
better. It still does the same idea of just
tweeting between two positions. But I think this is
a little bit more predictable because you can actually feed
at a time they should take. Okay, cool
, so that is no longer a problem. So
how does this look different? Okay, that's automatically
already. Pretty different. We have our camera that
can look at any direction of the players. Now
we can run towards the camera which we could not
do before. It's running a little slow. Mhm
. Minimizing dolphins seem to have helped a bit.
Okay, so now we can run uh and we're
not quite orbiting the camera, but we're definitely running
towards that angle. As you can see, the
purple vector is moving towards the green vector wherever I
position. The green vector, the character runs and
tries to reach that goal. So this is a
lot more like the behavior that we were looking for
, which was exemplified here, where we turned in
a direction and link moves to face that direction and
this lets us do things like continue to turn the
stick. This is something we couldn't do before and
now we can rotate in circles just based on turning
the stick, which is a lot nicer. So
yeah, that's that's the first step. So we
got one more thing we want to do and that's
this behavior of sort of rotating around the camera.
Because right now, if we just run run to
the right or left, we don't quite managed to
run in a circle around the camera. We run
in a curve but not quite, not quite a
circle and it's too wide of a circle. We
don't want to be just running straight in those directions
. So the way that we're going to do this
, they're going to do some work in fixed updating
controller logic. So I can't be some code into
fixed update. So what we're doing here and fixed
update is we're rotating the characters model if the stick
is Tillerson Writer left but only if the character is
moving in that direction and I'll show how this how
this changes if you don't actually put this condition in
and this supposes a new method that says it is
in locomotion and the way that we're going to check
if it's in locomotion is just query against the okay
the state machine, the mechanism state machine. And
look for a hash ID. We're going to set
that hash ID. Yeah create any variable for it
. Call it locomotion. I'd and as soon as
the game starts in the start my hood we just
want to store this and it's a lot faster to
look up these ints than it is to look up
these full strings. It's a lot faster Just for
performance reasons. So you typically want to do this
for best practice. I'm not sure if it's performance
a sensitive code, but it's definitely doesn't cost you
any extra time and it's actually provides nicer code because
you can just change this and then everywhere we'll use
this new variable. So I think it's just a
nice way to do things case we're missing rotation degrees
per second. So let's copy that variable in and
we need state info, which is just our animator
stated. Yeah. And we want to set state
info and each one of our update functions. Okay
, cool. So now we've done, we've got
a lot of code there. Let's make sure this
compiles great compiles. So what did we do here
? We added this new rotation degrees per second,
which is going to be how fast how many seconds
it takes for our character to rotate in a full
360 If you're holding the stick perfectly to the writer
left. So that's all that's doing. And we
added a couple of new functions here. We've got
this is in locomotion that just checks if the state
info, the current state info for the animation.
If that name hash is equal to our hash for
locomotion ID. And this right here just comes from
our state tree. So we've got our state tree
here and we have a base layer which is this
name right here, you're gonna want to use this
for here and the name of herbal entry which is
locomotion. So if we're in this country on this
layer then we don't want to uh they want to
say they were in locomotion. So what this function
and fix update does is it checks a friend locomotion
and then it grabs the rotation amount. So this
looks like a long line of code. Uh it
actually is a long line of code. And what
it's doing is it's tweeting between a rotation amount of
zero and irritation amount of the rotation degrees per second
. And that's based on the value of the horizontal
joystick. So if we're holding it all the way
to the right or left we're going to give it
a value of it is less than zero. I
want to give it a value of one. If
it's greater than zero we want to give it a
value of sorry if his lessons you want to give
value of negative one greater than you want to give
it a value of one. And we're just tweeting
between these two vectors. And we're doing that based
on the amount of the horizontal joystick. So if
we hold this all the way to the right,
this right here will return zero. Um it'll return
zero one zero. It will hold all the way
to the left will return 0-10. If we
let it go still it'll give you zero. And
that it will just give a zero vector vector 3.0
. So we're just doing this tweeting using this turn
every operator here between uh these these vectors right or
left. So that's the rotation amount. And this
is all done in the y axis. So we're
actually turning the characters Y axis, sort of like
sort of like turning a top. So like we're
turning link to the left like a top here and
we're turning link like a top to the right here
and that's just based off of how we're holding the
joystick. That's what that logic does. And then
delta rotation is a good attorney in that were forming
from these oiler angles that we just sit here And
we're normalizing it two times delta time and all that's
doing this time. Delta time is just making sure
that on every system he turns 120°/s and not if
you have a faster computer, he turns faster.
If you have a slower computer, he turns slower
. So this is pretty necessary right here. And
then finally, we just want to set the transformed
rotation equal to the product of the attorneys. And
we just talked about this earlier. Um, if
you look in the unity script reference, this is
adding the two rotations. So that's all that does
. And if you want to save this and have
a look at it. Game plays quite differently now
. So now if you run to the right,
he runs to the right, just like how he
wanted, like in Zola or Mario or insert any
Nintendo third person game here. So we're matching these
angles properly as you can see between the blue or
sorry, I'm colorblind the purple vector and the green
vector. And we've got this behavior of running right
and left when you hold all the way to the
right or left. And if you look at the
state tree when he's running right and left, you'll
notice that it's actually using the run forward animation,
which is what we wanted. But we're still turning
. So that was the tricky part there. And
you can see this cross product to this red vector
here. That's moving up and down. So that's
pretty handy. But what does this if statement for
right here? Like why do we have to say
all this stuff up here? So there isn't locomotion
makes sense. You only want to do this if
he's if he's moving. But what about this?
And right here, what this is saying is like
if the direction that the characters moving is right and
the direction of the stick is right, that's if
you're doing this, you want to do this turning
like a top behavior. But if the direction or
or if the direction of the stick is left and
the horizontal direction of the stick is left. So
if he's turning left and the stick is left,
you want to do this. If he's turning right
and the stick is turning right, you still want
to do it, but you don't want to do
it if he's turning left and the stick is going
right, because that will give this sort of fighting
behavior and I'll show you what that looks like just
so you can see what's happening there. This is
pretty, pretty fatal bug. It doesn't look very
good. So it looks fine here. And then
as I start turning around, you've got this sort
of fighting behavior and look, I'm holding all the
way to the right, but the character is turning
to the wrong direction and he can't quite make the
turn and it just doesn't feel very good. So
you definitely want to make sure that you have those
conditions in there because otherwise you get this sort of
unwanted behavior. So let's undo that. Save it
. I think probably what we want to lower the
camera a little bit too. It doesn't look quite
right yet. So let's have a look at our
main camera here. We've got our offset, got
our distances, got the value saved over here in
a text document that looked pretty good. I want
to distance away of 3.5 In a distance of 2.5
or sorry, 2.5 we could probably actually get rid
of the offset or the distance up. We probably
need them both. But hey there in there um
it doesn't look quite right still well you get the
idea, it works well. I think we just
need to kind of move this value up a little
bit, mainly away, a little bit. That
looks better. So yeah, you just gotta you
just gotta tweet these values until you get something you
think looks good. But there we go. We
got the behavior we wanted, we can do turns
the stick is working correctly and we can control the
character in three dimensions with just one stick. This
is pretty nice. It's a lot easier to navigate
these cubes now that we can face the camera.
Alright, thanks for watching. And I'll see you
in the next lesson.