Welcome back.
So we've just learned how we can initialize attributes with a data table.
And I mentioned that that's not my favorite way.
My preferred way is to use a gameplay effect.
Now I'd like to add the ability to initialize our attribute values with a function that will apply this
gameplay effect.
And before we go and start doing that, I'm going to open the editor.
And just remove that data table from our ability system component on the player state.
Why?
Because we're not going to use it anymore.
It was just for educational purposes.
But now that we're educated, we're not going to use it.
So I'm going to select the ability system component on the player state.
I'm going to remove this.
I'm going to delete that index from default starting data.
So we're now no longer using it.
And if I press play and show debug, all of my attributes have values of zero and in fact we don't see
them twice anymore.
Now I'm going to close the editor, and now we're going to work on applying a gameplay effect to initialize
these primary attributes instead.
So let's create the ability to do that.
And I'm first going to add a function for doing this on the character.
Our base character class can have some sort of default primary attributes, gameplay effect.
So I'm going to go into my public folder, into character and open aura character base, and I'm going
to add a variable to the protected section.
This is going to be a subclass of.
It's going to be of you gameplay effect and I'm going to call this default primary attributes.
Now I want to be able to set this from the blueprint so it gets a new property and we'll make this blueprint
read only edit anywhere and I'll put it in the category of attributes.
So we have a default primary attributes.
I'm going to want a way to apply these attributes, so I'm just going to make a function that can do
it and I'll put it right here in the protected section.
It'll be void, and I'm going to call this initialize primary attributes.
So let's generate the definition for this and how are we going to do this?
Well, first, if we're going to call something like apply gameplay effect spec to self or to target
or something like that, we're going to need a gameplay effect spec to apply which we can create because
we have a class for our default primary attributes.
So we can work backwards by first trying to call the function to apply the effect so we can always get
our ability system component, assuming that it's valid at the time we call this function and from this
ability system component, we can call apply.
And I'm not getting the auto complete because ability system component is an incomplete type here,
which means I'll have to include its header file.
I like autocomplete, so I'm going to go ahead and include that first ability system component.
H And now I should be able to get autocomplete.
Now we know about apply gameplay effect spec to self this function, but what about to target?
Let's get some practice using that one this time.
So this one, much like the two self version requires an effect spec an ability system component that
is the target and an optional prediction key.
And we're not talking about prediction keys yet.
For now we just need that effect spec.
So how did we do it?
See if you can remember, try to remember without me telling you and without looking over at other classes
because we've done this before.
How do you make an effect spec now?
It's okay if you don't remember.
It's good enough to know that there's a way to make it and you can always go and look at old code that
you've done before, but, but it's good to give it a try to try remembering.
So the way to do it is through the ability system component.
So I'm going to say get ability system component and the function is make outgoing spec.
And of course this requires a gameplay effect class and we created one here on the base class.
It's called default primary attributes.
So that's what we pass in.
It also needs a level and we're just going to use level one for this effect and then it needs a context
handle.
So we need to make that context handle.
I like to work backwards like this and just comment these things out until I'm ready to fill them in.
So I need a context handle, right?
And so for that I get the ability system component and I call, see if you can remember make effect
context.
And this if we hover over we see it returns an gameplay effect context handle.
So let's make that F gameplay effect context handle.
I'll call this context handle and I'm going to uncomment the next line and pass in that context handle.
Now this one make outgoing spec returns an F gameplay effect spec handle.
Let's make that f gameplay effect spec handle and we'll call this spec handle.
And then finally we get to pass this in to apply gameplay effect spec to target, we pass in the spec
handle and we pass in the ability system component.
We'll just use get ability system component.
Now remember, we can't just pass in spec handle can we, because this requires the actual gameplay
effect spec.
So remember the words syntax nightmare should come to mind.
We have some things buried deep down inside the spec handle.
Right?
We know that the spec handle has the actual spec and see if you can remember just from memory what is
that internal variable called that stores the actual spec?
That would be data, but data is also a wrapper of its own.
It's a shared pointer, which means to get the actual pointer we have to use.
That's right.
We have to use get.
And if we get the pointer, well we're now passing a pointer in, but it doesn't take a pointer.
This function takes a const reference so we have to dereference the pointer and then this will work.
So kind of a lot to do.
And of course these can both be made const, so to be const correct and make this nice and squiggle
free, we can put the const on both of those and there we go.
This is the version that applies a gameplay effect spec to a target ability system component and we
have to specify the ability system component when we use this one.
And if we hover over this, we see that this function can be const as well.
So we don't need to change the values of any variables, we're just calling functions on them.
So I'm going to make this a const function, which means I have to come back to the header file and
make it const there.
So now we have initialized primary attributes and as long as we have a valid gameplay effect class default
primary attributes, then calling this function should be fine.
We shouldn't have any issues as as long as that is the case and our ability system component is valid
as well and we can check those things, we can say check and we can use is valid get ability system
component, and we can also check default primary attributes just to make sure that that's not a null
pointer that's unset.
So now that we have this function, the question is where do we call it?
Now we can call it in different circumstances depending on if we're the aura, character or the enemy.
In this video, I'm just concerned with setting our attributes for Aura.
So I'd like to open up our Aura character CPP file.
So I'm going to go to character and open Aura character.
And an aura character.
I'd like to apply this effect.
In other words, I'd like to call the function we just created in the base character class.
We called that initialize primary attributes.
But unlike init ability actor info which we're calling on both server and client, applying this gameplay
effect only really needs to be done on the server.
Why?
Because all of those attributes are marked to be replicated.
So if we change them on the server then they'll change on the client as well.
However, if we wanted to do this on both client and server, that'd be okay too.
We'd be setting these attributes to the same values on client and server, and that way the client won't
have to wait for the server to replicate it back down.
Either way is fine.
Now if we do it in init ability actor info, we know that our ability system component should be valid
by this point and it's clear by looking in this function.
So we can just do it here after ability system component is set since we know that it should be safe
to call it there.
So I'm going to call it here.
And what did we call it?
Initialize primary attributes.
So we'll call it right here in init ability actor info.
So now all that's left is to make sure that we have that effect and that we configure some default values.
So I'm going to go ahead and run and debug mode so that we can create that effect.
That sets our initial primary attribute values.
Now get some compilation errors.
So the first one says an aura character based gameplay effect is an undeclared identifier.
We didn't forward declare gameplay effect.
I'm going to go ahead and do that up here at the top.
Then I'll go ahead and compile again.
And that solves all of our problems.
So when I have multiple compiler errors, if I know how to fix one, usually I'll fix it and compile
again.
And then if there are more compiler errors, I'll find another one that I can fix.
Fix it, compile again, and usually that list goes down pretty quickly.
It's usually only a couple of errors that can propagate many more.
So that's kind of my workflow.
Okay, let's run in debug mode.
And here in the editor, what we need is a gameplay effect that we can use to initialize our primary
attribute values.
And this is going to be specific to really the aura character, right?
And eventually we're going to have different types of characters that may start off with different values
and we'll be implementing a clever solution.
But for now, just to get it working, we're going to just create one for Aura and apply it for aura.
So I'm going to go into ability system and we're going to make a new folder here in ability system called
Gameplay Effects and here in gameplay effects, we're going to make a new folder called Primary Attributes.
And in primary attributes we're going to make a new gameplay effect.
So let's make this gameplay effect and we're going to call it G Aura primary attributes.
We're going to open it up and this will be an instant gameplay effect.
We're going to apply it once.
We're already calling the function to apply it and we're going to have four modifiers, one for each
of the primary attributes.
So I'm going to click plus one, two, three, four times and expand the first one and pick from among
our attributes.
I'm going to choose attribute set dot strength modifier.
OP though, is not going to be Add.
It's going to be override.
Yes, they will have values of zero.
So adding this initial value to it will give us the same result, but we're going to override just because
this is the value that it should start off at for the modifier magnitude, we're going to set the scalable
float magnitude and for auras initial strength.
Let's give her ten.
And there's our strength.
Let's go to the next one.
For the next one, we're going to choose intelligence.
We're going to set, modifier op to override modifier magnitude.
We'll give her, let's say, 17.
And there's our intelligence.
Next, we'll choose our resilience modifier.
OP will be override modifier magnitude.
These might not be the same values I chose earlier, but I'm going to choose 12.
And then for the next one, finally we have vigor.
It's going to be override and the magnitude can be nine.
So we should see ten, 17, 12 and nine.
I'm going to compile that.
And now we have our gameplay effect.
We just need to set that and that's going to be on the Aura character itself.
So into character, Aura, BP or a character blueprint.
And I'm going to search for primary attribute and here's my default primary attributes.
I'm going to choose Aura, primary attributes, compile and save, and I'm going to save all.
So now all we need to do is press play and show debug.
And there we have it.
We have strength.
Ten Intelligence 17.
Resilience 12 and Vigor nine.
So pretty simple.
We kind of already knew how to apply gameplay effects, didn't we?
But this is my preferred way and this is the most common way I've seen it done.
I think it's a good way and it works as long as we apply the gameplay effect at the beginning of the
game, then we get our default attributes and later when we decide that we need to load in some attributes
from saved data, it's pretty simple to do that as well.
Once you know how to apply an effect and specify the magnitude at runtime and we'll learn how to do
that too.
So now we know two different ways to initialize our attributes.
Actually three ways.
One is through the attribute accessors in code.
That's currently how we're doing our health, Max Health Mana and Max Mana.
We'll fix that later.
We're going to handle these in time, but now we're using a gameplay effect for our primary attributes,
which is great.
So excellent job and I'll see you in the next video.