Welcome back.
Now I've got my aura effect actor open here.
I'm going to right click on it and close the other tabs.
So I just have my CPP file and my aura effect actor here is actually kind of really specific.
It has a sphere component, so we can't really change that.
If we wanted a box, well, we'd be kind of out of luck.
And when something overlaps with that sphere, well we see if the other actor implements the ability
system interface.
If it does, then we reach into its attribute set.
We cast away the constness of the const pointer returned from get attribute set, which should give
us a sort of dirty feeling like we're not doing something we should be doing.
And then we directly set the health and the mana equal to what they were before, plus or minus some
hardcoded value.
So this is all a whole bunch of really rigid, restrictive things that doesn't make our aura effect
actor good for really anything other than adding 25 to the health and subtracting 25 from mana.
We have no flexibility, no modularity and well, this should be changed and that's what we're going
to do in this video.
We're going to make aura effect actor live up to the flexibility of its potential.
We're going to make it a lot better.
So what I'd like to do is rather than adding a static mesh and a sphere component in the C plus plus
class, I'd like those to be things done in the designer domain on the blueprint side, because the
visual representation of the actor and for that matter the shape of the overlap sphere could be determined
by designers.
Someone who works mostly on the blueprint side.
And it's the technical details of what happens when we overlap or specifically when we'd like to apply
some kind of effect that can be determined in C plus plus.
So here's what I'm going to do.
I'm going to actually remove the sphere and the mesh.
We're going to go ahead and just remove both of those.
I'm going to hit alt o and open up the header file and I'm going to remove both the sphere and the mesh.
And for that matter, we really don't need on overlap and on end overlap.
I'm going to remove both of these callbacks as well.
And here in the CPP file, we can totally get rid of them altogether.
And in Beginplay we no longer have a sphere to bind callbacks to its overlap event.
So we're essentially stripping this actor almost completely clean.
In fact, most of these headers aren't being used anymore.
Now, why did I do that?
Well, I'd like to still have functionality here in C plus plus, but I'd like to give the designer
side some control over how this actor works.
I'd like to make this actor really flexible and easily configurable on the blueprint side.
So if we want to add a mesh or a sphere or a box or capsule component, we can do that and we can add
it to the root component.
So really I'm going to use create default Subobject with a simple scene component, you scene component
and call this scene root and I'm going to set the root component to the result of this.
So I'm going to call set root component setting the root component to the result of simply creating
a scene component.
And that's it.
So if we compile and run this code.
We can search for potion here.
There's that BP health potion.
It's been stripped of all that makes it a health potion.
I'm going to delete it from the world and go into my blueprints folder into actor And here's BP health
potion.
If I open the full blueprint editor, there's that root component and there's nothing else.
We've stripped it all clean.
Now it seems like we've made this actor class less capable, but what we've actually done is we've made
it more versatile by no longer restricting the visual representation or the overlap volume to the C
plus plus side.
We now have the freedom to choose and customize it here in the blueprint.
So for the mesh, I can click add, I can add a static mesh to this and call this potion mesh and I
can select the potion bottle.
And I can change its scale.
Let's put it down to 0.2 in all dimensions, and I haven't scaled the entire actor as the root component
is still at a scale of one.
We can't see it because it's not exposed.
And if we wanted, we could set this to a U.
Property variable with visible anywhere.
But for now, I'm fine without that.
We now have the ability to add things.
In addition to the potion mesh, I can add a overlap volume.
Let's say I want that sphere back.
I can type in sphere, we can get a sphere collision and we'll call this sphere.
We'll just call it sphere.
That's fine.
And there it is.
And now I have control over what happens when the sphere gets overlapped.
Now, since I added the potion mesh here in Blueprint, I have to configure collision settings in Blueprint.
That's not a problem.
All I need to do is come over here, come to Collision Presets and choose no collision as I don't want
the mesh to collide with anything.
And for the sphere, well, spheres automatically overlap all dynamic.
I'm fine with that.
Now we can go into the event graph and with the sphere selected I can use on component began overlap.
For the sphere itself and do what I'd like here.
So in many ways, taking some of that stuff out of C plus plus and moving it to the blueprint side makes
this a lot better.
And we'll see why once we start using this effect.
Actor to apply effects.
You see, what we'd like to do with the other actor is apply some sort of effect and our effect actor
can have a variable for an effect to apply.
That's what we're going to add and we're going to create a function to apply that effect.
Now, again, doing this in blueprint versus C plus plus is a matter of choice.
And in many ways, adding this functionality here in Blueprints gives us a little bit more control on
this side of things.
But we're going to do this on the C plus plus side for educational purposes.
I think it's important for you to see how to do things both in C plus plus and blueprint so that way
you can make the decision as in some cases it will be more appropriate to do in C plus plus and in some
cases it'll be more appropriate to do here in blueprint.
So what we're going to do is add a gameplay effect to this class in C plus plus and learn how we can
apply that effect to the actor that overlaps with this sphere.
And then we'll see later how we can derive from our effect actor and add any type of overlap volume
we wish.
And we'll really start to see how carefully choosing the balance between C plus plus and blueprints
can allow us to have more choices in our game design.
So I'm going to go ahead and save all and we're going to close out of this and we're going to make a
function that we can call to apply an effect, and we're going to make an effect that we can apply.
Now, I want to be able to set this effect from the blueprint side.
I'm going to make this protected, and it's going to be a subclass of of type U gameplay effect.
We're going to add a forward declaration for that.
We no longer need the forward declaration for U sphere components.
So we'll remove that.
And I'm going to call this instant gameplay effect class.
So it's the class for an instant gameplay effect that we'd like to apply.
I'm going to add a U property to it and make it edit anywhere and give it the category.
Applied effects.
And if you want, you can just call it effects.
It doesn't matter.
I'm going to call it applied effects.
And now that we have this class, I'd like to make a function that we can call to apply this gameplay
effect.
It's also going to be protected.
It'll be void.
And what I'd like to call this is apply effect to Target.
And it's going to take an A actor as a target.
We'll call it Target, and it'll also take a subclass of of type gameplay effect called gameplay effect
class.
And I'd like this function to be a new function with blueprint callable.
That way we can call it from Blueprint whenever we want.
I'm going to go ahead and generate the definition.
And here's where we want to apply the effect to the target.
Now, we've seen before that we can get the ability system of some actor by seeing if that actor implements
the ability system interface.
We say I ability system interface.
We can call it as interface equals and then we take that other actor and cast it to the interface.
We say cast to I ability system interface and we cast that actor.
We're going to call this cast function on the target actor.
And if this cast succeeds, then we have an ability system interface so we can say if AC interface.
And then we have access to the interface by saying ASC interface and we call that function get ability
system component.
So that's one way to do it.
Now there's another way, and that's by the use of a static function library.
You should be familiar with things like gameplay statics and kismet math library and static function
libraries like that.
Well, there's one for gas as well.
It's called you Ability System Blueprint Library.
And there's a handy dandy function get ability system component, and it requires an actor we can pass
in target.
And what do you think this returns?
Well it returns an ability system component and of course up here at the top rider included that header
for me.
So if you're not using rider, make sure you get that include up there.
Now, how does this magic function work?
Well, we can go to declaration or usages and we see that it just calls ability system globals get ability
system component from actor.
Let's dive deeper down the rabbit hole, shall we?
We'll go to the declaration or usage of this and if the actor passed in is null we return null.
And look at this.
It's casting actor to ability system interface.
And if that cast succeeds, it returns the ability system component on that interface.
How nice.
Well, it also has a boolean called look for component.
And if that ability system interface cast fails, then it uses find component by class.
So this function is a little bit better because, well, you might have actors in your world that don't
implement the ability system interface we usually do, right?
We mentioned earlier that we implement this interface because it's easy, right?
We can always cast to that interface and call get ability system component and get the ability system
component.
But well, you know, not everybody follows those conventions and rules of thumb.
And so the gas system has these global libraries that are designed to handle those edge cases.
And here's one where the actor may not implement that interface.
And if it doesn't, well, this function is designed to look for any component on the actor that has
the type ability system component.
If it does, it'll find that component and return it.
So this is a little more sophisticated than what we were doing.
And for that reason, well, maybe you might want to default to using that function instead of this
method.
We were doing simply casting to the interface and getting the ability system component.
It's up to you if you're in the habit of implementing the interface for all the actors in your game
project.
Maybe that's a policy that your game company will set.
You know, your whole dev team may get together, have a meeting and say, we will implement the interface
no matter what.
Then we can always cast to it and it works.
Or you may decide, well, these actors over here aren't going to implement the interface for one reason
or another.
And in that case, for that reason you may opt to use the ability system, blueprint, library function,
get ability system component.
Either way you have options.
And just so you know, this is blueprint callable and it's a very handy way to get an ability system
component of any actor in blueprint.
So there's that.
So what we're going to do is use this instead of what we were doing before.
So I'm going to replace this line with this one where we get an ability system component.
I'm going to go ahead and store it in a new ability system component pointer called Target ASC.
And if you're shaking your head and tsk tsking at me for having ASC in the name, I apologize for that.
Feel free to call this target ability system component.
And of course this returns a ability system component, not an aura ability system component.
So I can't have aura in the type name there.
And once we have this target ability system component, we can now apply effects.
We can apply this game play effect, We have the class and there are many different options and ways
to do things in gas.
And by the end of this course you'll know just about a dozen ways to do any given thing.
The first way that we'll learn how to apply a game play effect is as follows.
The ability system component.
Here we have Target ASC, which is an ability system component pointer has a function called apply gameplay
effect.
And before I finished typing, I want you to notice all of these options we have apply gameplay effect
to self.
We have apply gameplay effect to target.
We have apply gameplay effect spec to self and apply a gameplay effect spec to target and of course
blueprint versions of these.
Now just so we don't get overwhelmed, we're just going to pick one of these.
We're going to pick apply gameplay effect spec to self.
So let's type that spec to self and we'll open the parentheses and we'll see that the first input parameter
is a const reference to a gameplay effect spec.
Now the other input parameter is a prediction key.
Prediction has to do with prediction in a lag compensation sense.
We'll get into prediction later.
Don't worry about that.
For now, it's an optional parameter, so we're going to leave it out for now.
We just care about the gameplay effect spec.
How do we get one of those?
If we could just manage to pass one of those in.
We can apply a gameplay effect, right.
So we want to get an effect spec.
Well we can make one and the ability system component has the ability to do that.
We're just going to use the target's ability system component because any ability system component can
make an effect spec given some gameplay effect class.
So we're going to say Target ASC.
We're going to get that ability system component.
I'll go ahead and comment out this line until we're ready.
And the ability system component has a function called make outgoing spec and make outgoing spec takes.
Well, how about this a subclass of you gameplay effect.
That's what we have here to subclass of gameplay effect called gameplay effect class.
We're going to pass that in to satisfy that argument requirement.
Now next is a float called level.
You see, gameplay effects can have their own levels and as our game project gets bigger and more serious,
we'll have different levels for everything.
But for now we'll just use one point F, 1.0, f, one point f, same thing and f gameplay effect context
handle called context.
That's the third input parameter that we need.
So what exactly is that?
Well, a gameplay effect is closely related to something called an effect context.
Context meaning what's the context of the effect?
What is the context of the situation?
Who's causing the effect?
Who's the target of the effect?
Is the effect a fire effect?
Lightning effect?
There's really no end to how you can describe a gameplay effect and the context surrounding it, but
the gameplay effect context is something that we associate with the gameplay effect, specifically its
effect spec by passing in an effect context actually an effect context handle a handle is a lightweight
thing that contains the pointer to the context itself.
Now how do we make that context handle?
Well, the ability system component has a function for that.
It has a function for everything really.
So we're going to comment that line out until we're ready to continue with it because we need an effect
context, right?
So we're going to say Target ASC make effect context.
This doesn't take any inputs.
It's just a function that makes an effect context and returns a handle to that.
You see, it returns an F gameplay effect context handle.
Now at this point, we're going to slow down and start taking a look at these types so we don't get
confused.
We know that this is an F game play effect context handle and we're going to call this effect context
handle just so we're not confused.
And now it's time to start looking at these types.
So I'm going to right click on F gameplay effect context handle, go to declaration or usages and we
see that it's defined here in gameplay effect types dot h and we see that it's a handle that wraps f
gameplay effect context or a subclass allowing it to be polymorphic and replicate properly so we can
subclass the effect context if we want to add things for fire or lightning or poison or really anything
we want, It doesn't have to be related to damage or anything like that.
The handle is a lightweight wrapper that stores the actual effect context as a pointer.
It's called data.
See data is a shared pointer of type gameplay effect context.
So data here is the true context.
The handle itself is just a wrapper that has a couple of utilities.
It has the ability to clear that pointer.
We have an is valid function to check if it's valid.
We have a way to get any gameplay tags that that affect context may have and we'll learn more about
those later.
We can add things like instigators, set abilities.
It has a bunch of utilities, but really at the heart of this handle is the context itself called data.
So make effect.
Context is just a way to create that wrapper that contains an internal gameplay effect context, and
we can set things on that context.
For example, we can take effect context handle and we can call add source object.
So if we want to know what caused this effect, we can store a source object and just peeking this definition
add source object that we see that it takes the data inside and calls, add source object.
And if we check out the definition of that, we see that now we're inside the effect context struct.
Gameplay effect.
Context.
And what does this do?
Well, it takes that you object and it sets an internal member variable called source object uses a
weak pointer and that basically means it won't affect garbage collection, it won't affect reference
counting for garbage collection.
So the gameplay effect context has the ability to store things related to the context of the gameplay
effect.
So we'll go ahead and add the source object so that say some time down the line we need to know what
object caused this effect.
Well, we can know by checking the source object on the effect context, for instance.
So now that we have an effect context, we're going to uncomment the next line where we're calling make
outgoing spec, which needs that effect context.
We're going to pass it in for the third parameter, and this function requires an gameplay effect context
handle specifically so we can just pass in the handle like so.
Now make outgoing spec returns an gameplay effect spec handle.
So let's store that in a local for gameplay effect spec handle.
And we'll call this effect spec handle.
No need for that equal sign there.
There we go.
So you'll see a lot in gas code.
These words like handle will be omitted.
For example, we know that make outgoing spec returns a spec handle, right?
You'll see oftentimes in gas code that a spec handle will have the name effect spec without the handle
on it.
So be aware of that.
If you're looking at other gas code in other projects you'll see effect spec and you'll hover over it
and you'll see, well it's actually an effect spec handle that's pretty common to leave off that handle
part because well, it makes the variables longer.
But in this case we're going to try to keep it descriptive of what it is so as to avoid confusion.
But just, just know that you might see a variable called effect spec or just spec that really is an
effect spec handle.
So look at the type to be sure.
Now these handles are wrappers, right?
We've seen that the effect context handle is a wrapper that holds a gameplay effect context.
Well, similarly an effect spec handle is a wrapper that holds an effect spec.
We can go to the definition of this.
And see that it's an effect.
Spec handle.
The comment says allows blueprints to generate a gameplay effect spec once and reference it by handle
to apply it multiple times to multiple targets etcetera.
And notice it has its own data of type gameplay effect spec.
It's wrapped in a shared pointer, but you get the gist.
It has a data member variable.
So these wrappers, these handles have an internal data member that stores the actual struct, in this
case a gameplay effect spec.
So we went through all that just to get this effect spec handle which now has the effect class a level
and the effect context handle all set for it.
Now we can take our target ASC and call apply gameplay effect spec to self, which takes the gameplay
effect spec, but we have an effect spec handle not an effect spec.
This function needs the effect spec, not the handle.
So we need to get that data from inside the effect spec handle and notice that it takes an gameplay
effect spec by const reference, not by pointer but by const reference.
And we know that that data is a pointer inside of our handle.
So to get that data we take our effect spec handle and we access data.
Data as we saw was a smart pointer.
So data itself is also yet another wrapper to get that raw pointer inside of it.
We got to call get right, so right clicking and going to the declaration of data, we see that it's
a shared pointer at least in gameplay effect spec handle and to get that raw pointer inside of the shared
pointer wrapper we have to use get.
Now that just returns us the raw pointer.
But as I said, this function doesn't take a pointer to an gameplay effect spec, it takes a const reference
so it can't be a pointer.
We have to dereference the pointer, so using the asterisk that's going to get rid of those squiggles.
Now we've drilled down to the gameplay effect spec buried down deep inside this gameplay effect spec
handle.
So it's a little bit of kind of syntax nightmare, but you take the effect spec handle, you get the
data, which is a shared pointer, storing a gameplay effect spec pointer in its raw form.
You call get to get that raw pointer and then all of this gets dereferenced into the gameplay effect
spec that apply gameplay effect spec to self craves and desires.
And with our semicolon there we now have a successful call to this function.
So as long as our subclassof is set to some gameplay effect, apply a gameplay effect to target should
work when we pass that in and we pass in a target that has an ability system component now to kind of
safeguard us.
You know, we could check the pointer target ASC and do nothing if it doesn't have an ability system
component, we could do it that way.
And think about this.
If something overlaps with the volume, it may not have an ability system component.
And in that case, well, we don't really want to crash the program or anything.
We kind of just want the pickup to do nothing, right?
So what I'm going to do is simply check the pointer.
We're going to say if Target ASC is a null pointer, we'll just return and do nothing and otherwise
we'll apply the effect that we have.
But the effect itself should not be unset.
That's something we want to check.
We're going to say check gameplay effect class.
If that's a null pointer, we got problems right.
We should definitely set this because this aura effect actor is designed to apply an effect.
If it doesn't have an effect, we're going to crash intentionally here.
Okay, so this is looking pretty good.
We have apply effect to Target and we can call this from Blueprint.
So since we have our sphere set up in Blueprint, we're going to call this when overlapping, but we
also need to create a gameplay effect and that is something we're going to get into in the next video
and we'll tie this all together and see our effect actor in action.
So in the meantime, go ahead and compile your code.
Make sure there are no typos or anything like that.
If you want, you can go up here and remove any of these unused includes.
They're not needed either.
And in the next video, we'll get this all working.
So great job, well done for sitting through a bit of a lengthy discussion.
But, you know, these things are kind of important.
To understand in depth.
And the better you understand the working parts here, the more capable you'll be when you start implementing
these type of effects on your own.
So great job.
And I'm just noticing the green squiggles writer says that this can be a const variable.
Why not?
We'll go ahead and do that and I'll see you in the next video.