Welcome back.
So things are coming right along.
We have our enemies approaching.
We have our melee enemies attacking, but so far it just plays an attack montage, although we do have
that location at the tip of the spear, as indicated by our debug red sphere, what I'd like to do is
use that location and get all actors within the radius of that point, that spear point, so that we
can do things to them like apply gameplay effects and so on.
Now this is going to involve some kind of query.
We need to see what actors exist within some sphere of a particular radius.
Now finding all actors or players within a given sphere is quite useful.
It's one of those types of things that you would add to a blueprint function library because because
you could use it for multiple purposes.
You could use it for what we want to use it for here.
In our case, we'd like to say cause a damage gameplay effect to all actors inside of the sphere, but
we could use it for a number of things.
You could probably think of 4 or 5 different use cases for something like this.
So we're going to make a nice static function to add to our function library for this.
Now I'd like to have this function check to see if any actors within that radius are still alive, because
I don't really care about any actors within the radius that have already died.
Now we've already introduced the concept of death to our project.
In fact, our combat interface has a die function and it'd be handy if we could have interface functions
to check to see if we have died, to see if the thing that implements that interface has died.
Now, another thing that I think would be extremely useful is if our combat interface could return the
Avatar actor, the actor associated with the combat interface that implements it, basically its owner.
So that would be nice.
And we've seen that when we make these functions Blueprint Native event that we could execute the static
function, the execute version of it without having to cast to the interface.
We saw this in our projectile spell we had execute get combat socket location.
This exists because we made that blueprint native event.
We can do that same thing for functions on the combat interface to get whether our actor has died and
to also get the actor itself an avatar, if you will.
Now for that reason, I'd like to make two blueprint callable functions.
One to get whether or not the player or character has died and one for getting the avatar.
So the first one I'm going to get is a boolean called is Dead So Is Dead will be Blueprint Native event
Blueprint Callable.
Let's go ahead and mark it as such and the next one will return an A actor pointer and this is going
to be called get Avatar.
So get Avatar will also be Blueprint Native event Blueprint callable.
Now these are simple getters, so there's really no reason that they can't be const functions.
Let's go ahead and mark them both.
Const.
And we just need to override these.
We can override them in the or a character base class.
So let's go there and override them.
Here we're going to have a virtual BOOL.
Returning function is dead, but we have to override the implementation version and we're also going
to have a virtual a actor returning function called Get Avatar.
That's also the implementation version We must override.
These, by the way, are functions belonging to the combat interface.
So just like we did in our enemy class or an enemy where we have this combat interface, we could do
the same thing in or a character base.
We can say this is the combat interface and right here is the end of the combat interface.
We could do that.
That would allow us to put those functions there.
These are all public, by the way.
And here we have get hit, react montage.
We have die and get player level.
These are all functions belonging to this interface.
We could move them into this comment and again, they're public so we could actually move these all
up to the public section.
So I'm just going to quickly do that, move these up to the public section like so, and if we missed
any, we can move them in there later.
But we do need to implement these two functions.
So we're going to generate function definitions and what are we going to return for is dead and get
Avatar.
Well, get Avatar is the easiest one.
It's going to return this.
And in fact this can't be const as the function cannot convert from const A or a character base pointer
to type a actor.
So we'll go ahead and just remove const from this and from the declaration here and from combat interface.
That'll take care of that.
Now for is dead.
Now we have a die function, don't we?
And if we want is dead to return that status, we can have a variable for whether or not this particular
character has died.
So we can go ahead and just add that variable here.
I'll go ahead and make it right here in the protected section.
Bool, be dead.
We'll set it to false by default and we have a handy is dead function that will return that.
Let's go ahead and return that here.
Return be dead.
Now be dead is never set so we can always set that.
We only really need to set it to true if the character dies.
Now we don't have to make this a replicated variable.
If we set it in multicast, handle death because then it will be set everywhere.
Clients and server.
So we can just set it here.
Be dead equals.
True.
So now we have two nice blueprint native events.
They're blueprint callable is dead and get Avatar.
Let's just compile.
Make sure we haven't done anything too reckless.
And with a successful compile, we're ready to head over to our Blueprint function library, where we
can make use of these functions and create a function for getting all live players within a radius.
So let's go to that.
I'm actually going to close all tabs at this point and go to public ability system or ability system
library dot H.
We're going to make a new static function to add to our library.
So what exactly is this function going to return?
What's it going to take?
Well, a function to get all live players within a radius is clearly going to need a world context object.
I'd also like to pass in an array of actors that we can fill out.
So this will be a static void function.
I'm going to call it get live players within radius.
I'd like it to be blueprint callable, so I'm going to give it that new function macro right off the
bat.
And for category, I'm just going to call this category gameplay mechanics as I don't really have a
better name for it.
Now get live players within radius will take a world context object.
We're definitely going to need that.
I'm also going to want to fill in a t array of actor pointers, so we're going to pass that in by reference.
T array of type A actor pointer.
We're going to pass it in by reference and I'm going to call this out.
Overlapping actors.
Now, whenever doing something like this, we can always ignore some actors.
We can provide a t array of actors that we can ignore.
So I'm going to provide a const reference to a t array of actor pointers called actors to ignore.
That way, if we do want to ignore any actors, we can.
Now we're going to get all live players within a radius, so we're going to need that radius.
So I'll pass in radius and we need all live players within radius of some location, right?
So we're going to pass in a const vector reference called Sphere location.
So we're getting all live players within some invisible sphere.
We need that sphere location more specifically, it actually is a sphere origin, the center of some
imaginary sphere.
So now all that's left is to decide how to implement this function.
Let's generate the definition for it.
Get all live players within radius and let's decide how we're going to do this.
So the way I'd like to do this is to perform some kind of query, some kind of check for a sphere in
the world.
And first of all, get all actors inside of that sphere.
So how do we do that?
Well, I know of some functions that exist in the engine, but a lot of people ask me when I just come
up with this magic function that exists in the engine.
How did you know that?
How did you figure that out?
So for something like this, if I just give you the function, I should tell you, Well, how did I
figure that out?
How do I already know that?
Well, the first thing I do, if I don't already know, is I try to think what exists in the engine
that I do know about that does something similar.
Now, I personally already know about a couple of things in the engine.
So if I say, Well, I know about this function and you didn't know about that.
Well, yes, that's something I knew that you didn't.
But that comes with experience.
So the more experience you get, the more things you will do, the more functions, you know, that
exist in the engine.
So, you know, with practice, you start to gain that kind of knowledge.
But one of the things that I know exists in the engine is a nice function for applying radial damage
with a fall off.
You see, if you want to apply radial damage with a fall off in Unreal Engine, that's clearly going
to involve seeing what actors are nearby and applying damage to each of those according to their distance
from some origin, some sphere origin.
So it would make sense that a function for applying radial damage with a fall off would query some kind
of sphere to see what actors are overlapping.
Now in writer I can hit shift twice and I can search.
In Visual Studio it's control shift F where I could search the whole solution and I'm going to search
for apply radial damage.
With fall off.
And I'll see the gameplay statics pops up.
First thing, I'm going to double click on that.
And here we have apply radial damage with falloff.
So here's an example of a function that's already doing, at least in part, some of what we want to
do, right?
Let's look through this function line by line and see what it's doing.
First, it's creating a struct of something called F collision query params.
Now it's calling it sphere params interesting, and it's taking in something called scene query.
Stat looks like a macro, right?
And it says in stat id along with a boolean called Trace complex, which it's set to false.
And then this damage causer looks like it's an actor, so sphere params might be something we want to
create.
Now check this out.
Sphere params has add ignored actors taking in a const array reference called in ignore actors.
So we have an array of actors we'd like to ignore.
So that's a nice function to know about as well.
Now the next line says query scene to see what we hit and it has a t array of something called F overlap
result.
Sounds kind of like F hit result, right?
It's called overlaps.
And this function gets the world with get world from context object and from that world it calls overlap
multi by object type.
Now what this does is it passes in that overlaps that t array of overlap results.
It also takes a position looks like origin is passed in here.
It takes a rotation in the form of a quaternion and also an f collision object query params.
And what's being passed in here?
Well, it looks like it's being created right here in f collision object query params which seems to
have a constructor that takes in this f collision object query params init type all dynamic objects.
Interesting.
Now after that input it takes an f collision shape.
So there's this f collision shape type and it looks like it has a static function make sphere which
takes a radius.
So that's how we can specify that this function overlap.
Multi by object type can create some invisible sphere and check to see if any actors overlap with it.
And then that last input is an f collision query params for which sphere params is passed in.
That's that sphere params that was created here.
So really this overlap multi by object type is performing most of the work here with a few parameters
that are set.
Now that's pretty much all we're interested in here.
The rest of this stuff appears to be related to actually causing damage.
All that we really cared about was this right?
We wanted to overlap with stuff.
So now that we've seen an example of how to get all overlapping actors in some kind of a radius, we
can do something similar in our static function library.
So let's think about how to do that.
Now I'm going to keep gameplay statics open so we can use it as a reference.
The first thing it does is creates an f collision query params.
Now the most important thing that I see here is be trace complex.
We don't want to trace against complex collision.
I rarely do.
I want to trace against simple collision things like the physics asset and what have you.
Now it is taking in a damage causer actor for this collision query params constructor, but I suspect
that there are multiple constructors for this collision query params there usually are for most types
in the engine, so let's see what we can get away with creating our own collision query params called
sphere params.
So I'm going to make an f collision query params called sphere params.
Now if I just hit semicolon I see that well look, it has a default constructor.
I don't have to really pass anything into it, but if I right click on this type and I go to it.
Then I see that it has a few different things.
It has a number of variables.
And if we scroll down enough, we can see that it has a constructor.
It actually has a constructor that takes a number of inputs, but they all have default values.
So we can get away with not passing any in.
That's what's happening here.
Now what do we care about?
Well, we do want to trace against only simple collision, but that's already set there.
We have a mobility type.
What does that do?
Well, it's an E query mobility type.
And if we're really interested in that, we can go to the definition of that and see that we have any
static and dynamic.
Static says any shape that's considered static by physics.
Static mobility.
So this is actually an old comment that dates back to the time when Unreal Engine used physics as its
physics engine.
Unreal Engine now uses chaos as its physics engine, so this is interesting.
It's one of those relics of code that was never updated.
But nevertheless, for any objects that are simulating physics, if they're static or dynamic, we can
only trace for those kinds of objects based on their mobility, or we can trace for any.
Right.
So that's interesting.
We can set that mobility type.
Okay, back to our collision query params.
It's in this same header file so I can search for f collision query params and go back down to this
constructor here.
So we have mobility type.
We also have a trace tag.
It says tag used to provide extra information or filtering for debugging of the trace.
I'm not too concerned with that.
We have find initial overlaps that's set to true by default.
It says whether we want to find out initial overlap or not.
If true, we'll return.
If this was an initial overlap, that's something we do want.
True because we're going to do the query and if anything was already overlapping, we want that.
Now there's a number of other things, and I think the most important thing here is tracing complex
and the mobility type.
I'm not really too concerned with any of this other stuff.
So what I'm going to do is set these two properties.
It looks like they're the first two, so I could pass in a false.
And let's just say I only want dynamic objects.
Really.
I could just leave it at any if I want to.
We're going to check those objects anyway, make sure that they implement our interface and that they're
alive.
So yeah, really, it doesn't matter.
We don't have to set any of these, so I'm just going to leave it at sphere params.
It has its settings set to my liking already.
Now, I may have spoken too soon because we do have our actors to ignore, don't we?
So that's really the only thing that I'd like to set.
I'm going to take my sphere params and call that function, add ignored actors and pass in actors to
ignore for that.
And after that I have my sphere params.
All right, let's go back to gameplay statics and see the next thing we did.
So after add ignored actors.
Next there was a t array of f overlap results and we then called overlap multi by object type on the
world.
So that's what we're going to want to do.
So I'm just going to copy the line declaring the t array and come back to my function and paste it.
So I have this overlaps and I'm going to want to get the world.
So I'm going to have an if statement and make a const New World Pointer and I'm going to call it World
and set it equal to what was the function.
Let's go look.
It's getting it from engine.
It's saying G engine, get world from context object.
Now, this function requires a world context object as well as an E get world error mode.
So if it fails to get the world this s error mode determines what to do.
In response to that, it says log and return null.
That sounds to me like it's going to log something, an error perhaps, and return a null pointer.
That's the behavior I'd like.
So I'm going to copy the function call, come back to my function, call it there.
And if we make it inside this if statement, we have the world.
Now, once the world has been acquired, we have overlap multi by object type.
With all these parameters, let's just copy the whole line.
And we'll call that function here.
Now, of course, we're going to need to change some things, right?
Let's see what we need to change.
The first input is the t array of overlap results.
We can pass that in now.
The next input is origin.
That's going to be our sphere origin, so we can go ahead and pass that in.
Now, next is a rotation.
I don't care about a rotation for a sphere, so we're going to leave that at identity for the F collision
object query params.
I think all dynamic objects is fine for the f collision shape.
We're going to make a sphere, but it needs a radius and our function has a radius input.
So we're going to pass in the radius.
And finally we get our sphere params pass that in and after this our array should have some overlaps.
All right.
Now, what I really like to do is loop over this array so I can make a for loop.
Now, what are these items in overlaps?
Well, they're overlap results, right?
So I'm going to use overlap result.
I'm going to use a reference for it.
I'm going to call it overlap result.
I'll just call it overlap.
And we're going to loop through the overlaps array.
And for each of these, what am I going to do?
Well, overlap.
If I type overlap dot, then we have a number of functions including get actor.
That's really the only one I care about because this actor is what I'd like to check for one to see
if it implements my combat interface to.
I want to see if it is dead.
And three, I want to add that actor to my out overlapping actors array, and then my function has done
its job.
So what I'm going to do is first check to see if the actor implements my combat interface.
Now, one way we can do this is we can call implements.
Implements is a template function that requires the type of the interface you're interested in.
Now, unlike when we say cast the actor to our interface, we don't use the I combat interface.
We use the you combat interface instead.
So this is going to return a Boolean whether or not this actor implements the interface so I can make
a local const bull called implements Combat interface, for example.
And it'll be true if the actor does in fact implement it.
Now, in addition to that, I want to know if the actor's dead, so I'm going to make another const
bool called is I'll make it is alive and I'll set it to true if the actor is dead.
Now for that I can use the static function on I combat interface, which is the execute version of is
dead.
But this of course requires the object that implements that interface that's going to be overlapped.
Get actor.
So now I know both whether or not the interface is implemented and if that actor is alive and I can
check these conditions, I'm going to check first implements combat interface, but I'm also going to
check with an and is alive.
And I almost made a logical error here because this is checking is dead.
We want a negation operator here because if it's not dead, it's alive.
So we only want to do anything if it's, first of all, implementing the interface.
And second of all, not dead.
Now, at this point, it's pretty simple.
What we need to do, we need to add that actor to our out overlapping actors.
So I'm going to take out overlapping actors and I'm going to use Add Unique so we don't get any duplications.
And I can simply use overlap dot get actor or if I want to, I could take that interface I combat interface
and call execute get Avatar as we created that as a nice function to get the avatar we can pass in overlap
dot get actor.
I know it's a little bit of a roundabout way if we already have the actor well we have it so you know
you don't really have to call this execute get avatar.
We can just pass in overlap get actor.
But either way, we should now have our out overlapping actors filled in with all live avatars controlled
by players or AI.
So with that we can go ahead and play, test this and see if we actually get all overlapping actors
and we can do something to them.
Okay, I'm going to click open.
Go back to my game.
Melee attack.
And I'm going to see if I can call my new static function.
Get live players within radius.
Here it is.
Now we need a sphere origin.
That's going to be the combat socket location.
We need a radius.
I'm just going to pass in 45 for now.
We need actors to ignore.
I'm going to get the avatar actor of this melee attack and hook it straight into actors to ignore.
That's going to create an array.
We need a world context object we can pass in self or we can pass in the Avatar actor directly.
Either way is fine.
And what are we going to do with our out overlapping actors?
Well, we could for now just draw debug spheres.
I know debug spheres are less exciting than say causing real damage, but we'll get to that.
So let's go ahead and draw debug spheres for each of our overlapping actors.
In fact, we could loop through these with a for each loop.
And for the loop body we could draw the debug sphere.
Using the array element.
This is an actor.
We'll get actor location and we'll just draw a debug sphere at the actor's location.
This will be a different color.
Let's make it greenish, greenish blue and I'll go ahead and draw this smaller.
We'll make it radius 15.
Okay.
And we also want to end the ability not after drawing the first debug sphere, we'll only end the ability
when we complete the loop.
But really this for each loop is just for debug purposes.
We only care that we're getting any live players within the radius.
So let's see if we're doing that.
Save all.
Press play.
Let's see if we get hit.
And there we go.
So we don't want to see that sphere at the tip of the spear anymore necessarily.
We just want to see the sphere at the actor's location, which is the center of aura.
There we go.
Okay.
Now we got a exception here.
And I suspect that I know what it's from, but let's take a look.
We're failing this check here.
That's checking implements, interface passing in you combat interface, static class.
So we see that right here after calling execute is dead and passing in an object here called AU.
That AU has get class called.
And from that class we're calling implements interface with you combat interface static class.
We see that AU is BP test actor.
So that's a problem.
BP test actor does not implement the interface and because test actor does not implement the interface,
then this check fails because our test actor after calling implements interface will return zero here.
So execute is dead is not going to allow us to even check anything that doesn't implement the interface.
Now that's a pretty easy fix we don't even want to try to execute is dead if we don't implement the
interface.
So one easy thing we could do is check implements interface first and only continue.
We could make an if statement here and then we'll never try to execute is dead unless the actor implements
the interface.
That's one.
Or we can make this more compact and just check overlap.
Get actor implements first in our if statement here and if this first part of the if statement fails,
then the and will never check the second one and that second one can be not execute is dead.
We can replace that.
And then we don't need these two temporary booleans.
We'll first check and make sure that we implement the interface and then check to make sure we're dead.
But we don't.
Check is dead unless we make it pass this first check and then we'll never get that exception.
So with that, let's go ahead and hit stop and we'll debug this.
This should take care of that.
Just a logic error there.
Okay.
I'm going to go ahead and open up my.
Melee attack again.
Looks like everything is still there.
I'm going to go ahead and press play.
We're going to get close to the effect actor that was causing the problem.
And now the problem is no more.
Yeah, and we're definitely overlapping with this effect.
Actor And no problems are occurring.
Perfect.
Now, this time we got this other enemy, right?
It was overlapping in that radius too.
Let's see if we can make it happen again.
There we go.
We got two spheres, one for me and one for the other enemy.
So if we wanted to only affect Aura and not the enemies, or if it's aura doing it, we only want to
affect the enemies and not aura and other friendly players.
Well, we could also check those tags that we gave our aura character and our enemy.
We have tags, aura and enemy.
That's something that we could look into.
For now, I think this is working just fine.
We are getting all live players within Radius.
We're now ready to start doing cool things like damage and parameterizing some of these values in melee
attacks so we can reuse it with a bunch of different types of enemies.
So things are starting to look great.
Excellent job and I'll see you in the next video.