So, in this video we're going
to be Creating an AI Controller Class! By the end of it, you'll have a
couple of different AI tanks placed in the scene, and when we play the game, your player will
spawn in, at the player start point. And in the logs, if you look down in the
Output Log at the bottom of the screen, you'll see that we'll be reporting that the AI
Controller, which we'll be creating, is possessing a couple of the tanks and that we
are possessing a third. So good news, we'll see you in there!
So, before we go any deeper into the aiming architecture, let's take a look
at the basics of the Tank AI Controller. So, that's
kind of analogous to the Tank Player Controller. The players us will possess the, well
the tank that is spawned at the player start point, the rest of the tanks the AI Control
tanks we'll be placing into the game, well this game anyway, at design time.
We'll just drop the tanks in. Of course, they could be spawned in as well, at play time.
So, let's take a look at how that's going to work! The first thing we're going to need
is a Tank AI Controller class. Now, this is a little bit different. We're going to do this,
obviously C++ Classes, Add New, New C++ Class.
So, go ahead and do that, but you won't find it here, You won't find the AI
Controller in this standard list, so you need to click Show All Classes in order to find
it. And we're looking for AI Controller, which is a child
of Controller, just like the PlayerController was. So, we have Actor, Controller,
and then, under that there's PlayerController and AIController. So, we're going to go for a new
C++ class, based on the AI Controller, and we're going to extend it's
functionality. Cool! What are we going to call it? Well, let's be conventional.
Let's go TankAIController,
leaving the Public checkbox ticked. For now, we're going to do that for the entire section, just
while you get used to just the fact that it separates your files into public and private
and create your class. And let's see what
goods it comes up with. So, what I'm going to be showing you, in this video, is how to,
as I said at the beginning, how to make an AI Controller, which we're doing now.
Once that's done, we're going to look at how to assign that AI Controller to
the... Just do a recompile. I don't know why it wants to recompile, let's see what happens.
We're going to assign the AI Controller to the tank, and then, we're going to get you
logging out to show that possession has happened. So, I've got a
file compilation error. Let's have a look! C++ 25.
Let's see what my problem is here. I've got a Z on the end of a line!
So, just to help you out, by the way, if you see something like "unexpected end-of-file", then
look out for dodgy stuff (laughs), like a missing character. So, I had a
Z on the end, it didn't like that. Let's go ahead and recompile. So yeah, we're making an AI
Controller class, we're going to tell the tank what it's AI Controller class is,
and then, I'll get you to do the logging that we did on the PlayerController, over here on the AI
Controller, so that all tanks report in on the log for us, to say that they have been
appropriately possessed. So the game will play now, the PlayerController is possessing Tank_
BP_C_44, which is the tank right in front of me. The other tank in the
scene is not possessed by an AI Controller yet. You may ask, how on earth do you do that?
Well, let me show you! We've got one tank, remember one unified tank for both us and
the PlayerController. So, it's going to be on the tank that we set this, so here's
the Tank blueprint, and we're not really interested in the input setup or the
Event Graph. In fact, just to throw you off the scent, I'll go back to the construction script,
and I'm going to expand out the details panel of the Tank blueprint. This is where we want to
go, right! So, do we Auto Possess the player in
general? No, not in general we do not, we leave
that as it is. Do we Auto Possess the AI with an
AI? So, this is what it's really saying. Do we Auto Possess with the Player?
Well no, not all the tanks, only one tank.
Only the one take that's right in front of us, and that's already working, that's already been Auto Possessed
with the player, because, just to remind you why, because the
game mode blueprint tells it to here.Tells it that this
is the PlayerController class that, that this is the tank. Alright, so
Oh, I just closed Unreal, that wasn't very cleaver! So, what's the next thing? The next thing
is, if I tank's in the world and it's not being possessed by the player,
it's going to be possessed by the AI. So, that's what we're doing, we're telling the
Tank blueprint that we want
Actors that are placed in pawns or Actors or Tanks that are placed
in the world, to be possessed by the AI. The final thing we have to do is tell it
which class, not just a standard AI Controller but with the Tank
AIController. That's the thing that's going to do it. Later on, we're going to go the
other way. This time, we're going to make a Blueprint class later, and then, if we need to
and then change this to point to the Blueprint class, but we don't need the blueprint
right now. We're only going to need the blueprint if we have some parameters we want to tweak or
some assets we want to reference, but for now, we can do this in pure C++.
Previously, in the PlayerController, we created the Blueprint class first, and than when we
needed it, we added a C++ class and reparented it. In this case, we created
a C++ class first, and if we need it, we'll create a Blueprint
class later. Cool! So, there we have pointed it to the AIController class!
Let's compile and play the game! Now, another way
to get out of the game, apart from F8 to de-possess, is Shift F1. That
will give you your mouse back. Alright, now you can then go into the World Outliner
and we can have a little look at what's going on. We've got Tank_BP20,
we have Tank_BP, so there's a couple of tanks in the world. In fact, I will de-possess with
F8, then I can select tanks. Tank_BP20 is possessed by the TankPlayerController.
How about this guy over here, this other tank, Tank_BP?
Does he have an AI Controller on him yet? Yes he does look, TankAI
Controller. You see that right above it?! So we have an AI Controller possessing
that tank. That is pretty cool! So, I have a challenge for you,
now that we've done that! What I want you to do is to log the possessed tank for the
AI Controller. Use similar code to what we did in the previous video on our
PlayerController, this'll make sure that you understood it all. Try placing some more
tanks in the level, once you've got it working for one, and what you should find is that you get a log
entry for each tank, saying that an AI Controller is possessed and then the
tank designator, the tank ID. Alright, go ahead pause the video and see if you can make
the same type of logging work for the AI Controller now.
Awesome, welcome back! Well, I said
that it's going to be similar. I hope it's going to be very similar to what we did in the Player
Controller. So, in the PlayerController we needed a BeginPlay to do this,
so I think that in the AI Controller we're going to need something very similar. So, let's go ahead
into the ATankAIController header file and give ourselves a begin playback just
like that. Now, how do I know that's going to work? Well actually, the
inheritance for the controller is exactly the same isn't it? If we
look the Class Viewer and we go to controller,
of course. Sorry Mac people, you can't do this, but PC people can now turn off
Actors Only. We go Controller, AIController, but you'll remember
that you've also got PlayerController see, both under Controller. So, it's got the same inheritance,
they're both children of Actor, so therefore we can go ahead and say
override the BeingPlay(). Cool! Well, let's also take a leaf out of
the book of the PlayerController for the rest of this. In fact,
we can pretty much do exactly the
same thing. Let's go ahead and do that, TankAIController.cpp.
By the way, from the head file, you can right-click and say Toggle Header / Code File in Visual
Studio. It'll take you right there. So, with that code pasted in,
hoping that we can do things exactly the same way, we just need to change PlayerController to AI
Controller.
Let's see what happens! Let's go ahead back to Unreal, see if we can
compile that! Now, I've heavy handedly
cut and pasted it. In fact, I've so heavy handedly cut and pasted it, I know it's not going to work,
because look. I'm saying AIPlayer
Controller when I should be saying AIController here, so I know that that compile is going to fail.
That was too hasty! Let's try again!
So obviously, when you cut and paste
code from one class to the other, because of the nuances of C++ it fully qualifies the
class names, and that's not going to work. So, I've still got a problem, GetControlledTank is not defined,
and that's simply because I've used this GetControlledTank method here but
I never declared it in the header file, so we'll do that as well.
We'll have a GetControlledTank on both, at least for now, we may not end up with those at the end.
There's your getter and your constant
getter. Let's try again! So, this method of just compiling and
compiling until something runs is fine when your code base is small and the
compilation is fast, but if it's not, then it's not a great strategy
really long term. You probably want to look at your code a little bit more carefully. But I'm just going to go
through this way of going it for you for now, just for run. So, on the 18 I'm
missing a semicolon, and that's
because I'm not hash including tank here.
OK, strange error for that isn't it? It just doesn't understand ATank*.
It's good to see a variety of different errors, I think, because you're going to get them when you
write your won code, and if I just give you totally clinical clean examples, then
I think that it's going to be detrimental to the learning. So, there you
go now! Let's stop the game and play it! Oh, look at that, "AI
Controller not possessing a tank", "PlayerController not possessing a tank". So, that
old chestnut! The first thing I'm going to check is that the Tank blueprint... You see the
parent class has gone back again. I'm not sure what's going on with my machine here, why it keeps
going back, but I'm going to patiently set it once more back to tank. I don't think you'll be
having this problem. I'm going to recompile and if it still doesn't
save it for some reason I'll save it out, then I'll look at a solution, but let's
just go ahead. Ah there you go! AIController possessing one tank, the other
controller is possessing the other. Let's just check those ID's against the Content Browser,
the World Outliner rather. These guys go together and
then the TankAIController5
is possessing Tank_BP there. So yeah, you have to hover
over to be sure, and really to be sure, you have to actually depossess
with F8, click the tank to be sure that it's Tank_BP 6,
hover over it in the World Outliner. It shows you that it's BP
underscore 569, and then, you can see that the AIController is indeed possessing the
correct one. OK, so let's drop another tank in just to finish off
that challenge. Now, this gets really easy, because we've got a Tank blueprint. We can just drop another
one in the world there, click play. I'm just going to clear this log, right-click,
Clear Log. Now you get two messages, two AIControllers possessing
two different tanks, and the PlayerController possessing the one in the foreground. Again to check
the suggestion, you F8 to come out, click on the tank,
go to the World Outliner, hover over it, it's designated 215.
In this case, it's actually in the hierarchy in the World Outliner as well, whereas these guys you need to
hover to see, and you can see that all the right things are being possessed by the
right PlayerController. So, that is absolute triumph! Congratulations, you
now know how to place things in the world and have them possessed by AI or by the player,
and let's continue this exciting journey towards a working tank game!