So, welcome to
Creating an Out Parameter Method! In this video you're actually going to create a method that
uses out parameters yourself. It's going to return a boolean, but actually, it's going to
take the state of something called hit location. So, it's going to use out parameters, and that will give you a much
deeper understanding. So, please take the time to do the challenge in this one and spend a good hour on it,
if you're a beginner, but if you get stuck after that, then do just keep watching.
OK, let's take a look at this AimTowardsCrosshair()
a little bit more closely. I don't think I explained in the last video that the reason we are
checking that we have a Controlled Tank is, it doesn't make any sense to start aiming towards the
crosshair on the screen unless we're controlling a tank. So, what we need to do
now is what it says in the pseudocode, and I want to use an out parameter for this, because the
engine uses out parameters a lot and I want you to get used to not only using them but
actually creating methods without parameters yourself. And that's going to be your challenge
for this video, but for now, let me explain how I plan on building it up. So, we're going
to make ourselves an FVector, and you may as well follow me along with this, for the moment, and
it's called HitLocation. And it's going to be an
Out parameter. Now, I'm going to stop doing the whole hash define out
at the top, because that's not common in the engine code, and you need to
start getting your eye in, as it were, start getting used to seeing Out parameters
when they exist. So, we're going to have this
FVector HitLocation rather. Alright,
now what I want to do is, I want to print this out
to start with. So, let's just go ahead and UE_
LOG this, and it should just be a 0 vector to the (LogTemp)
with Warning verbosity, using the TEXT
sub-macro inside a macro. [Laughs] And we're going to say now,
I'm just going to say the following. I'm going to say (HitLocation.)...
You'll see there's a ToString. Why is it a dot rather than an arrow? Because it's an actual
variable here, it's an actual member, it's not a pointer. Dot
ToString() and that's a method, so check your brackets are matched. So, that bracket there,
if you select it, is matched with this one which is good. And let's go ahead and
see if we at least on every tick, because AimTowardsCrosshair() is called, remember from
Tick, we should be getting a 0 vector logged out. Now, there is a deliberate mistake here, by the way.
This will not compile but it would be a very easy and innocent thing for you to
do here right? And you might think that it's because we're missing a start, but let's
just see what the error messages are for the learning. " 'LHitLocation' : undeclared identifier",
" 'ToString' must have a" something or other, " 'unknown-type' ", and "function
doesn't take 4 arguments". So, the first thing you may try is putting a star, going oh yeah, that old
chestnut. This thing needs to be converted to a
TCHAR *, so I'm going to put a star in front of it. So yeah, you can
try that! Let's see what we get not! It'll get rid of some of the errors, I think, but I don't think it'll get rid of
all of them. And then, we'll look a bit more closely, because this is something you may
well come up against, and it would frustrate you and I want to help you. So we
have here, the log "function does not take 4 arguments", OK.
And the reason for that is, it's interpreting...
Whatever this TEXT macro is doing, it's interpreting the result as two
separate arguments. So, the solution to this type of thing is actually to use the
format operator, it's to do something useful. Say TEXT
and then say (" *HitLocation:
and then you %s" or whatever. Well it needs to be %s
for string. Close your braces and then the comma for the HitLocation.
Now, check your bracket matching, and now I think that this will work,
because actually, this whole thing will be considered just that third
parameter. I know that's really strange with where all the commas are. You pretty much
just have to remember this, because we don't want to go and dissect exactly how all these macros
work. But the UE_LOG macro expects three parameters, one, two, three,
and this third thing I have highlighted on the screen is itself just a parameter
in aggregate. And let's try that, I think that'll work, and it'll also be more useful
because it'll log hit location, rather than just logging some random thing to a console.
It's always worth labeling what you're logging, so you know what you're looking at.
So that did work, and if we play now we get hit location [Laughs] some massive
number, because it was never initialized and then 00.
So normally you would initialize your variables, but as we're going to use this as an out parameter
I'm actually not going to bother. OK so, so far we've done pretty much nothing
apart from learning a little bit more about the necessity of using these format
operators or the trick in here. So, what we're going to do is we're going to "//Get the world location
of"... Oh, not if but "of the linetrace through the crosshair". What that means
is, when you're playing the game and the crosshair's up in the sky it's probably going to hit nothing, but if it
hits the terrain, we want to know it's hitting the terrain; and then we want to return the
world space location of where it's hit, so that the aiming system can now aim at that mountainside.
That's what we're aiming to do, [Laughs] excuse the pun there!
What I want to do is, I want to make a method that does all this in one go,
and that method's going to be called GetSightRayHitsLocation(). And I actually want
you to write the outline of that method, to take in an out parameter,
and the out parameter is this HitLocation. So, you may have to stop and think about this or revisit
a previous section, depending on how advanced you are. So, I want you to write Get
SightRayHitLocation(). It's going to return a bool, and that's going to be true
if it hits anything and false if it doesn't. Now listen, I don't want you to fully implement necessarily
this method, I just want you to write the signature of it and get it laid out. So, it's going
to return a bool, and it's going to be a constant method, because it's just going to
return an out parameter, it's not going to mutate any of the member functions of
this tank player controller. We want to return a
FVector called HitLocation as an out parameter, but remember you need to use
an ampersand somewhere. The reference or is it address of
operator, depending on whether it's a prefix or a post fix. Remember about two thirds of the way
through, I think, Building Escape, we talk about this in references and pointers, the lecture.
As a bonus, you could pseudocode the actual functionality of this GetSightRayHit
Location() method. It's purpose is to ray cast through the dot as
far as we're concerned, into the landscape and determine whether we hit anything. If
we do we return true, and as an out parameter, we return where in the world we've hit,
and if we don't hit anything we return false. Alright, and as a super bonus you
get it working but that's a bit much, and if it works then your log will actually
continuously update with the location of where that crosshair
is aiming at. But as I said, that's super advanced and really only try it if you want an extra challenge.
That's just for the more advanced people. So go ahead, pause the video and have a go at all that now!
Do have a go at this
one. It's going to take just a tiny bit of thinking about, but you've used out parameters before, so go and see
where they were used and see if you can remember how to do it.
OK welcome back! So, here in the
PlayerController we're going to think whether we want public or private, for a start. Well, I'm going to
suggest it's going to be private, because no other method outside this needs it.
It's a helper method. So, we're going to
"// Return an OUT parameter," and it's
going to be called "true if hit landscape".
Alright, now it's bool. I said I wanted it to return bool, we said it's going to be called
GetSightRayHitLocation(),
and we said that it was going to
mutate or change an (FVector) and that that FVector was going to
be called HitLocation) and that the
method was going to be const. But there's one more thing, out parameters work
by passing references to, and in this case it's going to be
passing a reference to an FVector. This will make a little more sense in a minute,
when we see it in context in the implementation. So, I'm going to copy that signature,
put it straight in here. Of course, I need the class name
like so.
OK, long old method definition right?
So, or signature as you call it! This thing here is the signature. So,
what's actually happening when I pass this in? Well, let's make the call. We're
going to go
if (GetSight
RayHitLocation
passing in (HitLocation))
as the thing that's going to be used as the out parameter.
Then, all of this lot, like
so. OK so, the thing about this if (GetSight
RayHitLocation(HitLocation)) is it actually does anything. It has a side effect, it doesn't change
anything, but it "// Has a sort of "side-effect"" which is that it's
actually going to ray trace
later on when we implement that OK. And if that's true,
then we're going to log it out. So, this "// Get world location of line trace", that's going to be
implemented in here.
"// If it hits the landscape" return true. Well we know that.
I actually better put that in as a comment, I think.
The FVector ampersand reference is saying
that we're going to create a variable here called HitLocation,
we're going to pass what looks like (HitLocation) itself into GetSightRayHitLocation, but it's
actually a reference to that. And then, somewhere later in our code,
we're going to change the value of hit location. So, I don't like this architecture
but you'll see it all over the engine and you do need to make sure you know how that works.
Leave this as a TODO, because later we're going to "tell the controlled tank to aim at this point".
All, at the moment, we wanted to do is get you this far, that we actually setup
an out parameter. So, in
the next video we're going to look and see what actual values
we're going to put into this thing. But for now, I'm going to return false to say i.e. we haven't hit anything,
and I'm going to return a, I'm going to just set HitLocation
to something recognizable.
FVector(1.0); let's
say. And what that's going to do is that's going to return an FVector that's 1 1 1
and then we'll be able to recognize that. So, we should go through here, this should go to true,
and then, we should log the hit location and it should be 1 and that proves that all our code is
executing properly. And then, in the next video, we'll talk about
how GetSightRayHitLocation() is actually going to work in practice!
It'll be a good further exercise! So, I hope you got on well with that reference challenge and you were
able to make this out parameter based method.
If this is all sounding gobbley goop to you, then just stick with it, you'll get used to it.
You'll get used to the idea here. I really want you to be able to read engine code
and real read code written by other people, so we're kind of dragging you through the mud
a little bit. So, let's have a look! That all looks OK
apart from we're not getting anything come out every hit, so it's obviously
(HitLocation)... Ah, I return false, I need to return
true. So, what happened there is I was returning false, so this wasn't evaluating
so it was just jumping straight out. So, we need to return true, i.e. we're pretending
we've hit something and we're pretending we've hit something at a location of 1 1 1.
So, I'm playing the game now, it's compiling in the background. When the hot reload
completes, I expect the log to fill up with exactly that, HitLocation
1 1 1. Great, so we are a step further through our process
of setting up the whole architecture. Before we go, let's just remind ourselves
what that whole aiming architecture was. I'll go through the slides and show you that one.
Remember what we're trying to do here ultimately is, we're trying
to have a Tank... Whoops let me go back!
...a Tank possessed by either an AI or a Player. The AI
was going to tell it where to aim or the Player is going to tell it where to aim. Actually, it's the same AimAt() method,
and then, that tank will delegate out the aiming to an aiming component, which we haven't made yet,
which will finally tell the blueprint to do some actual
rotations of turret and barrel respectively. So, it's a long odd haul getting
this working. This is the second most complicated thing we're going to do in this section. The most complicated
will be the actual movement of the tank, which of course we'll be game to do the moment we
can actually aim our tank. So, anyway that's it! I hope that's all made sense to you. I'll leave
the code on the screen, I think that might be helpful to you, so that you can see what we've actually
written here. And that's it, I shall see you in the
next video! Remember to commit your work to source control!