Welcome to 3rd Person
Camera Control! This is going to get fun! So, by the end of the video,
we will have a rudimentary but error prone
camera rotation system. We'll be able to move around the tank, we'll have some glitches, we'll have a common problem with
the camera ending up rotating like this, the horizon ending up not
horizontal, but this is good, this is a step towards the camera control we want. And because
of the flexibility of blueprint, we're going to be able to fix the hierarchy,
fix what we need, and swap a couple of elements out really quickly, after the video.
But for now, I want you to get this initial thing setup, a very simple system
and we will have camera control. So, let's go and see how it works!
So, let's take a look at what's happening with the camera. I'm going to get the World Outliner up,
play the game, and see what's happening. If we de-possess, where's our camera? Well,
try at the origin of the tank, right underneath it. Where is it in
the world here? Do we have a camera in the world? We have a Player
CameraManager, and you can see that's where it is there. And it's a thing of type
PlayerCameraManager. OK, so what we want to do is we want to attach a
camera to the blueprint, the tank blueprint. So, let's go ahead and do that together
now. Open up your tank blueprint. And the naive way to do this, and we'll start by doing
this, is just to go and Add a Component, add a camera.
Then, we move the camera and position it up and behind the tank.
This is basically fine but there are some issues with it, which we'll see in a moment,
because it's going to be hard to rotate the camera, and also the camera's not going to
particularly, nicely follow the tank with a kind of
springy springy feel to it. But let's just see what this is like, if there is a camera
attached to the tank and then we play. You see what's happening? Now, it's found a
camera, so... Oops, excuse me! So, there we go, we're where we want to be
up and behind the tank. But we want some way of moving
the camera around. So, how are we going to do that? Well,
we can't just rotate the camera right, because that would do something really strange, if you think
about it. If we had some controls that just rotate the camera, we'd just be looking away from the tank.
We can indeed just move the tank, but we
don't want to rotate the tank randomly, because the tank rotation should be based on it's
movement right? So, we're going to have to
put the camera on an arm, and that's something called a spring arm,
and then rotate that spring arm. So, that's what we're going to be doing here!
So, let's start by adding a spring arm. Camera,
SpringArm; it's designed for cameras. We can just call it a
SpringArm, that's fine, but we want to child the Camera to it. So now, what's happening is you've
got this virtual SpringArm in the world, and if you rotate that, you see how the Camera goes
with it? OK, so let's just play the game and see what's happening with it
on the SpringArm. It'd be very similar. OK, that's fine! It's pointing a little bit
at the ground, so maybe come back a click.
Great! OK! Still not quite pointing in the right place.
So, there's two angles, or course, here. You've got how kind of high the camera
is, based on the rotation of the SpringArm. Imagine a line going from
the center of the tank out to the camera, but also you're still free to rotate the camera itself.
So just fiddle around with it, until you get a reasonable view. Something
like that isn't far off. OK, we can tweak that later.
But importantly now, what we want to be able to do is, we want to be able to rotate and move
this SpringArm, so when we look around, we want the camera to orbit around
the tank like that. So, we're going to setup this input
binding now. We're going to do it in Blueprint. Don't worry, this section have over 500 lines of juicy
C++ code to get into, but this is very simple. We're just going to
take an input axis and do a very simple manipulation of it, and
we're going to be referencing this SpringArm or perhaps a SpringArm container. We'll see in a moment.
So, because we're referencing input and we're referencing an actual asset in
the game, they're both candidates for using Blueprint. There's no point in
going from Blueprint to C++, back out to Blueprint. Let's just keep it all in Blueprint, in this case. So, let's
take ourselves to the Event Graph of the tank Blueprint.
By the way, the Event Graph is just one of potentially many Event Graphs.
So, here we have MyBlueprint tab, if you can find the MyBlueprint tab. If you
can't, then you can have a look on the Window menu, and you'll find MyBlueprint
there. And this MyBlueprint tab will show you all sorts of things, which we'll come to
discover in this section, but one of them is, the Event Graph is not the only place that we can add Blueprint
code, actual execution flow code,
to our tank. We could add another graph, so let's do that! Let's add another
graph called InputSetup, and that will work exactly like the Event Graph. In fact, it is an
Event Graph. It's just that you can't rename EventGraph; there has to be one thing called EventGraph,
and you can have as many other Event Graphs as you like.
The only thing is, you can't have a begin play on both, otherwise it wouldn't know where to start.
But we're not dealing with begin play, so let's put all our input into a separate graph from the
word go, just to organize them basically, just as an organizational tool.
Cool! So, I'm going to go ahead and do a tiny bit of input binding now. So we
go to our ProjectSettings, however you want to get to it. One way is the toolbar, ProjectSettings.
Just hide that tab, give myself some more room. Let's start setting
up some input. What have we already got input-wise? Nothing, we've got no
Action, no Axis. So your first question is, is this an Action or an Axis?
Let's see if we're going to move the camera. Well, it's an Axis isn't it? We're going to use say the right stick on a
gamepad or the mouse or something like that. So, let's start off
just with the mouse. Now, what are we going to call these
axes? Well, I'm going to call it AimAzimuth.
Now, what is AimAzimuth?
Well, if you pop over to Wikipedia and look for Horizontal Coordinate System, there
is a link in your notes. Then, we have Azimuth and
Altitude, or in the case of the tank, I'd prefer to call it Azimuth and Elevation.
That is what we are moving the camera in. Azimuth
means moving it around horizontally, looking at a different part of the sky
horizontally, and Elevation or Altitude means looking up and down. So, we're going to use an Azimuth
and Elevation model. OK, so we have Aim
Azimuth. Now, I'm not going to add the other one, this is going to be part of your challenge, but I am going to start
binding it to a couple of different things, I think. We're going to have the Mouse X axis,
and we're also going to have the... No, that's it actually. No no, actually
I'm going to use the Gamepad Right Thumbstick X-Axis. I'm going to give myself two
options, from the word. Now the scaling, this is something that the user may use to
control the sensitivity of their controls. We don't generally
want to change that. We want to set it at a sensible value, but we don't tweak it here.
This is really for a user to tweak later. Alright, so
we have two things that are leading to AimAzimuth.
OK great! Now, let's go to the tank blueprint, and
let's go find Azimuth. There you go! AimAzimuth, it's an event.
So, when that AimAzimuth input is triggered,
what are we going to do? Well, we're going to add a local rotation, so come
off the execution pin
and look for local rotation. Add
ActorLocalRotation, or just AddLocal
Rotation to the SpringArm. Here you go!
So, we're going to AddLocalRotation to a component, not the actor itself, because that's the
tank. Remember we're on the tank blueprint. So, out of all of these options, it's not
the actor because it's not the tank, it is the SpringArm. We're going to add a LocalRotation to the Spring
Arm. You see it's found the reference to the SpringArm, which is quite helpful for us,
OK. And then, we need to decide well, how much rotation do we
give to the SpringArm? Well, it's pretty much just
this axis value. The only problem with just piping this axis value in
is that it would then depend on the frame rate, right? Because in a particular
frame we would rotate a different amount, depending on how long the frame is. So, what we're going to
do is, we're going to get the World Delta Seconds. Get World Delta Seconds, that's going to
output how long the last frame was. We're going to multiply
so there's a float times float,
multiplication there. The Axis Value...
Oh now, I don't want to cross the nodes. It just looks ugly, so let's just mess around with that.
We're going to multiply the Axis Value that's coming out between 0 and 1,
with how long the previous frame was, and use that for our rotation.
But we need to split this struct, so if you just
right-click on Delta Rotation, you can
Split Struct Pin. And that allows us to pipe this into
the Yaw right? Azimuth is Yaw, in airplane
terms. So, I just stop all of the crossing of the nodes. It doesn't matter functionally, of course, these
nodes crossing, it just matters aesthetically. And that's
it, that is how we would setup the input control for the
axis. Let's try it! Let's compile the blueprint, come into the game, and
try just moving our mouse. Now, it is working, it's
just working very slowly, alright. So, how do we sort that out?
Well, that multiply pin here has the ability to
add another pin. We do that, and we put a fixed
100 in here. For example, so we've go the axis value
times how long the frame was times 100. Let's try that! We're just
increasing the speed. Now, the mouse X axis does exactly what we. It'll be
a bit glitchy, but we'll worry about this later. OK, and then the
right axis of my gamepad also does the same
thing. That's cool! So, I wonder why that's
snapping like that? We're getting a sudden snapping of our camera position.
Well, we'll take a look at that in the next
video, but what I want you to do now is a challenge. I want you to setup the AimElevation
for me. So, I want you to bind the AimElevation in blueprint
for me. Use the AimAzimuth as a template, setup the corresponding
binding for the elevation, and just test that it all works and make sure you really
understood what I just told you. So go ahead, pause the video, and work the same magic for the elevation
right now.
Welcome back! Let's take a look at how to make this work! So of course, the first thing that
we need to do is go into our Project Settings, into Input,
and then, we need to come down to the Axis Mappings and give ourselves a
similar one AimElevation, oops, with a few extra
letters there. [Laughs] It almost looks Scandinavian in its spelling, just then.
So, Mouse Y is one of our options, and Gamepad
Right Thumbstick Y Axis
is what we want. There you go! So, now we have an AimElevation. Remember copy and paste
is the safest way of using this, although we don't need to in the case of Blueprint
because we're going to reference it directly like this. Well, still paste! AimElevation,
make sure I'm getting the right thing. It's the event we want, not a function,
and then, we are simply going to do exactly the same thing. So
basically, because we're doing exactly the same thing, we can come off the Elevation
node, we can look for LocalRotation, AddLocalRotation to the
SpringArm. AddLocalRotation
to the SpringArm. That gives us most of what we wanted!
These nodes, we can just copy and paste.
Wire things up. Now split,
we need to split the struct here. Now,
which one did you opt to put it into? If we're doing Elevation, what are you doing? Are you pitching, yawing,
or rolling the camera? I think we're pitching the camera! We've still got that
100 multiplier, which should be appropriate. It should give us
a similar effect. Collapse that node down a little bit,
and I'll tidy this blueprint up at the end. Let's make sure it works before we tidy up, so I'll stop
tidying it. I'm obsessively tidying it! Come to the battleground, play the game,
and then, look the camera is moving around but its doing something really
strange OK and the reason for that
is because we need yet another level
of indirection here. We need to, not just be
rotating the SpringArm, because the rotations
are compounding. If we pan the SpringArm or move the SpringArm
in Azimuth and then elevate it and then move it in Azimuth again, then what we're ending up
with is we're ending up with the wrong end
result. So, let me show you that in action! Here's the SpringArm, for instance. If I take the Spring
Arm up like that and then around and then down, it's
going to start twisting. So, let's see how this looks! You
see how it's twisted? The horizon is no longer steady, and that's just
because of the way these rotations are adding together. They don't add together
in a way that is independent of the order you've done them in, with rotations of
course. So, what we want to do then is, we want to have
another object, and we'll put another object, a SpringArm container in
between in the next video. So, I'm just going to try and return my camera to where it
was. I think it was there? Let's try that
and make sure that we start in a sensible position. Yeah, and I'm going to accept that a
combination of movements is going to end up with this in a really strange position. So, we'll see
how to sort that out next, but at least, we have the bindings that we need. I'm going to just
go to the Input Setup tab again, tidy up my blueprint. I think that's a good habit,
once you know that it works. Don't waste your time beforehand, if you can possibly help it.
I know if you're tidy-minded like me you won't be able to help it, and if you're not tidy-minded, then you'll wonder what
I'm spending all the time on. And what I'm going to do here is, I'm going to highlight all
the nodes and push the C key, C for Comment on the keyboard, and I'm just
going to say exactly what this is. This is Camera Input
Binding, and then, that's it, we are done!
Let's save everything! Remember, to be using source controller.
I haven't showed you any commits going on recently, but I am,
once I've saved everything, going over to my Source Control System, or course, and then committing
using a commit name that matches the video name. And do remember also
that you can get to all of this code over on github.com forward slash
bentristem. You can navigate through to the Unreal Course, the
Battle Tank section, or you could directly go to github.com forwardslash UnrealCourse. And
then, you can choose the stick on the master branch, and if you look at the 14 commits,
you'll see that each one of these corresponds to a video. For each of those,
you could choose to go to that commit, and you would be able to download the files
if you want to. You can browse them, download them, clone them, whatever you like.
So you can get to the end state of every single video in the course, which is pretty handy.
Cool! So, I'm going to go ahead and commit this now, and I'll see you in the next video!