Welcome back.
So now that we have visuals looking pretty good for our Arcane Shards ability, it's time to start thinking
about damage.
Now for Arcane Shards.
I'd like to have radial damage.
I'd like to be able to cast this.
And for each of these shards, I'd like to cause more damage.
The closer my enemies are to those individual shards, and they'll take a little bit of damage as their
distance increases to those shards.
So radial damage with a falloff.
And if we're going to be implementing damage that has a falloff, we're going to need some parameters.
Now I'd like to take a look at one engine function.
If I double shift to search I'm going to search for radial damage with falloff.
And we have a gameplay statics function.
Apply radial damage with falloff.
Now this is an interesting function.
In fact, Unreal Engines damage system itself is quite interesting.
It has damage capabilities, but it doesn't really have a built in concept of health.
You're supposed to do with these damage functions what you need for your project.
So apply radial damage with falloff is just going to perform this algorithm where it actually sees which
actors are overlapping with it.
And for any actors overlapping with it, it's going to if we scroll down to the bottom called Take Damage
on it.
So it calculates the falloff using its own algorithm, it performs the sphere query, similar to what
we've done in one of our functions and our function library.
And then it ends up calling take damage.
And take damage is a function that is virtual and can be overridden in any actor.
And so it's up to us to receive that value passed through for the damage calculated and take damage.
So apply.
Radial damage with falloff is something we can use.
We can take advantage of the built in engine capabilities, and then we can choose how to handle that
damage.
But apply radial damage with falloff requires some parameters.
It takes in a base damage amount, and it basically nerfs that damage depending on how far away the
target is from some origin, and it uses two spheres, a minimum and a maximum sphere.
One sphere has an inner radius, one sphere has an outer radius, and any actors that are within that
inner radius get the full amount of damage, that base damage passed in, and any actors that are outside
of damage outer radius, they get the minimum amount of damage passed in, and then any actors in between
those two radii have base damage with some falloff, some percentage taken off of that damage depending
on the falloff chosen.
And there are different types of falloff.
There's linear fall offs, other types of fall offs that are smoother curves, such as cubic mathematics
functions.
And if you want the details, you can always look at the implementation here in this function to see
exactly how that radial damage is calculated.
But if we're going to call this function we need some of these parameters.
Notice there's a damage falloff parameter.
And if we look at how that's used we see that it's passed into radial damage params.
If we go to this, we see that it has these base damage, minimum damage, these parameters.
Right.
And it also has get damage scale, which returns the damage done at a certain distance.
This is defined in damage events.
If we look for jet damage scale, it has a distance from epicenter.
And it checks the damage fall off here.
Sees if it's equal to zero, then it says no fall off or inside an a radius means full effect.
It returns one.
This is a scale by the way.
So we're scaling damage by this.
But notice here it's calculating an interpolated scale.
It's creating a damage scale set to one minus some value.
This is a ratio.
It's the validated distance minus validated distance inner radius.
So the space in between the inner radius and this distance divided by the validated outer radius minus
validated inner radius.
So it's scaling outer radius.
So it's setting damage scale equal to one minus this ratio here which means damage scale is going to
probably be less than one.
And then it takes that damage scale and it calls power on it.
It takes damage scale and raises it to the power of the damage fall off.
So we're taking a value that's either one or smaller.
So smaller than one say 0.25 for example.
And we're raising it to a power.
And if that power is smaller than one then damage scale is going to get smaller.
I mean, think about it.
Let's say damage scale is 0.25.
And let's say damage fall off is two.
Well, 0.25 times 0.25.
That's a quarter of a quarter, right?
If it was 0.5 then that would be half of a half.
What's half of a half?
That's a quarter.
So raising a value that's less than one to a power that's greater than one is going to make it smaller.
So if we just want to have this linear fall off, we can just have a damage fall off of one.
Otherwise if we want a harsher fall off we can use a higher value for damage fall off.
So that's how damage fall off works.
And now we understand that if we're going to be using our built in apply radial damage with fall off
and responding to it, then we're going to need some parameters.
Those parameters are going to be a radial damage minimum, a radial damage origin, a radial damage
inner and outer radius, a fall off if we want that.
Otherwise we can hard code one.
And then we have a bunch of other things, such as a damage type class.
Now we're using gameplay tags to identify our damage types, but damage types are essentially classes
that you only add static functions to if you add anything to them.
Because we never instantiate instances of them, we just use the class.
So you can use it as a static class, like an identifier, and add static functions.
If you want, you could subclass this and give it a gameplay tag.
I don't think we really have any uses for it in this project.
And of course we have a damage prevention channel if you want to prevent damage for any specific channel.
So we're going to create some radial damage parameters.
And I'd like to add those to a couple of our types.
I'm going to go to our ability types.
We have CP here.
And I'll open the header file.
And we have f damage effect params.
I'd like to add some parameters for radial damage here.
And we also have the effect context.
And this will be a good way for us to carry radial damage params with us throughout the pipeline if
we want to access it, say in exec calc damage.
So that's what I'd like to do in this video.
I'd like to create some radial damage parameters, and we're going to start by adding them to f damage
effect params.
So down here at the bottom I'm going to add a few parameters.
One will be a boolean and I'm going to call this b is radial damage.
And it'll be false by default.
So if we never set it then we'll never have radial damage.
And I'll make it blueprint red right now in addition to this boolean that we can check to see if we
even have radial damage, we can have a few radial damage parameters.
We're going to have a float.
And this can be our radial damage inner radius.
We can have a float radial damage outer radius, and we can initialize these both to zero.
And we can also have a radial damage origin, which can be an f vector.
So we'll make an f vector.
Radial damage origin.
We'll initialize it to the zero vector.
And with that, we should have enough information.
Now, if we really wanted to, we could have a radial damage minimum amount, and that could be separate
from our base damage that we have here.
But I'm just going to use zero for my minimum radial damage.
And with that let's add blueprint read.
Write to all of these in case we want to set them from within.
Blueprint.
If we make one of these structs now that we have these for radial damage properties, we're going to
want to make sure that we're setting these in our damage effect params when we make them with our gameplay
ability, or a damage gameplay ability.
And that means or a damage gameplay ability should have the option to be radial.
You see, we want radial damage for arcane shards, but for that reason I want our whole system to be
capable of radial damage.
And that way when we have another radial damage spell, we don't have to reinvent the wheel.
So I'm going to go into aura damage gameplay ability.
Here's the cpp file.
I'm going to go into the header as well.
And we're going to add some radial damage parameters.
And they're going to be identical to our damage effect parameters, namely these four here.
So I'm just going to copy them.
And over here in aura damage gameplay ability I'm going to go ahead and paste them.
The only difference is I'm only going to make them edit defaults only.
We don't need them to be blueprint read write.
So I'm going to change their U properties.
Now I'm going to take these four and set them in our aura damaged gameplay ability function make damage
effect params from class defaults.
We're going to go ahead and set these, but really only if this is radial damage.
So we're going to first check if B is radial damage.
If that's true then we can go ahead and set the radial damage parameters.
We're going to say params.
Dot b is radial damage equals B is radial damage.
We'll carry that across.
We'll have params dot radial damage origin.
We'll set that to radial damage origin and two more params dot radial damage inner radius.
We'll set that to radial damage inner radius and params dot radial damage outer radius.
We'll set that to radial damage outer radius.
And now our function for making these params sets them based on the ability class itself.
And then we can carry those along whenever we need to apply a gameplay effect.
Now, if we're going to apply a gameplay effect and make use of these, we need to decide how.
I'd like to set them in the effect context.
And that way we can check the effect context to see if it's radial damage at any point in our damage
pipeline.
Another alternative is to make these set by color magnitudes, but I'm opting for setting them in the
effect context.
And that way we don't have to crowd up our gameplay effect spec with too many set by color magnitudes,
and we don't have to create too many gameplay tags just for those reasons.
So I'm going to go back to aura ability types, and we're going to add these into our gameplay effect
context.
So we're going to have four new variables here.
Which means we're going to have a few steps including determining how these should be serialized.
So just after knockback force we're going to add these four variables.
And if I'd like to be lazy I could go back to aura damage gameplay ability or just scroll up to.
F damage effect params and I can just copy them from here.
Starting it is radial damage so I'm going to do that.
And I'm going to paste them down here.
Only they're not going to be blueprint read write.
They're just going to be you properties.
So now we have a new bool two floats and an F vector.
And they're all initialized.
So because these are protected we're going to need getters and setters for each of them starting with
the Boolean and working our way down.
So I'm just going to add the getter here.
It's going to be bool is radial damage const return B is radial damage.
And while I'm at it, I'll make this better as well.
Void set is radial damage, takes a bool, be in is radial damage, and sets B is radial damage to be
in is radial damage.
So this is a bit monotonous.
So I can time lapse creating the getters and setters for the remaining three.
So let's do that.
All right.
So I have my getters and my setters.
Now the next step is to net serialize these.
Now I'm going to go into our ability types Datcp.
And we're going to flip our bits by checking our member variables.
Now the first one I'm going to check here is is radial damage.
So I'm going to say if B is radial damage then I'm going to flip the 16th bit.
And I'm not going to bother flipping any of the other bits.
If is radial damage is false.
So we're going to check not only are these zero or the zero vector, but we're also going to check is
radial damage as well.
We can do that.
We can also just check them here.
Inside of B is radial damage.
If we wanted to do that, we could say if radial damage inner radius is greater than zero.
In that case, we can flip the 17th bit.
We can also check the outer radius.
So we could change this to outer.
And flip the 18th bit, and then we can finally check if radial damage origin dot is zero and we can
put a knot here.
So if it's not zero then we'll flip the 19th bit just like that.
Now if you don't like nesting them, if it seems a little bit confusing, you could take these out and
just check both these conditions and have an end and check B is radial damage.
You could do it that way too.
So with that we can now serialize and we need serialized bits to accommodate now for 19 bits.
So we're going to pass a 19 into that.
And after that we can check these new bits 16, 17, 18 and 19.
So let's go all the way down and check those.
So first we're going to check if rep bits.
We're going to use and bitwise and that is.
One left shift 16.
And while I'm at it, I'm just going to copy this before I put anything in there and check 17, 18 and
19.
So if we flipped 16, we can go back up and look.
That's going to be.
For is radial damage.
So what we'll do is we'll take the archive.
And will archive be is radial damage.
Now, if this bit has not been flipped, there's no point in checking these other four.
So if you wanted to, you could also put these inside here as well.
Now we can check if rep bits has its 17th bit flipped and the 17th bit, if we remind ourselves up here
is radial damage inner radius.
So we can archive that float.
So we'll say are radial damage inner radius.
And 18 is the outer radius.
So are radial damage.
Outer radius.
And now for the origin which is an f vector.
So how are we going to handle the f vector.
Well we've seen that we can handle.
Net serialize for f vectors using the f vector.
Net serialize.
So we're going to do that with our radial damage origin.
We're going to call.
Net serialize.
And we're going to pass in the archive.
We're going to pass in the map and be out.
Success.
And with that, we're serializing our radial damage parameters.
Okay, so that's a good start.
We now know that our effect context can accommodate for radial damage parameters, as well as our f
damage effect parameters as well.
And our damage gameplay ability now has radial parameters.
And we'll just set that B is radial damage.
It's set to false by default.
And if we never set it to true then the context doesn't carry it along.
It doesn't even serialize it for replication.
So we're not picking up any bandwidth across the net if we're not using these parameters.
And if B is radial, damage is false.
We don't serialize any of these four.
So that's a nice optimization.
So with that we can go ahead and compile.
Looks like I have the editor open.
I'm going to close it and just compile.
Make sure I have no errors.
And with that, we're now set up and ready to use our radial damage parameters.
And we'll do that next.