Okay, so you're sitting at your desk and the project manager for your video game walks up and says,
hey, lovely developer, I need knockback.
And you look at your project manager and say, uh, what's knockback?
And the PM says knockback.
You know, when an enemy gets hit, it flies in the air, kind of like the death impulse.
And you say, oh, knockback.
Right.
And your PM says, that's right, knockback.
I need knockback and I know that you are capable of implementing that.
The project manager then proceeds to tell you more details that you don't really know, such as well,
damage abilities should have a knockback magnitude and when taking damage if it's not fatal knockback.
And just so we have knockback well defined, it's when you launch the character into the air when they
get hit.
So for when an enemy takes fatal damage we have the death impulse, right?
But when damage is not fatal, we are going to have a knockback.
Now there should be a knockback chance that could be a parameter, and we can use that knockback chance
to see if there was a successful knockback and then some damage abilities will not have knockback,
their knockback chance will be zero, and so on.
Now before your turns and walks away they say.
Oh, and by the way, here's a hint use launch character and not add impulse.
And then they turn and walk away, at which point you turn to your coworker and say, hey, why should
we use launch character instead of add impulse?
And your coworker turns to you and says, well, I think it's because you can use launch character without
having to simulate physics and ragdoll the character and you say, oh, okay, that makes sense.
So for this quest, you're going to implement knockback.
There are a lot of steps to this, and you may find yourself needing to make some of the decisions,
but I think you're capable of it.
So pause the video and conquer knockback now.
Okay so we need knockback.
I'm going to start in the ability.
So I'm going to close all tabs and go to my aura damage gameplay ability.
So I'll go ahead and get the header file open.
And I'm going to add a couple of parameters for knockback.
Now we have a death impulse magnitude I'm going to have a knockback force magnitude.
So whereas death impulse is an impulse are knockback is going to be a force.
So I'm going to say knockback force magnitude.
And it turned out that 60 was too low for death impulse.
So I'm going to set those both to a thousand.
But we'll almost certainly need to tweak this.
Now we can also have a knock back chance.
So if we want to do that we can say float, knock back chance.
And I'm going to set it to zero by default.
So it's more like something you enable rather than something that's enabled by default.
If it's zero, then there's no need for knockback.
Okay, so now we have a knockback magnitude.
And just like death impulse, we're going to have a couple of parameters in our damage effect params.
And that's going to be in our aura ability types.
So let's open that.
And right here in damage effect params, we're going to go ahead and add not only a knockback force
magnitude, but a knockback force vector.
So I'm just going to copy both the death impulse magnitude and death impulse.
And this will be knockback force magnitude.
By the way, it's okay if you didn't call yours the same.
You could have just said knock back magnitude.
That's totally fine.
And instead of death impulse, we're going to have knock back force.
A force is a vector.
Okay.
So we have those properties.
Now we know that we're going to need something in the effect context right.
For the knock back force.
So really what we're going to do is just copy the knock back force here and paste it down in our effect
context, just under death impulse.
And once we have this set in the in the effect context, we know that it's going to have the correct
magnitude by this time.
Now because we have knock back force, we're going to have to handle it in.
Net serialize.
And by this point this is a pretty well known process.
We're going to take the next bit in our rep bits.
That's going to be 15.
We're going to flip that bit and we're checking knock back force.
Now to see if it's zero.
And then we come down and check if that bit was flipped.
Just like we did with Death Impulse.
So we're checking the 15th bit.
And then we're taking knockback force and calling net serialize on it.
Piece of cake.
And we have to make sure that we don't forget to scroll back up to serialized bits.
And now that we're flipping 15 bits, we have to serialize all 15 of those bits.
So this is something that we'll have to come back to each time we add to the effect context and update
that as well.
Okay.
Now our effect context is going to need a getter and setter for this.
So we're going to make a getter that returns an f vector called get knockback force.
And it's going to return knockback force.
And we'll have a setter void set knockback force.
Which takes a const f vector reference called enforce.
And we'll set knockback force equal to n force.
And we have those now.
Now we also are going to want a couple functions on our ability system library.
So we'll go up to get death impulse.
We'll copy it.
We'll paste it and we'll have get knockback force.
And we can go ahead and define this and we'll define it just like get death impulse.
The only difference is we're going to call get knockback force on the effect context.
And then we can scroll down to get set death impulse.
And we can make one for set knockback force.
So we'll call it set knockback force.
And we'll call the input parameter in force.
And we'll go ahead and generate the definition.
Looks like Ryder gave me an inline one.
So I'm going to cut it and put it into the cpp file.
We're going to find set death impulse.
We'll copy set knockback force just underneath it.
Copy the contents from Set Death Impulse and change this to.
Set.
Knockback force passing in.
In force.
Now we have those functions on the ability system library.
Okay, great.
Next we need to make sure that our effect params carries that knockback force magnitude.
And then also that we set the knockback force in it when we should set it.
So back to aura damage gameplay ability.
We're going to go into the cpp file to make damage effect params from class defaults, and we'll set
that knockback force.
We'll get it from the ability.
So we're going to say params dot knockback force equals knockback force magnitude.
And in fact we have to set the magnitude.
Actually knockback force magnitude equals knockback force magnitude.
Now we also have the knockback force chance right.
We also made a knockback chance actually.
So we could add that as well in aura ability types.
In damage effect params we can just add another float for the knockback chance also.
So I'm going to go ahead and make that two knockback chance equals zero.
And we'll set that here in make damage effect params from class defaults.
So we'll say params dot knockback chance.
Equals knockback chance.
There we go.
So now we can start the whole thing rolling by setting the knockback force in the effect context.
And I'm going to do this in the aura projectile.
So I'm going to go ahead and get that open.
I'll get the CP file open for it or a projectile.
And we'll see that right here.
We're setting death impulse.
We can also set the knockback force.
Let's calculate that though we're going to say const f vector knockback force.
Equals get actor forward vector.
Times.
Damage effect.
Params.
Knockback.
Force.
But here's the thing.
We have a knockback chance, don't we?
So what we should do is roll the dice.
We should see if we should get a knockback.
So we're going to say const bool be knockback equals f math rand range.
From 1 to 100.
We'll see if that's less than damage effect params.
Knockback.
Chance.
And we'll only do this if b knockback is true.
So if B knockback is true, then we should calculate knockback force and we can take our damage effect
params dot knockback force.
And we can set that here equal to knockback force.
Then when we call apply damage effect we'll be passing in the knockback force along with that.
So that's only if we got a successful knockback and then apply damage effect can handle what happens.
So an aura ability system library we can go into apply damage effect and we can set that knockback force.
We'll do it right after setting the death impulse.
So set knockback force.
We'll get the effect context handle and we'll get damage effect params dot knockback force.
So now that's set on the context handle.
At that point we'll be applying the damage effect with a knockback force.
And the last piece of this puzzle is to use that knockback force in the attribute set.
Of course we need to add the capability there of using it, but we can go to the attribute set and at
least find out where we should be reacting here.
So I'm going to go into aura attribute set dot cp find handle incoming damage.
Here it is.
And find where we have be fatal.
And right here is where we do the death impulse stuff.
And in the else case is where we can do the knockback force.
So we're doing the effects dot hit react while there hit reacting.
They might be knocking back right.
But we could also take over.
Make sure they don't hit react if there's a knockback force.
But that's up to us.
It doesn't really matter.
The the animations being played while in knockback while in the air.
That just depends on what we want to be played.
But either way, we're going to handle knockback here, and what we need to do is see if that knockback
force is greater than zero.
So we're going to check if and we're going to get that effect context.
So from props we'll get the effect context handle.
And how do we get that knockback force.
Actually we have a function for that.
So I'm going to cut what I just wrote and use your aura ability system library.
Get knockback force passing in the effect context handle.
Now this returns actually a knockback force which is a vector.
So I'm just going to make a const f vector reference called knockback force.
And I'll just set that equal to this.
That way I don't have too long of a conditional in here.
And then I'm going to check the knockback force and see if it's not zero.
So I'll get knockback force and I'll call I'll use is nearly zero.
And I'll put a tolerance in there.
Something that's small but not that small, something like ten.
So if knockback force is near zero with this tolerance, we could even make it smaller like one.
Either way, if it's almost zero, we won't get into this if statement.
But if it is zero, then we know what we can do.
We can launch the character.
Now in this case we have the character already and the function launch character is already on the character
class.
So we could just take props, dot target character and simply call launch character on it.
No need to go through the trouble to make interface functions.
If you did, that's fine.
It was good practice, but you don't actually need to.
In this case, you can just call launch character directly on the character and pass in launch velocity,
we're going to pass in knockback force for the launch velocity and x y override along with z override.
This is how we can control the knockback force a little bit.
If we wanted to launch the character in the x y direction, we would pass in true.
And if we don't want to launch the character in the Z, we would pass in false there.
But I do want to launch the character in the Z as well, so I'm going to pass in both true true for
launch character.
Now this is going to be basically in the direction that the Firebolt was flying.
But I'd like knockback to have a little bit of a pitch override.
In other words, I like my knockback force to be angled up just a bit so we could really enjoy seeing
those enemies fly all around.
And for that reason, I'm going to go back to Aura Projectile when we're setting our.
Knockback force.
And as we got our actor forward vector here, we can rotate that actors forward vector.
I'd like to say const f vector knockback direction equals get actor forward vector.
But on this vector I'm going to call rotate angle axis, which will allow me to rotate the vector about
some axis by some angle.
And I'd like to rotate it upward by oh say 45 degrees.
And the axis I'd like to rotate it about is the right vector.
Get actor right vector.
And if we want this to be a basically pitch override completely, we could zero out the forward vector.
So that's one way to do it.
But we could also say get actor rotation.
And from that rotation we can get the vector.
And if we want it get actor rotation.
We could create an F rotator called rotation.
And we could take this rotation dot pitch and set it to 45 degrees.
So it's pointing 45 degrees up.
And then we could take that knockback direction and make it out of rotation.
We could say that knockback direction equals rotation vector.
That would work too.
And then we could take knockback direction and use it instead of the forward vector for our fireball.
That's another thing we could do.
And I'm just seeing now that I use knockback force.
This should actually be knockback force magnitude, right?
Because otherwise we'd be setting knockback force to knockback force times knockback direction.
Knockback force is a zero vector.
So we'd be setting this to zero.
So now we're setting this to a knockback force.
Let's go ahead and test this out.
Okay.
So let's go into Firebolt and let's change the knockback chance to 100.
So 100% chance to knock back.
And let's just see what happens.
Ooh, look at that.
Awesome.
All right.
Oh, and I just realized something.
If we have knockback now, enemies can fly over, and we need to take care of that, don't we?
Either the enemy should die shortly after falling off the edge, or they should just bounce and come
back in.
I personally like the option of having them bounce and come back in, because if you can knock an enemy
over and kill them that way, it's kind of something that could be exploited in a way.
So I kind of don't like that possibility.
But there goes an enemy and he's flying away and his head's on fire.
Cool.
So okay, so knockback force magnitude of 1000 is a bit much.
I think we can put it to 500 or so.
And if we set the knockback chance down to say 20, then we won't always get a knockback.
But every once in a while we will.
Pretty cool.
Now if we want to see knock back on aura two, we can do that.
And all we'd have to do is go into blueprints, ability system, enemy abilities and check out melee
and ranged attack and give them a knockback chance.
I'd like the melee attack to maybe have a knockback chance of 20 with a knockback force magnitude of
500.
We'll do the ranged attack.
The ranged attack will be a knockback chance of 10% knockback force magnitude of 400.
But before we go on and move on with our lives, I'm going to set the melee knockback chance to 100
just for testing and see what aura looks like knocked back.
Okay not seeing the knockback happen.
Oh, and of course, we're not setting the knockback vector, are we?
We're only setting the knockback vector in the projectile on sphere overlap.
So it's up to us to set that knockback force.
So for example, here in melee attack, if we're about to say cause damage for example, that would
be a good spot to set that knockback force.
So here we have cause damage in the aura damage gameplay ability.
Why don't we take a look at that.
Let's go to Aura Damage gameplay ability I'm going to find cause damage.
Here it is.
So here is where we know we're going to cause damage.
This would be an appropriate place to perhaps do it, but rather than just setting a knockback force
for every damage gameplay ability, I'd like this to be custom per damage gameplay ability.
So really cause damage would be a little bit easier to work with when it comes to setting things like
parameters on the effect context, it would be a lot easier if caused damage could take in an effect
context.
So if we go back to our Aura ability system library, for example, and look at applied damage effect,
we'll see that.
First we make an effect context handle.
Right.
And then we make a spec handle using not only the damage gameplay effect class, but also the ability
level and the effect context handle.
So making a spec handle this way we could specify the effect context using an effect context handle.
So using that cause damage function, we kind of bypass the effect context altogether.
So there are a number of ways we can handle something like this.
For one, we could make some damage effect params and we can go through the pipeline we created for
aura projectile, namely make these params.
First we can set the target for our damage effect and then we can use our ability system blueprint library
function apply damage effect.
If we wanted to make it a little bit easier to set that knockback force and any other things that we
want to set.
So really, if we're going to make a damage effect params from class defaults, we could expose this
function to blueprint.
For example, we can make it a blueprint pure.
Why don't we see what happens if we do that?
So I'm going to go ahead and close the editor.
We'll save everything first and we'll launch after having made this blueprint here.
And once we have damage effect params, we know that the ability system library has applied damage effect.
So we could go about this a number of ways.
We can make those damage effect params in blueprint, expose them to blueprint and set that knockback
force in blueprint and then call Apply Damage effect in blueprint.
Or we could make a blueprint callable function to do all of that for us.
I'd like to show both solutions.
So in aura ability types I'd like to expose all of these to blueprint.
So I'll make them all blueprint read right.
So for world context object damage effect class source ASC and target ASC base damage ability level
damage type debuff chance debuff damage duration, frequency, impulse magnitude, death impulse, knockback
force magnitude, knockback force chance, and knockback force.
Now we could set those in the blueprint if we wanted to, but in addition to that, I'd like a function
that can do all this for us.
In fact, we can calculate that knockback force at least its direction if we know the knockback force
magnitude and the effect params based on the avatar actor's location and the target actor's location.
Right?
In other words, what we could do is we could have a function on or a damage gameplay ability like this.
Make damage effect params from class defaults that takes the target actor if it's not a null pointer,
and calculates the knockback direction and the death impulse direction, and then if we don't set those
later and overwrite them, then they'll at least have direction set for them.
So here's what I'm going to do.
If target actor is not a null pointer, then way down here.
First we'll check if we'll see if it's valid is valid.
Target actor first and foremost.
And if it is valid, then I'd like to calculate a direction from the enemy to the target actor.
Now the enemy is going to be the avatar actor of the owning ability system component.
So we can call get avatar actor from actor info.
And I'd like to get a vector from the avatar to the target, which will be a const f vector.
We'll call it to target and it'll be target actor get actor location minus get avatar actor from actor
info get actor location.
Now this is not a normalized vector.
So I'm going to wrap it in parentheses and call get safe normal.
So we have a two target which is a normalized vector with the direction from the avatar to the target.
Now if we wanted to give it a pitch override we could we could do it in a number of ways.
We could make this a f rotator called rotation.
And instead of calling get safe normal.
We could call rotation, take rotation pitch and set that to 45.
Just override it.
That could be a parameter and the damage effect params.
And then we can say const f vector.
To target equals rotation vector.
That's something we could do.
And then we could set our knockback force and our death impulse.
If we like, we can say params dot death impulse.
We'll do that first equals to target.
Times death impulse magnitude and we can say params dot knockback force equals to target times knockback
force magnitude.
So really, we're just making this set those directions to a default with a pitch override of 45 from
the avatar to the target, but only if that target actor is set.
And then we wouldn't have to go in and set it right.
So we know that these will be set, of course, for the fireball we override those, but at least they're
set by default if there is a target.
So what I'm going to do in the melee attack here is I'm going to call make damage effect params from
class defaults.
And I know that this has a knockback force, but I exposed these parameters.
If I wanted to say break damage effect params, I could.
I could expand it and see all of them and if I wanted, I could drag off of one and say make damage
effect params and then I could go in and take my knockback force and calculate it here in blueprint.
If I wanted to do that, and then plug it in right here and then anything that I want to take from what's
created by this function here, which we made blueprint pure, we could just pass it on in and overwrite
anything here that we want to overwrite like this.
So I figured I'd show you this way too.
So we have flexibility in blueprint.
But at the same time I know my knockback force is set so I don't have to break it.
After all, I can just take it and then we can use an ability system component.
Apply damage effect is what it's called and that takes the damage effect params.
We have it there and when we make the damage effect params from class defaults.
Here in the for loop we have the array element which is our target actor.
We are passing in to cause damage.
Now we can pass that in to make damage effect params from class defaults and instead of causing damage
with cause damage, we can cause damage with apply damage effect on the ability system blueprint library.
And now we know the knockback force will be applied.
So let's test that out.
Boom, and we have a knock back and we can do the same thing for the ranged attack as well.
So the ranged attack, I believe, is spawning a projectile actually, and that is using spawn projectile
on the aura projectile spell, which is actually making those effect parameters.
So we could set a knockback force of 100 here.
And we should see that knockback force from the slingshot rocks.
And we do.
Cool.
So now that we know that's working, the only thing left is, well, it kind of looks funny for aura
when she's in the air.
Right.
I'm not a big fan of her doing the running animation while she's in the air.
We need to fix that.
And we'll fix that real quick too.
So we'll go to assets, characters, aura animations.
And no, we need to go to blueprints.
Character.
Aura.
AB aura.
That's what I need.
Her animation blueprint.
And in the event graph, we should be calculating whether or not Ora is in the air.
And we can get that from character movement.
So I'm just going to make this a sequence node.
The first thing we do is the stuff that we're doing here set the ground speed and should move.
But in addition to this, I'd like to get the character movement and call is falling, and if is falling
is true, then we'll use a different animation.
So I'm going to promote this to a variable called is falling.
And we'll be setting that too.
And then we can go into main states, and we can have a state here for when or is in the air.
So if or is in the air I want to play an in air animation.
Here's one.
I think this one will work.
So I'm going to go ahead and drag that in and get a state machine for in air.
And really if aura is falling, if that is falling Boolean is true.
I want to transition into an air whether we're idling, whether we're running either one.
I want a transition to in air.
Now I could drag from idle to an air and from running to in air and add the same transition rule is
falling for those.
That's one way.
Another way is to right click and use a state alias.
We can call this to in air and bring it into an air.
And then to an air can serve as an arrow coming from any number of nodes.
By checking the checkboxes over here.
So if we check running an Idle then this is the same as having two arrows coming to an air, both of
which will have the same transition rule.
Now I can open that transition rule and I can use is falling for that.
So that makes it easier.
Of course we need to transition out of in air.
So if we're no longer falling we'll transition back.
And this can be for is falling.
Not so not boolean.
We can transition there.
We could even have a to idol as well if we wanted one of those, and then we could transition to idol
from any states.
I'll just show you what that looks like.
We'll just do a state alias called to idle, and this goes to idle, and it'll have that transition
rule there.
That's fine if you want to do it either way, I'm just going to have one here.
And use is falling with a not boolean hooked in there.
And now when is falling?
We should see.
There we go.
We're in the air.
All right.
Now.
It's a little annoying when the enemies have 100% knockback.
That's not what I want.
That's just for testing purposes.
We'll be setting that knockback chance down.
And realistically, for games you would have like 0% knockback chance for most of your enemies and then
some of your special enemies would have knockback, right?
So you don't have to have knockback on all enemies.
You could subclass your abilities for individual enemies and set those parameters differently for them.
I'm going to have a knockback chance of, let's say 10% for the ranged attacker, but the knockback
force magnitude could be a little higher 500 and the melee attack can have a knockback chance.
And that's going to be probably, I'd say 1,520%.
And with that we have knockback.
And knockback works both ways.
And auras.
Firebolt spell.
I'd like to have at least a little bit more knockback than that.
So in GA firebolts knockback chance, I think 20 actually is fine.
We'll leave it at 20 and 20% of the time we'll see them knock back.
There was a knock back.
Great.
And the last, last, last thing I know, this lecture is getting long, but the very last thing is
I wanted to make sure that enemies can't fly off the map.
And for that, I'm going to get a blocking volume.
So I'm going to get volumes.
We're going to get a blocking volume.
I'm going to drag one in and I'm just going to go to perspective top and get that blocking volume.
Right about there.
I don't want it encroaching into the play area.
So it's going to be here.
And I'm going to make it this big.
I'm going to duplicate it and bring one down here.
I'm going to duplicate it again with snapping on and bring one up here.
And one here.
I just don't want them flying off the edge, that's all.
So going back to perspective, we can see we got some blocking volumes.
Oh and I'm going to need to make them bigger in the Z dimension as well.
So I'll go ahead and select all four.
Not that volume.
One, two, three.
For press R and we'll scale them up nice and big.
That should do it.
And with that now we won't have the problem of enemies flying off the edge.
All right.
Awesome job in this lecture and I'll see you in the next one.