Welcome back.
Now we're adding our widget to the viewport from the HUD.
But we need to give this widget a widget controller.
And as we've seen our widget controller which we called or a widget controller has four key variables
player controller, player, state ability, system component, and attribute set.
Which means if we're going to create a widget controller, we should be setting these variables.
So I'd like an easy way to set these four key variables.
I'd like to create a struct that has these four key variables in it.
And that will make it easy to initialize any given widget controller.
So I'm going to make that struct here in the widget controller class, or a widget controller right
here up at the top just under our forward declarations.
So I'll make a new struct and I'm going to call this F widget controller params f widget controller
params can be a u struct.
So we'll give it a use struct macro.
And I'm going to make it blueprint type just in case.
We need to create one of these and use this type in blueprint.
And I'll give it a generated body as u structs need that, and I'll go ahead and make a default constructor
for it, an empty one with an empty body.
And I'll make one that can receive the four key variables these four here.
So I'm going to make another constructor that takes these parameters.
So I'm going to make the constructor receive an A player controller pointer.
I'm just going to call this PC.
I know that's a very non-descriptive name for a parameter, but for these four key variables I'm going
to use abbreviations.
And whenever I use those abbreviations, you know that I'm talking about these key variables.
So after player controller I'm going to add an a player state input.
That's a pointer.
We're going to call this PS for player state.
And we're also going to have a U ability system component called ask for ability system component.
And a U attribute set.
Looks like we need a forward declaration.
I'll add that as well here.
And this is going to be called a S for attribute set.
And I'm going to go ahead and define the constructor right here.
So I'm going to use an initializer list that involves a colon.
And between the colon and the curly brackets we initialize the variables right here.
Well this struct doesn't have any.
So I'm going to add a few pointers here.
I'm going to use t object pointer.
And the first one will be the player controller.
That's an A player controller.
We'll call it player controller.
We'll keep things simple.
And this will be initialized to a null pointer.
We tend to get compiler warnings when creating structs when they're member variables are not initialized.
So I'm going to initialize it to null pointer.
And this gets a u property.
And I'm going to make it edit anywhere and blueprint rewrite.
We'll give full blueprint access for all of these variables.
So that's going to be the player controller.
We're going to need the player state.
So we'll make a t object pointer a player state.
This variable will be player state.
It'll be initialized to null pointer and gets the same u property access.
And next we have ability system component.
That will be a t object pointer u ability system component called ability system component initialized
to null and given the u property.
And finally our last t object pointer of type u attribute set called attribute set.
Initialized to null with our U property.
And now in our constructor we can initialize these.
We can say player controller and with parentheses we can initialize it with our PC input parameter PC.
And these are going to run off the screen.
So I'm going to go ahead and starting at the colon start these on the next line here.
And this will be a comma separated list.
This is known as the member initializer list.
We're going to initialize player state with PS ability system component with ASC and attribute set with
a s.
And I get red squiggles because it looks like I allowed autocomplete to put aura attributes set here.
I actually just want u attribute set, so I don't need to forward declare u or attribute set either.
It's just going to be a u attribute set here in the or widget controller class.
So now we have a struct that can receive these four key variables when we create one.
And that's going to make it easy to initialize a widget controller.
And in order to do that I'd like to have a function here in or a widget controller that can initialize
those values for us.
So I'd like a public section and a function for that.
Now I'm just going to call it set widget controller params.
And it's going to take a const f widget controller params.
We'll pass in a const reference and I'll call this w c params.
For widget controller params, I'll generate the definition and all that's going to do is set those
key variables.
We'll set player controller equal to and we have our w c param struct w c params dot player controller.
We also have player state.
We'll set that to w c params dot player state.
We have ability system component set that to w c params dot ability, system component and finally attribute
set will be set to w c params dot attribute set.
So now we can simply call this function and pass in the structure f widget controller params and the
widget controller will set its member variables.
And I'll go ahead and make this a u function with blueprint callable so we can set it if we need to
from blueprint.
And now we have a way to set those variables okay.
So now our widget controller has a way to set those variables.
We're going to want to actually create an object of this widget controller.
But I'd like to use a widget controller as a sort of base class.
So all of our widget controllers will inherit these properties.
And for our overlay I'd like to create a specific overlay widget controller.
So I'm going to make a child class based on aura widget controller.
So I'm going to go ahead and compile and launch the editor so I can make that child class.
So opening all the assets that I had open looks like I still have AVP aura.
I can close that.
I can close Aura Game mode and Aura HUD.
And I'm going to make the new C plus plus class that I want.
So C plus plus classes, Aura Public.
And here in UI we have the widget controller.
I'm going to make a widget controller based on Aura widget controller.
So I'm going to right click create a C plus plus class.
And I can let this live in the widget controller folder.
And this is going to be called Overlay Widget Controller.
So we'll go ahead and create this class and close the editor.
And now that I have an overlay widget controller, this is the one I want to create.
So here in the UI folder we have widget controller.
There's aura widget controller.
That'll be our sort of base class.
And we have overlay widget controller.
And that's the one I want to construct.
Now I'm going to construct this in the HUD class.
I think the HUD is a good class to create both the overlay widget and the widget controller itself.
So I'm going to make a function to create the widget controller.
Now I'd like this function to create the widget controller if it hasn't been created yet.
And if it has, just return that widget controller.
In other words, we'll only ever have one overlay widget controller.
It's kind of treated like a singleton in a sense that will only be able to get it through a getter that
will create it if it doesn't exist yet.
So I'm going to make that get'er it's going to be public and it's.
Going to return a U overlay widget controller.
That's the class we just created, and we're going to add a forward declaration for that.
And this function will be called get Overlay Widget Controller.
And it can take a const reference to some f widget controller params.
So that'll be a const reference.
And we're going to call it wc params.
And we can even forward declare f widget controller params.
So up here with our forward declarations I'm going to say struct.
F widget controller params and let's generate this get'er.
And the Get'er is going to check to see if one of these has already been created.
So if one has been created, we should store it in a pointer.
So or a HUD is also going to get a pointer to store that overlay widget controller.
So here in the private section we're going to have a you overlay widget controller.
That's a pointer.
We'll make it a t object pointer.
And we'll call this overlay widget controller.
And for now we'll give it a U property with no specifiers.
And here in our HUD we're going to check to see if it's a null pointer or not.
So we'll say if overlay widget controller.
Is a null pointer.
If it is a null pointer, we'll create one.
And if it's not a null pointer, well then we'll just return the non null widget controller.
So we'll return right here overlay widget controller.
But if it's null we need to create one first.
So we'll create one.
We'll say overlay widget controller equals and we'll use new object.
So new object is how we can create a new object.
We're going to create a you overlay widget controller.
Notice that writer just included that header for us.
And for new object we need to pass in the outer which is going to be the HUD itself.
And we also need the you class.
So our HUD needs to have a new class.
In other words, we should make a subclass of for the overlay widget controller class itself.
So just like we have the overlay widget class, we need an overlay widget controller class.
That'll be a subclass of you Overlay widget controller.
And this will be overlay Widget controller class.
And this gets a new property.
But we also have to be able to set this from the blueprint.
So we'll make it edit anywhere.
And back in the cpp file we'll pass that class into new object.
So overlay widget controller class.
So here we're setting our member variable overlay widget controller.
We've just created a new one.
And we should set its widget controller params.
So this get'er takes WC params.
So we can take our overlay widget controller.
And call that public function that we created called set widget controller params and pass in WC params.
And that way those key variables will be set.
And once we've created one of these we can return overlay widget controller.
So now we have this Get'er that will construct it for the first time if it hasn't been constructed yet,
but if it already has, it won't be null and we'll just return it without constructing anything.
So in order to get it, we have to pass in those WC params for it to have its key variables set.
So now that we have that get'er, we now need to construct our widget controller.
But we see that in order to construct it we need widget controller params and to initialize widget controller
params, we need those four key variables which we don't really have access to in begin play.
Here.
So essentially we need to do this somewhere where we have access to those four key variables.
So what I like is for the HUD to have a function, we can call and pass in those four key variables.
That way we can create that widget controller and assign it to our widget.
So rather than doing all this and begin play, I'd rather do it in a function that can take those key
variables.
So we're going to make a function here in our HUD.
It's going to be a void function.
We'll call it init overlay.
And it's going to take the four key variables in a player controller.
We'll call it PC and a player state.
We'll call it PS.
These are pointers a new ability system component.
I'll add the forward declaration and call this as C and a U attribute set.
We'll add a forward declaration and we'll call this as.
And we'll make the definition for init overlay.
And init overlay is going to construct the widget controller, construct the widget, set the widgets
widget controller and add it to the viewport.
We'll take all those things and tie them all together here.
So we're not going to do it in begin play anymore.
I'm going to go ahead and just remove begin play.
But these two lines, we can go ahead and cut and paste them into init overlay first.
And then we're done with begin play I'm going to delete begin play.
I'm going to delete it from our HUD as well.
And here in init overlay is where we're going to actually do everything.
Now first of all init overlay needs to create two things.
Now we need to create a widget and a widget controller.
So what I'd like to do is perform a check.
But if that check fails and we crash the engine, I'd like to provide a more descriptive crash message.
So for that I'm going to use check f.
Check F will check a condition and then print a formatted string to the crash log if the check fails.
So I'm going to check first of all overlay widget class.
We need that to be set.
And if we fail then we'll use the following message.
We'll say overlay widget class uninitialized.
Please fill out BP or HUD and we'll do the same thing for the widget controller as well.
So we'll say check F overlay widget controller class.
And we'll say for the text overlay widget controller class uninitialized please fill out BP or HUD.
So that way we definitely won't forget.
So first we need to create the widget.
We'll call it widget.
And we're going to want to create the widget controller as well.
So I'm going to wait to add this widget to the viewport till the very end.
And I'm going to cast Widget to Aura User widget.
That way we can set its widget controller.
So that way I can set my overlay widget.
Remember we have this overlay widget which is an aura user widget.
So I'm going to say overlay widget equals we're going to cast to you aura user widget.
And we're casting widget.
Now we have access to overlay widgets or a user widget stuff like set widget controller and so on.
But we have to create that widget controller first.
So next we create the widget controller.
And I'm going to make a f widget controller params struct.
I'm going to call it widget controller params.
And remember this has a constructor.
That takes the four key variables.
We'll just briefly go back to our widget controller to remind ourselves.
We'll scroll up and see that constructor.
It takes player controller, player state AC.
And as let's initialize the widget controller params.
Because init overlay takes those key variables, we can initialize it with pc, PS, AC and as and.
Now this struct contains those.
And really we could make this a const struct.
There's no need to make it mutable.
And now that we have those params, we can now construct the widget controller.
Because our HUD has get overlay widget controller, it requires the params.
It's going to be null the first time our overlay widget controller.
So it's going to construct the widget controller, set it's widget controller params and return one.
So what we'll do here is we'll call that function and store its result in a you overlay widget controller.
So we'll make that pointer.
We'll call it overlay widget controller and we'll initialize it with get overlay widget controller.
And as we can see it requires the f widget controller params.
We have one here called widget controller params.
We'll pass that in just like that.
And now we have the overlay widget controller.
So now that we've constructed the overlay widget we've constructed the overlay widget controller.
We need to tie the two together by taking overlay widget and calling set widget controller passing in
overlay widget controller.
And after that we can add it to the viewport and in our HUD line 30.
Looks like I'm hiding a class member overlay widget controller here.
Looks like we made this a member variable, so I'm going to actually call this simply Widget Controller
and pass that in to set Widget Controller.
That way I'm not actually hiding a member variable.
So now we have this function in the HUD called init overlay.
And as long as we can pass in the player controller, the player state the ability system component
and the attribute set.
Then init overlay can construct both the overlay and its widget controller and set the overlay widgets
widget controller before adding it to the viewport.
So this init overlay sort of does everything for us.
We just need to be able to pass in all that information.
So the question is when and where do we call init overlay.
When do we have access to all of these.
When do we know that they're all initialized with valid data already.
And that's going to be your quest.
Now this quest requires you to do a little bit of thinking.
Your objective here is to call init overlay, but decide where to call it.
Where should we call it, when and where are we going to know?
All the key variables are initialized?
This is going to make us think back to where we had to make sure our player controller was already set
for the character, and our player state was already set back when we had to init ability actor info.
So there's a hint for you.
So decide where you need to call this.
Make sure you take care of both cases, the server and the client.
And once you're calling that function, you can play test and make sure you get that widget added to
the viewport.
So pause the video and try this quest now.
Okay, so we now have this nice function that ties everything together in the HUD.
And here is where we create the widget.
Create the widget controller.
Set that widget controller on the widget and add it to the viewport.
But this function requires the player controller, the player state, the ability system component,
and the attribute set.
So where do we know that those are initialized already?
Well, you might have thought that we could go into aura character into init ability actor info.
This is a pretty good spot right?
It's called in possessed by and on rep player state taken care of for the server and the client.
And here is where we already know the controller is set.
And we also know that we have a player state.
Right.
We're checking the aura player state here.
So there's two of our key variables.
And also we have the ability system component.
And the attribute set.
We have all four of the key variables here.
This is a great place to do it.
Now we need to access the HUD.
And the HUD is always accessible through the player controller.
So what we can do is we can get our player controller, the character class has get controller.
And of course this returns a a controller.
So we have to cast it to a player controller.
In fact we can cast it directly to our player controller class.
What is that?
That's of course our A player controller.
That's a or a player controller.
We can cast right away.
And we can store this in an A or a player controller pointer called Aura Player controller.
Now the question arises, do we check this pointer, make sure it's not null?
Or are we sure that it'll never be null?
Or should we place a check here?
Should it never be null?
Is there a problem if it's null?
This is a pretty common question, and a lot of newer developers sort of struggle to understand when
to null check and when to perform an assert like check, and it helps to understand the player controller
and whether or not it exists on all machines for all players.
And we can recall back in our multiplayer discussion that only on the server are all player controllers
valid.
The server has all the players player controllers, but each player has only its own player controller.
So on a client's machine who is controlling that specific character, that player controller is valid,
but the other characters on that client's machine who are not locally controlled, those do not have
valid player controllers.
So say in a three player game, for example, if you are a client, your player controller is valid,
but on your machine, the other two characters, those two copies do not have valid player controllers
and innate ability actor info.
This function will be called and or a player controller will be a null pointer in that case.
So in this scenario for this function or, a player controller can and will be null in multiplayer.
And that's okay.
We only want to continue if it's not null.
So we'll place an if check and wrap this in an if check in that way only if our player controller is
not a null pointer do we continue.
So this is the case where we check the null pointer and don't place an assert, because we don't want
to crash in this case.
Now this brings up an interesting point.
Is there anywhere else in our code that's like that?
Well, there is one.
And just as a bit of a tangent, I'm going to show you that if we go to our player controller dot cpp
right here in Beginplay, we're checking the subsystem and we're calling Add mapping context here.
But really in Beginplay for or a player controller, we're not going to get a valid subsystem unless
we're on the locally controlled machine who has a valid local player here.
So this is one of those cases where we should check the subsystem instead of using an assert that says
check.
So really quick we can just say if subsystem here.
And place that subsystem call right there.
And now this will work in multiplayer.
So that's kind of an important detail now that we're thinking about multiplayer.
So we'll make that change and close or a player controller and come back to or a character in init ability
actor info where we're getting or a player controller now from or a player controller, we can get the
HUD so we can take or a player controller, we can call get HUD and this returns a a HUD.
Right.
But we want an A or a HUD.
So we're going to cast this to A or a HUD.
And we have a valid player controller.
We want a valid HUD as well.
So we'll make a local A or a HUD called or a HUD, and we'll place that in an if check to make sure
that it's valid.
Because remember the HUD is only valid for the locally controlled player.
So if our HUD is valid now we have access to our HUD.
And we can finally call that function on our HUD, which we called init overlay which takes the four
key variables.
We need the player controller we can pass in, or a player controller.
We need the player state we can pass in, or a player state.
We need ability system component.
That's going to be ability system component.
And finally an attribute set that's going to be attribute set.
And now init ability actor info is complete.
And we can go ahead and test this out.
And I'm going to run in debug mode.
All right.
So we've opened the editor.
We're running in debug mode I'm going to go to blueprints UI HUD Openbor HUD.
And we have overlay widget class and overlay widget controller class.
Now we don't have a blueprint for overlay widget controller.
That's okay.
We can set it to the C plus plus class for now, but eventually our overlay widget controller may wish
to have some data on it that we can set from blueprints, so we'll keep that in mind.
But let's go ahead and hit play.
And there we have it.
Our health and mana globes are being displayed on the screen.
And that's all being drawn from the aura HUD, thanks to our nice function init overlay.
And we know that our widget controller is being created and it's being set on our overlay widget.
And just before we wrap up, we can prove to ourselves that that widget controller is being set.
How can we do that?
Well, we can go into the Wbbp overlay widget, go into its graph and right click and we have a widget
controller set event.
Remember we added this to Aura User widget.
And if event widget controller set is fired off, that means we've set the widget controller.
And in fact we can search for widget controller.
And here's the get widget controller.
That's the getter for that variable.
That is our widget controller.
Here in blueprints we can use get object name.
To get a string with that object name, and we can use print string to print it to the screen.
And now if we go ahead and hit play we see overlay widget controller underscore zero.
That's the name of the widget controller.
So we know that the widget controller is set as well.
And we're running this in debug mode.
Which means if we want we can place a breakpoint anywhere, such as in init overlay.
If I place a breakpoint at the very last line where we're calling add to viewport, well, now if we
press play, we'll hit that breakpoint.
And here's where we're starting to see the benefit of having symbols.
Because now check this out.
Down here we have a lot of things popping up and showing us values.
So when this function init overlay gets called we see that this is bp or hud underscore c.
So we know inside this function this this holds a blueprint the or HUD blueprint.
That's pretty cool.
And we know that PC this local input parameter local to init overlay has a valid value look it's set
to BP or a player controller.
How about that.
And we can hover over it and see that in fact hovering over it we see its memory address, we see its
name.
And if we click plus we get a dropdown.
This is all writer specific, but Visual Studio also has debug capabilities.
We see that the player controller has last actor and this actor set to null.
Those are those I enemy interface pointers.
And so we have the ability to see all kinds of information.
So that's really really cool.
And by the way I'm just now remembering last actor and this actor where raw pointers right.
We didn't use object pointer.
And we can see that here.
And if we want that to be an object pointer we really could change it to object pointer and keep up
with that standard.
So hey there Steven from the future here.
Coming back to let you know that what I said in this video is incorrect.
We cannot use a t object pointer for an interface such as our eye enemy interface.
That is not correct.
What we need to use instead is a t script interface.
Please refer back to section two in the Highlight Enemies lecture, where we use the T script interface
instead of eye enemy interface.
That was an addition to the course, so please make sure that you're using a T script interface and
not a raw pointer or a t object pointer for your last actor and this actor.
Very important.
Use t script interface.
Now that widget controller can start really controlling the widgets that have their widget controller
set to it.
And we're going to see that power in the videos to come.
So excellent job and I'll see you in the next video.