Welcome back.
So in the last video, we made a damaged text component, which is a widget component, and it has one
function that we created on the Cplusplus side, which is a blueprint implementable event.
We implemented that here.
Now, this widget component has its own widget.
It's called damage text.
We set that in the class defaults if we search for widget class.
We'll see that we set it to damage text, and that's a simple user widget that just has text and an
animation and it plays that animation itself whenever it's constructed.
So event construct plays its own animation, and it also has a simple function that takes in a float
and sets the text for this number, the damage text number.
So really all we need to do is show this or create one of these components.
Whenever we take damage, we're going to do that on the C plus plus side.
Now, I'd like a function for constructing one of these damage numbers.
And there's a couple things we have to keep in mind.
One, in our attribute set, this damage meta attribute is updated on the server.
So if we do something here, it's only going to happen on the server.
If that attribute changed here in post gameplay effect, execute is incoming damage.
So we have to take that into account.
Now, if the damage is fatal, we're calling die after making sure that the target Avatar actor is a
combat interface.
But in the case we're activating the hit React ability and this is when we should also show floating
text for damage.
And in fact, you could just show floating text anyway whether the incoming damage is fatal or not.
So really inside of if local incoming damage is greater than zero, we could do it here.
So why don't we just do it here Now?
We'd like to show the damage number and I'd like to have a function on the player controller that can
spawn this damage number widget.
And then what we can do is after spawning it, we can set that damage text, we can attach it to the
pond so that it's just above the pond in the world.
And so I'd like to have that be a function on the player controller.
So let's do a couple things here.
I'm going to close all tabs except for the attribute set, and I'm also going to open up my public folder,
go to player and open aura player controller, and I'm going to make a show damage number function here.
Now this function, I'd like to be a client RPC, so if we call it on the server, it'll be executed
on the server if the controlling player is local.
But if the controlling player is remote, then this function will be executed remotely on the client.
So we'll add a public function.
It's going to be a void function and we'll call this show damage number.
And show damage number needs a float for the damage amount.
That way it can pass that along to the widget component it's going to spawn and this will of course
be a new function.
And since it's a client RPC, it gets client and we'll make this a reliable RPC.
So show damage number needs a implementation so we can create the definition writer is going to put
underscore implementation here for me, which is exactly what I need and show damage number is going
to construct that widget component and it needs a widget component class so it knows what type of widget
component to construct.
So we're going to also add a variable on or a player controller for the class to spawn.
So this one will be a subclass of now it could be private.
We can stick it way down here all the way down at the bottom.
And this can be a subclass of of type you damage text component.
That's the class we made last video.
Now I'm going to add a forward declaration for that as we don't need to include its header file.
So we now have you damage text component forward declared and this is going to be called damage text
component class and we'll set this on the player controller blueprint.
So I'm going to make it edit defaults only and we'll use this to create our new component here and show
damage number implementation.
Now we're going to want to know where to show that damage number because this is going to be on the
player controller.
And the player controller is really only going to spawn this for the player that is a human player that
can see it.
But we want to show this damage number just above the head of the target that's being hit.
So from the attribute set, we're going to get the local player controller that actually has a human
player and call this function on it so we can see that damage number.
But we're going to call this function passing in the target character or actor that we want to attach
or show this number above.
So our RPC here should actually take in a pointer as well.
So show damage number should take in a target.
We can pass in an a character, for example, called Target Character.
So let's go ahead and add that input parameter into the client RPC.
And here we're going to check to make sure that it's a valid target character.
So we're going to say if is valid target character and we can't check an incomplete type without actually
including that type.
So we're going to have to include the character class header file.
So include and that's in game framework slash character dot h.
Now we can check that pointer.
We're also going to check damage text component class and make sure that is valid.
And that is also an incomplete type.
We're going to need to include the header for that.
So include UI slash widget slash damage text component.
Now if you're asking why I'm using is valid for target character and not using is valid for damage text
component class well is valid checks to see if the pointer is null, but it also checks to see if that
pointer is pending kill.
Now this damage text component class, it's either going to be null or not null.
I'm not worried if it's pending kill or anything.
It's just a property that we either set on the blueprint or not.
But Target character may have had destroy call on it recently, last frame or something.
If it's pending kill then is valid matters.
So for those of you who were wondering that that's the reason.
Now, if the character is valid and the damaged text component class is valid, then we can create one
of these damaged text components and we can create a new view object with new object specifying you
damage text component.
Now, when we create a new object, we need to specify the outer for this object, and that's going
to be the player controller itself.
It's going to own this object.
And we can also specify a class that's our damage text component class.
So once we've created this, we're going to store it in a local pointer called Damage Text.
So that'll be a new damage text component called damage text.
And now we've created one of these.
Now after we create a widget component dynamically, we have to register it by calling register component.
This is something that you probably don't see very often because when we create a component, we just
construct it with create default subobject, which handles registering it for us with unreal engine
for all kinds of behind the scenes stuff.
We're creating one dynamically, not in the constructor, so we're manually registering this component.
And I'd like to take my damage text.
And I'd like to attach it to the Target character.
So what I'm going to do is call attach to component and we're going to take the target character and
get its root component.
So we're going to attach it to the root component using the attachment transform rules.
And I'm going to choose keep relative transform.
So it's going to attach to the root component of the target character.
But the thing is, the only reason I want to attach it is so that it starts off at the correct location,
the location of the character taking damage.
After that, though, I'd like to detach it so that it just floats away according to its own animation.
I don't want it to follow that enemy around or that character.
So after attaching it to the component, we know that it's going to play its animation right away.
That's all the movement I want.
So I'm going to take damage text and I'm going to call detach from component.
And this requires an F detachment transform rules.
I'm going to use keep world transform.
So the result of this is that we'll create the new component.
It's going to be attached to the target character's root component, then we'll detach it.
So it's going to be left floating there and it's going to play its own widget animation.
Now it's just a 99 that's going to float and fade away.
What we want to do is set the damage text and the reason we created a C plus plus class called you damage
text component was so that we can have a C plus plus function on it that we can call from here.
So we're going to call that now.
We're going to take damage text and we're going to call, set damage text and pass in the damage amount.
Like so.
So we'll create one of these and set its damage amount and let it animate itself.
So now that our player controller has this function and it's an RPC, so we can call it on the server
and for the server controlled character, it'll just execute on the server and the server controlled
character will see it.
But for client controlled character, it'll be called on the server but executed on the client and the
client will see it.
So either way this widget will be seen.
Now we need to actually call this function show damage number and we're going to do that here in the
attribute set.
And I'd like to only do this if it's not self damage, right?
So we can check if props, dot source, actor or source character is not equal to props dot target character.
Otherwise this would be damage caused to the self.
And I don't want to show damage numbers in that case.
Now if we pass this check, then I need to get the player controller.
So what I'm going to do is call you gameplay statics, get player controller.
Now we're going to need to include gameplay statics.
I'm just going to hit alt enter there.
Thank you.
Writer and get player controller requires a world context object.
I'm just going to use props, dot source character and an index and that's going to give us the locally
controlled player controller.
Now this is just an a player controller.
We need to cast it to an aura player controller.
So we're going to cast here.
And we'll cast to a Ora player controller and we'll store this in a local ora player controller pointer.
Called we'll just call it PC and we'll wrap this in an if statement to make sure that this is a valid
pointer.
And looks like I clicked on the scroll bar there.
So if this is a valid pointer, we'll wrap that and it looks like I'm missing an equal sign there.
There we go.
If this is a valid pointer, then we can show the damage number.
So we can call PC show damage number and we can pass in the damage.
Now we have local damage.
We stored it.
It's called local incoming damage.
And show damage number also requires the target character.
So we're going to get props, dot, target character.
Okay.
So if local incoming damage is greater than zero, then we check if the source character and target
character are not equal.
And then we get the player controller cast to aura player controller and call show damage number, which
will spawn that widget component.
And we can refactor this into a function if we like.
This can be show floating text or something like that.
So let's just really quickly go to Aura attribute set stage.
We'll make a nice little private function way down here called Show Floating text.
And because this is going to check both the source character and target character, I'm just going to
go ahead and pass in a const reference to F effect properties.
Call that props and then we won't even bother making any copies.
We're just going to pass that straight in.
No copies made and a float called.
Damage.
All right.
So show floating text.
We can make a definition for.
We can move this to where we were working.
Just under post gameplay effect execute.
So I'm going to control X and move it down to the bottom so we can see what we're working with.
And I'm going to take this and paste it in show floating text and instead of local incoming damage,
we'll pass in damage and we'll call show floating text in place here, passing in local incoming damage
and props.
Actually, that's backwards.
Props should be first.
So now we have show floating text.
Now this could be a const function.
Let's go ahead and clean that up by making it const.
And now show floating text handles, floating text.
And our player controller handles the replication side of this.
So really all we need to do is make sure that the player controller has a damaged text component class.
So why don't we compile launch, set that damage text component class and see if all of this works?
So I'm going to go ahead and let these editors open up and go back to blueprints and player and BP or
a player controller, and we'll set that damage text component class.
I'm going to search for damage.
Here it is.
And we created BP damage text component.
And now that's set.
Now BP damage text component.
Once we spawn it, we're going to call set damage text.
Right?
And that means we're going to update the damage text on the widget that belongs to it.
But we also want to make it clean up itself because there's going to be a lot of these spawned all over
the place.
They should all destroy themselves.
So I'm going to set a delay.
And this will be a delay for one second.
And after one second, I'm going to call destroy component.
And that way the component itself will be destroyed after we've called this.
And because it's destroying itself, it's going to destroy its own user.
Widget damage, text and damage.
Text has its animation.
And that animation lasts for it looks like one second.
So that delay of one second is perfect.
So the last thing to do is test this out by pressing play.
Looks like I got some shadow artifacts there.
Nothing a good old reset won't fix.
But let's see if our damage text will work.
I'm not seeing it.
So I'm going to need to go to Aura attribute set and let's just make sure that we're getting this far
running in debug mode so I can press play, I can do some damage.
We are making it this far.
We have some valid props and let's see what our local incoming damage is.
It's 16.
Interesting.
We can step into show floating text.
We can see if we make it this far.
It looks like we're not making it this far.
We're not making it into this if statement.
So doing another fireball or making it this far, We get this far and it looks like my props source
character is null.
We can see where we're setting that source character and set effect properties.
So let's see how we're setting that.
It looks like we're taking the source controller and calling git pawn on it so we may not be setting
source controller looks like we're taking our source ability system component and getting the player
controller from ability actor info.
But if that's null, then we're trying to get it from the source Avatar Actor Let's go ahead and just
place a breakpoint there.
I'm going to continue and launch of Fireball and we'll see what happens.
The source controller here is BP or a player controller.
That's not null.
So I'm going to continue.
We'll make it to show floating text.
Okay, So source character is null.
The player controller is not.
And I'm seeing the problem already.
It's actually a human error right here.
We're setting a local variable called source character.
We're not setting props, dot source character.
We should be setting that here.
So once we get our source controller, that is valid, but we're never actually setting props dot source
character here.
That's what we need to do.
So I'm going to go ahead and click Stop.
And right here I'm going to change this to props dot source character.
And now we should be setting that.
And I don't think I need these breakpoints, so I'm going to go ahead and run in debug mode and see
if that fixes the problem.
So that's something that I probably rushed through when I was making this function.
You may have actually not made that mistake, but now that we're actually setting that source character
in the props, we should be able to see this work.
So I'm going to open the aura player controller again.
Search for damage, make sure that that damage text component class is set and it is.
And I'll do some damage.
Okay.
So we're not seeing it.
But at least at this point, we can check to see if our player controller is valid.
And we are making it inside here this time.
So now we're calling show damage number passing in, damage and passing in a valid target character.
Let's go into the player controller now.
And actually, I'm noticing here that when we create the new object that we should not use this.
We should actually be using the target character as the outer.
We're going to be attaching it to the target character.
So that is what we'd like to set as the outer.
So with that, let's go ahead and give this a try and we'll see if this works.
And we can go up to an enemy.
And I do see floating text now.
It doesn't quite look right.
But if we look at these enemies over here, they're kind of facing this way.
We can see that their numbers are kind of angled a little bit.
It's a little hard to see, but that's because we're showing those widgets in world space.
So if we go to BP damage text comp and search for space, we see that space is set to world.
We want it in screen space.
So setting that to screen space.
Now we see those numbers looking good.
Now, if you're really perceptive, you'll notice that the letters, the numbers actually in WPP damage
text get a little bit pixelated when they get larger.
That's because we're scaling up.
See, if I zoom in a little, it's it's hard to see.
Actually, if I zoom in, it looks just fine.
It's not until we play the animation on the screen that it looks sort of pixelated.
Now, there's a way to get around that.
And the way is to never scale above a scale of one.
We can start off at a smaller scale, go up to one and then go down.
So that's a sort of way to make sure that you never get pixelated.
So instead of going from 1 to 2.5, we could go from a smaller value.
Now, if we want that same ratio, we could just take one divided by 2.25.
And we can start off at 0.44.
So instead of starting off at one, we start off at 0.44.
For both X and Y, and then we go up to one instead of 2.25.
And then we go down to 0.44.
Now that means this is a lot smaller, right?
So what we can do is scale up our actual text font by a factor of 2.25.
So instead of 26, we can take 26 times 2.25 and that would put us at about 60.
We'll make it 59.
So that is the largest that it can be, but it starts off smaller, gets bigger and gets smaller again.
Let's see how this looks.
That might be a little too large, but.
We'll see.
Yeah, that looks better.
And we have damage numbers.
Beautiful.
And that really does add quite a bit to the game.
I really like it.
Damage numbers are one of my very favorite mechanics to implement, so that looks good.
And because our size is bigger, we'll make it 58.
I do want to make sure that we have a good enough outline.
I'll make our outline size too.
Let's just test that out.
Yeah, that looks good.
And if you like, you can play with other factors like letter spacing.
It's totally up to you.
Yeah, that looks pretty good.
I might actually keep it at about 100.
Anyways, enough tweaking.
We've got it working.
It's looking great.
Now there are a few things that I'd like to do in addition to this.
Some more ambitions.
We're going to want to see if we have a critical hit, a blocked hit, any number of special combat
mechanics.
And I'd like this number to be a different color, depending on whether it was a blocked hit or a critical
hit.
And I'd also like to show some text showing if we got a blocked or a critical hit.
That's just always really exciting when we get those types of things.
And so that's some stuff to look forward to in the videos to come.
But for now, we have damage numbers.
It's looking great.
Excellent job and I'll see you in the next video.