Welcome to your next quest.
Now we're going to close out the section by creating an enemy health bar.
And this is a quest for you.
Now, this is going to involve creating a widget, of course.
And I'd like this to be above the enemy's head.
So add a widget component to Ora enemy.
Now you're going to need to respond to health changes.
You should know how to do that.
And as a bonus, I'd like you to see if you can use the existing system that we have in place.
Now, by system, I'm talking about using a widget controller to broadcast data to a widget, in our
case, the health bar widget that we're going to set for our enemies widget component.
Now you'll need to think about what should be the widget controller for this and if you really even
need to create one.
And I have an additional hint for you if you're a little bit stumped as far as how to approach this,
if you're ready to jump in, go ahead and pause the video now and conquer the quest.
But if you'd like an extra hint, I'll give you one now.
Our widget controller is a new object.
That means it can be anything that derives from you object that includes actors, pawns and even characters.
A character itself could be the widget controller.
So there's a little bit of a hint and it's a hint about how I'm going to implement this as well.
So pause the video and conquer this quest now.
Okay.
So there are going to be a few parts to this challenge.
I need a widget component on my enemy class, so I'm going to start there.
I'm going to close all tabs and open my public folder, go to character and ora enemy.
And I'd like to add a health bar widget component.
So to add a widget component, we need a variable of that type.
I'm just going to put it down here in the protected section.
It's going to be a T object pointer and it'll be of type U widget component and we'll call this health
bar.
I'll go ahead and forward declare the U widget component type.
Class U widget component.
Now this needs a U property, so we'll give it one.
And I'd like to set this in the blueprint.
It's a component.
So like most components, we need to give it visible anywhere.
I'll make it blueprint read only and we'll have to construct this health bar widget component.
So we'll go into the CPP file all the way up to the constructor and make the health bar here.
I'm going to do it just after the attribute set.
So we're going to say health bar.
I'm going to use create default Subobject with u widget component.
And I'm just going to call it Health Bar.
And we'll need to include the header file for it.
So I'm going to put it right here.
Include and it's going to be in components slash widget component dot H.
So now we've created our health bar and we need to attach this.
I'm going to attach it to the root and then we'll adjust it from the enemy blueprint.
So we're going to say health bar setup attachment and we're going to attach to the root component.
So simply using get root component there.
And now we have a widget component so we can go ahead and launch and we can get started making the widget
that our widget component will use.
So we'll go ahead and create a new widget blueprint.
I'm going to go into Blueprints and UI and we have a progress bar folder where we have our globes.
I'd like to make my enemy health progress bar here, so I'm going to go to user interface, Choose Widget
Blueprint.
We're going to use Ora user widget and this is going to be WB underscore enemy health bar.
Actually, this could really just be a basic progress bar.
So I'm going to call this WB progress bar so we have a globe progress bar and we have a WB progress
bar that way this could be just generalized.
So I'm going to have first of all a size box.
We'll call this, as we normally do, size box routes.
And I'm going to set this from fill screen to desired.
And this is going to be a relatively small size box.
We're going to use width and height override, but I'd like these to be parameters and just to get a
sort of idea for what kind of size I want, we'll do something like perhaps 60 by maybe five.
That might be too small.
We'll have to see what it looks like on screen, but we'll go ahead and make the size box route a variable
and we'll set these from pre construct.
So I'm going to drag it out twice.
Call set width override.
And call.
Set height override.
And we'll promote these two variables.
We'll call this box width, as we normally do.
That's a float, and I'm going to give it a category of progress bar properties.
So we'll have a box width.
We'll promote this to a variable, have a box height.
And set that to progress bar properties as well, and we can hook these up.
Compile.
And give them defaults.
And I think for width we'll do 60 and for height we'll do five.
But we'll tweak these if we need to.
And I'm going to go ahead and collapse this to a function called update box.
Size and we just really need a progress bar.
But I'm anticipating possibly the desire for a ghost progress bar.
And because of that, I'd like an overlay.
So I'm going to stick an overlay onto my size box, rename this to overlay root, and then I'll put
a progress bar on top.
So we'll stick a progress bar in here.
And this progress bar, this is going to be simple.
I'm going to take my fill color and opacity and drag that saturation down so it's white.
I'm going to take the style and we're going to take our background image.
And I think I'd like my background image to have a tint with Zero Alpha.
This is just personal preference here, and I'm going to go ahead and up the percent a little bit so
I can set the fill image here and the image.
I think this actually can be a parameter so we can change it if we want it to be red, blue, whatever.
So I'm going to go ahead and call set style in the graph.
I'm going to change the progress bar, though.
I'm going to have progress bar underscore front and then we'll have one that's a ghost, possibly.
So for progress bar front I'm going to check is variable drag it on out and call set style.
And for the style.
We'll make a style, make progress, bar style.
And actually here is where we're going to set the properties like background image and things like that.
So here's what I'll do is first I'll drag off, we'll make a slate brush for the background image and
we'll expand it and drag out of the tent, make a slate color.
And I'm just going to go ahead and bring that alpha to zero for the slate color.
And there that takes care of our background image.
I'm also going to delete these two nodes for now.
So there's our background image and then we can promote fill image to a variable and we'll call this
front bar fill brush.
We'll stick this in the category of progress.
Bar properties will compile and give this a default.
And I'm going to just take the tint.
And I think what I'll do is make this just red click okay, maybe make it a little darker of a red and
click okay, draw his image is fine and compile that and we can hook this up.
See what it looks like if we compile and it looks like a red progress bar.
Since that looks great, we'll collapse this to a function.
Right click collapse the function update front fill brush.
So you could make the background image also a parameter.
I tend to like them transparent.
That's just me.
I'm going to set horizontal and vertical alignment to fill and we could just go ahead and make a background
bar, but I'm going to save that for later.
First, I want to make this work, so I'm going to get basic functionality working before I go and make
things like a ghost progress bar and so on.
So that's looking good.
We're going to add this to our enemy and I'll go to my base enemy.
So Blueprint, we'll go to character and enemy base, open the Viewports and I'm going to select my
health bar widget and we're going to want to decide how to draw this.
I'd like to draw this in screen space, so I'm going to set space to screen.
We're going to set our widget class and this is going to be a progress bar.
So we'll get that.
We can't see it here.
We'll have to look in the game when we play test it.
But we have a draw size here.
But we can also click draw at desired size.
So if we click draw at desired size, we should see what it looks like if we go ahead and save all.
And press play.
Then we can see what it looks like in the world.
And I think that looks pretty good.
Just a tiny little progress bar for our little enemies.
I think that looks great.
So that's going to work.
I would like the progress bar raised a little bit, so I'm going to raise it up to about there, about
80 units higher.
And now we can see them above the enemy.
And that looks great.
But if we launch a fireball at it, that's not going to do anything to the health bar.
We're not seeing any change.
So that's an issue.
So we're going to want to make this functional.
Before we do, though, I think my default size is a little small, so I'm going to take my box width
and change it from 60 to 80 and box height from five to maybe ten.
Compile that press play.
I liked five for the height.
I think so.
We'll go back to five.
Actually, let's do six.
Just tweaking six looks pretty good.
All right.
So we need this to be functional.
So we need to think about how to do that.
Well, our progress bar is going to need some way that we can set its progress.
And if this is going to be an enemy health bar, then it should specifically respond to some kind of
delegate broadcast.
Right.
To update the progress.
Now, we could keep this generic keep it a generic progress bar set to respond to a generic delegate
broadcast with an event that will set its progress.
And that way, if we subclass it, then we can change the color and respond to really any sort of delegate
broadcast.
So that's an option.
But what I'd like to do is perhaps make a child of this simply for health and then it can listen to
a health broadcast.
So the question is what class is going to have the delegate to broadcast that we can bind to on our
progress bar?
Well, if our progress bar class is going to be generic, then we'll subclass it into a health bar and
the health bar will decide.
But again, the question is still there.
What class do we bind to?
What class is going to broadcast?
Well, recall that I gave you a hint, and that hint was that the widget controller on the aura user
widget class is a U object, meaning it could be set to anything.
It could be set to a character, an actor, or whatever class we want that's derived from U object.
That means our enemy's progress bar could have its widget controller set to the enemy itself.
And that's what I'd like to do.
I'd like to broadcast values from the enemy, so I'm going to go ahead and save all and close and we're
going to go into Aura Enemy.
And I'd like to create a delegate we can broadcast to our widget from the enemy itself.
So for that reason, I'm going to create a variable that can broadcast an attribute value.
Now, recall that we already made a delegate like that.
If we go into our public folder, into UI widget controller and we go into overlay widget controller,
here's a multicast delegate one param called on attribute changed signature.
This is a delegate that can broadcast a float value f on attribute changed signature.
What if we want to use that here in aura enemy?
Could we go into the public section and simply add one of these and call this on health changed?
Well, it's not a defined type.
It's incomplete.
So we are going to need to include something here so we can include that overlay widget controller class.
So I'm going to include it and it's going to be in UI slash.
Widget Controller Slash overlay.
Widget controller.
And now check it out.
We can use that delegate.
So no need to create a new delegate.
We can actually reuse one that already exists for this very purpose.
Yes, we did have to include overlay widget controllers, which is not a huge class.
It's not a big deal that we're including that here.
This is more an exception rather than the rule for habits that we have.
However, if this was really a concern, then we could move that delegate into a different class and
just include that wherever we want to use it.
I'm not going to bother with that.
I'm okay with including the widget controller class here in enemy.
So we have an on health changed delegate.
Now this needs a new property and we're going to make it blueprint assignable so we can assign it from
blueprints.
And I'm going to copy and paste a new one of these called on Max Health Changed and now we have two
delegates.
So the question is, when do we broadcast these to our health bar?
Well, we know that there are delegates on the ability system component that we can bind to, and those
will be broadcast when attributes change.
So what we can do is we can bind to those delegates.
Now, when do we do that?
Well, we need our ability system component to be initialized before we do that.
And we know that on the enemy class this happens in init ability actor info.
That's when we're calling an ability actor info on the ability system component.
So we know that our ability system component is initialized by this point.
So right here in Beginplay, I'd like to bind to some of those delegates broadcast by the ability system
component.
Now what are those delegates?
Well, first let's get our ability system component pointer and we're going to call get gameplay attribute
value change delegate.
Now remember, this requires an gameplay attribute, so we need to get that from our attribute set,
which means we are going to need to cast to aura attribute set so we can say you aura attribute set.
We'll call this aura as.
We'll set that equal to a cast.
You aura attribute set and we're casting attribute set and if you want this could be a cast checked.
As we know our aura enemy will use our aura attribute set for sure.
But in your game, if it's possible that you might be using a different attribute set, you may just
try cast and check the pointer.
So you could do it this way and say if aura as then we can bind to those delegates like so.
Now to get that attribute.
First of all, we're going to want the health attribute, right?
So we're going to use aura as get health attribute.
And this returns that delegate and we're going to bind to it so we can use dot and we can use Add you
object and bind a function callback or we can add lambda and bind a lambda to it.
And I think that's cleaner as it doesn't require us to create a function callback.
So I'd like to do that.
So I'm going to make a lambda, which means I need square brackets, parentheses and a function body.
And this lambda needs to have the function signature of a function that takes a very specific const
reference input parameter and its type is F on attribute change data.
So that's a const reference and we can call that data.
And all we want to do in this lambda is broadcast the delegate.
So that's going to be on health changed as this is for the health attribute.
So we're going to broadcast and we're going to broadcast data dot new value.
Now we can't use on health changed because this is not captured.
So we have to pass that in to the capture list.
We got to put this in there and now we can do this.
Now that's for on health changed.
We need on max health changed.
So let's copy the whole thing, paste it here and we're going to change this to get max health and we're
going to change the delegate to on.
Max health changed.
So now our begin play is taking care of binding to these delegates.
We can move auras into the if statement if we want to do that.
I'm going to go ahead and do that, make it a little bit more concise.
We could also make it a const pointer and now we're taking care of binding to these delegates.
So now that we're broadcasting delegates for when health and max health change, the only thing left
is to bind to these in our progress bar.
So let's go ahead and compile and launch the editor.
All right.
I'm going to click open.
Get that progress bar back.
But this is our base progress bar class.
I'd like to now create one specific to health so we can bind to that on health changed and Max health
change delegate.
So let's go to let's browse to the asset and we'll make a new widget blueprint, user interface, widget
blueprint and under aura user widget we should see the progress Bar WP Progress Bar.
Let's select that and I'll call this WP underscore enemy health bar.
So this one will be specific to the enemy.
And now that we've created this, we need to make sure we're using that in enemy base.
So back in enemy base, let's get our health bar here and we'll change the widget class from Progress
Bar to WP Enemy Health Bar.
Now we're using the correct class.
And an enemy health bar.
We need to bind to some delegates.
But also how do we know the object to bind an event to its delegates?
How do we do that if we don't have a variable referencing our enemy?
We have a widget controller, but that widget controller variable is never being set.
We need to make sure to set that.
So if we go back into writer, how are we going to set the widget controller for our newly created health
bar widget blueprint?
Well, here's what I'd like to do.
I'd like to set the widget controller from the enemy itself as the enemy has access to it.
And I'll go ahead and do that before binding our callbacks here.
So right before we bind our callbacks here and begin play, I'm going to set the widget controller on
our health bar.
But the thing is, our health bar has the type you widget component.
So we're going to need to first of all get its user widget object.
We can do that with get user widget object.
So that gets the user widget used by the component itself.
We can cast that.
So we're going to cast.
And we can cast this to a user or a user widget.
So you or a user widget.
Once we do, that, writer is going to include or a user widget up here in my includes takes care of
that and I'm going to store this in a local variable of type U or a user widget called or a user widget.
And we can set the widget controller here.
Now, I can actually wrap this in an if statement like so.
And we can set the widget controller with or user widgets set widget controller and what are we going
to set it to?
We're going to set it to this.
So now it's widget controller is the enemy itself that owns it.
So that's perfect.
So after we set the widget controller, we then bind our lambdas to the callbacks, but then we should
probably broadcast the initial values because we're calling set widget controller.
Our widget can then bind to these delegates on health change and Non-max health changed.
So after calling set widget controller, we know that's the case.
And then we can simply broadcast the initial values for health and max health right here after we've
bound those delegates, we can say on health changed dot broadcast.
What are we going to broadcast?
Well, we're going to get our auras and we're going to broadcast the value of health so we can call,
get health, and then we can broadcast on max health changed.
So call broadcast here using auras.
Get max health.
So that's broadcasting the initial values.
So we have three things we're doing.
First, we're setting the Progress Bars widget controller, and that's the progress bar used by this
widget component.
Then we're binding callbacks actually.
Lambdas to get gameplay attribute value change delegates on the ability system component.
So those will be broadcast in response to attribute changes.
But then we're broadcasting the initial values here.
Okay, so we'll need to compile again for that to take place.
But in the meantime, now that we know we're going to have a valid widget controller here in enemy health
bar, we can use widget controller set.
And in response to having the widget controller set, we can get the widget controller variable as that
is exposed to blueprint and we can cast it to the enemy class.
So we're going to cast and we can cast to the blueprint BP enemy base or we can cast to Ora enemy.
It doesn't matter.
The delegate is on the C plus plus class so we don't have to choose the BP enemy base, but I'm going
to go ahead and do it.
Just in case in the future we add something to that blueprint.
We might need to access here.
I don't foresee that, but I'm casting to enemy base.
Now we can bind to delegates on it.
We're going to bind to two different delegates.
So first, I'm going to promote this to a variable and call this BP enemy base.
And I'd like to make a sequence node.
To organize the different things we're going to do.
So let's do a sequence.
The first thing we're doing is getting our widget controller casting to enemy base.
And then next.
Now that we've promoted this to a variable, we can get enemy base and we can get on health changed
and we can assign an event to on health changed.
And we can hook that up and we can do the same thing for on max health change.
So I'm going to duplicate BP enemy base and search for on max health changed.
Assign that and hook that up to the sequence as well.
So now we're going to have our two delegates for when max health and health change and what are we going
to do?
Well, we're going to update the progress bar.
So how are we going to set the progress bar?
Well, if we look at Progress Bar, the progress bar is called Progress Bar Front.
So from within our enemy health bar if we search for progress.
Bar underscore front.
We can get that as long as it's a variable, which it is.
And with progress bar front selected we can call set percent the function.
So what percent are we going to set it to?
Well, what we can do is we can take the value that we've gotten from on health changed and we can promote
that to a variable called health.
And I'm going to fix that typo.
And then we can promote this one to a variable for on max health changed and we'll call this max health.
And each time one of these is set, we'll divide health by max health.
So we'll take health.
We'll take max health.
And I'm going to use Safe Divide.
That way, if we're dividing by zero, we'll get zero rather than something that's not a number.
So after setting health, we do the safe divide and we set percent like so.
And this is going to be the same exact thing we do down here.
We can make a function for it or I can just duplicate like so and it's actually not going to be hooked
up to the blue node.
It has to be hooked up to just after we set max health.
That's kind of important.
And by kind of, I mean really important.
Okay, so this is all set up and we have our widget controller set.
At least we made that change in C plus plus, but we didn't compile it.
So we're going to have to close the editor, save all and compile and launch from our IDE.
That way we're actually setting the widget controller, binding the delegates, broadcasting initial
values.
So now we can play test.
All right.
I'll go ahead and get those asset editors open and then we can do some damage to the enemies.
And look at that.
Look at that health bar.
It's going down.
Beautiful.
That is looking really nice.
It's reacting as it should.
And we can see.
That we're doing damage.
Perfect.
Okay, so we have a working enemy health bar.
Now, this is great and we could easily stop here, but it's always fun to do things like trailing ghost
bars.
And I think it's one of those details that's really small and not necessarily noticed as much, but
it is noticed and it is kind of a nice detail.
So I'd really quickly like to have a trailing ghost bar and I'd also kind of like to hide our health
bar when it's inactive.
So if it hasn't been updated in a while, I'd like to sort of hide it and show it again once it's being
displayed.
So that's a couple of things we can do, and I think those would be great for the next video as getting
this working was a good milestone for now.
So in the next video, we're going to make a ghost health bar.
And make our health bar timer based.
Great job and congratulations.
If you made it through this challenge by yourself.
It was nice and challenging and ambitious, so congratulations.
And if you only made it part of the way through, that's totally fine.
Congratulations on doing the challenge.
I'll see you in the next video.