Okay.
Welcome back.
Now we have three callback functions, and for all of our input actions that exist in our data asset,
our input config, we're going to bind all three of these to those input actions.
We have callbacks for pressed, released and held.
Now we'd like to be able to activate abilities and we'd like to use the input tag to identify those
abilities.
Now abilities can be associated with gameplay tags in any number of ways.
We know that our ability system component will grant us our startup abilities, and if we start the
game with abilities, those abilities should be associated with some kind of input tag by default so
that we know if we press that particular input, we can activate that ability.
So for this reason I'd like to add a gameplay tag variable to our aura gameplay ability here in our
ability system folder under abilities, there's our aura gameplay ability.
I'm going to open the header file and I'd like this class to have a member variable for its startup
input tag.
Now a gameplay ability might have its input tag changed at runtime, so this variable is really only
for the startup input tag.
So it only applies if this is one of our startup abilities given at the beginning of the game.
So we'll make a public section and this input tag will be right here.
We're going to call this F gameplay tag startup input tag.
So this is the input tag that we'll use if this ability is a startup ability.
So I'll make this a new property and I'd like to be able to set this on the gameplay ability.
So I'll make it edit defaults only and I'll stick it in a category of input.
So we have this start up input tag.
Now the thing is that this will be set for our class default object for any given ability, but that's
not something that we should change at runtime.
Our abilities have different instancing policies and really it's the gameplay ability spec that we'll
be using throughout the game.
So start up input tag is really only good to check at the very beginning when giving abilities to the
character.
So if we go into our ability system component class and we can actually go into the CPP file where we're
adding character abilities, right here is where we can check if the ability has a valid startup input
tag.
Now we want our gameplay abilities to have the concept of an input tag, but we'd like to be able to
change this at runtime.
For example, we might have an ability mapped to the left mouse button and we may want to reassign it
to the right mouse button.
So having a variable on the gameplay ability class is great for something like the startup input tag,
but if we want to be able to change it, we can't really use variables on the gameplay ability class.
Well, the gameplay ability spec has a gameplay tag container specifically for tags that can be added
or removed dynamically throughout the game.
So that's perfect for our input tag.
And as our ability system component adds the abilities for the first time at the beginning of the game.
Since these are startup abilities, we can check that startup input tag and we can add those to our
ability spec for that given ability.
So right after we've created our ability spec, what we can do is we can take this ability spec and
we can get those dynamic tags and add to them.
So that tag container is called dynamic ability tags.
And if we add a tag with the add tag function, we can simply get the ability and get its startup ability
input tag.
So how do we get the ability?
Well, we take the ability spec.
And we use the dot operator to get ability and ability is the actual ability that the ability spec is
a representation of.
And if that ability is not a null pointer, then we can get variables on it.
Now ability is a simple you gameplay ability, so we'll have to cast it in order to get that member
variable called start up input tag.
So we'll go ahead and cast right here in the for loop.
Let's do it.
We're going to say if and we'll make a const you ora gameplay ability that will be a pointer and notice
writer included the header for me.
We'll call this ora ability and we'll use a cast to you or a gameplay ability and we're going to cast
our ability spec, which means we need the ability spec first, don't we?
So I'm going to move this line here.
I'm going to control X, control V to paste it there.
And what we're going to cast is ability, spec, dot, ability.
So if our ability spec ability can cast to an aura ability, we now are able to get that aura ability
and access startup input tag from it.
So I'm going to take this line where we're trying to add a tag to dynamic ability tags, control X and
control V right here and what we're going to add to it now, instead of using our ability spec, we're
going to use aura ability and get that startup input tag.
So now if we're looping through our abilities and if any of those are derived from aura gameplay ability,
then we get that startup input tag and we add it right here to dynamic ability tags.
And as soon as we've done that, we can then give the ability.
Now we've seen that we can give the ability and activate it immediately with give ability and activate
once that was just so we could prove to ourselves that the ability could be activated.
So we're not going to use that anymore.
We're just going to use give ability.
So as soon as we've added that startup input tag to the ability specs, dynamic ability tags, we're
then going to give that ability.
So now our startup abilities as we give them to the character are going to have their startup input
tag added to their dynamic ability tags.
Now this is great because dynamic ability tags are designed to be added and removed at runtime so we
can change this later if we want to.
And Ryder's going to tell me that ability class can be made const.
So to satisfy that little suggestion, I'm going to make that const.
Why not?
Okay, so now our start up abilities have in their dynamic ability tags, an input tag and all of our
input tags start with input tag dot something so they're really easy to identify as input tags if their
parent tag on the hierarchy has input tag.
So that's great.
That allows us to identify abilities and that's going to be crucial when we want to activate them.
Now to activate them, what are we going to do?
Well, here in our player controller, all we're doing so far is adding debug messages.
But what we can do instead is to call some function on the ability system component passing in the input
tag, telling the ability system component.
Hey, we're pressing this input, so if you have any abilities with this input tag assigned, go ahead
and activate them And the ability system component is going to see if we pass an input tag over that
corresponds to one of its activatable abilities.
Well, the ability system component can activate that ability.
So I'm going to make a couple of functions.
I'm going to make a function for when an input is pressed or held and a function for when an input is
released for activating abilities.
I'm really only concerned with when an input is held and released because if an input is pressed for
the first time, well, that's kind of something we can know if we're holding an input based on whether
or not an ability is activated.
So I'm going to have two functions.
We'll go back into our ability system component and these are going to be public and they'll be for
activating abilities.
So first we'll make a void function called ability input tag held and it's going to take a const gameplay
tag.
By reference, and this will be the input tag.
And I'll have another function called ability input tag released.
And it's also going to take a const reference to an input tag.
Looks like I accidentally moved my semicolon there.
Let's get it back to where it belongs so we can generate the definitions for these.
And what we can do is call these functions from our player controller for ability input tag held.
I'd like to access our ability system component and call its ability input tag pressed.
So back in my aura player controller class, when our ability input tag held is called, I'd like to
call ability input tag held on the ability system component.
Now I don't want to have to cast every single time as ability input tag held may be called every frame
and that can be expensive.
So I'm going to go into aura player controllers and I'm going to rearrange my tabs up here just a little
bit.
There we go.
And an aura player controller.
I'm going to scroll down and I see that I don't have any variables to store my ability system component
and I'd like one so I don't have to cast more than a single time.
So I'm going to forward declare the aura ability system component.
So you or a ability.
System component will forward declare that and we'll make a pointer of this type.
So down here under input config, we're going to say T object pointer.
You are a ability system component and we'll call this aura ability system component and we'll give
it a new property.
And where are we going to set it?
Well, I'd like to set it the first time we access it, so I'm going to make a function that returns
a pointer to this.
So you aura ability system component pointer, we're going to call this get ASC.
We're going to generate a definition for it.
And what we'll do is say if aura ability system component is a null pointer, which it will be the first
time, then we're going to set our ability system component equal to the result of a cast to oral ability
system component.
So we'll only cast the first time.
Right?
So how are we going to get the ability system component?
Well, we know several easy ways to do it right.
One of the most convenient is to use the you ability system blueprint library.
Function called get ability system component, which requires an actor and we can simply pass in our
pawn controlled by this controller.
We can use get pawn.
Now, get pawn requires a template parameter type.
We can use a pawn.
So calling get pawn.
Passing that into get ability system component gets us the ability system component and that can be
simply cast to the aura ability system component.
Now if we want one big line, we can do that here.
We can simply cast to you aura, ability, system component.
The result of this function and we can set aura ability system component equal to that.
If you don't want such a long line, you can break it up into multiple lines.
That would be fine as well.
So either way, if the variable is null will attempt to get it.
Now this cast could fail.
This could be called too early.
Perhaps the ability system component is not set yet.
That's totally fine.
That means this will return a null pointer because we're going to return aura ability system component.
But as soon as this function is called for the first time that the ability system component on our pawn
is valid, then we will get a valid ability system component and it won't be null from that point on.
So get ask is a nice utility function we'll use here in the player controller and it will ensure that
we only cast once.
So when we get the ability system component from here in ability input tag held, then we won't be casting
every frame.
So what we're going to do is we're going to get ASC here and we know that this is an aura ability system
component.
So we can call ability input tag held, which requires the gameplay tag for our input.
We're going to pass an input tag and for released we'll do the same thing.
We'll say get ASC ability input tag released, passing in input tag now because get ASC could return
a null pointer if we call it just too early in the game, we're going to say if get ASC.
And we'll just check if it's a null pointer and return early.
So we'll say if get ASC is null return and we'll do that in both of these.
We don't want to do a check because it's possible we call this too soon, at which point we want to
return and do nothing.
Okay.
And for now, ability input tag pressed.
I'd like to do nothing.
I'm just going to comment out the on screen debug message there.
And now it's up to the ability system component itself to handle what happens when we press these inputs.
So what is the ability system component going to do?
Well, we know we're giving it at least one startup ability and we can set its startup input tag.
So we want the ability system component to check to see if we have any activatable abilities associated
with this input tag.
And I'd like to activate them if they're not already active.
So if they're already active, I don't want to activate them every single frame while we're holding
input.
So we're going to check to see if they're active.
So we have a few things to do here.
First, I'm going to check to make sure the input tag is valid, so I'm going to say if input tag dot
is valid, we can do that with gameplay tags and if they're not valid, we can return and do nothing.
After that I want to check to see if this ability system component has any activatable abilities with
this input tag.
So how do we do that?
Well, we have a function that contains all of our activatable abilities.
It's called get Activatable abilities.
Now this returns a array of gameplay ability specs.
Now Activatable abilities means abilities that we have that can be activated.
And remember, we've seen lots of different gameplay tags that can block abilities and so on.
So this is the way to get all abilities that we can activate.
Now we can loop through these, so we're going to say for and first I'm going to say auto ref and say
ability spec because these are ability specs I'm going to say for auto ability spec and get activatable
abilities.
And what is auto?
Well it's an for gameplay ability spec.
This is just kind of a lazy habit I have so I don't have to think about or type the data type for certain
things.
So we're looping through all the gameplay ability specs in activatable abilities.
So we don't want to just activate them all.
We want to activate any abilities that have the input tag.
So we're going to check the input tags.
We're going to say if ability spec.
And we're going to get those dynamic ability tags and dynamic ability Tags is an gameplay tag container
and therefore it has functions like has tag and has tag exact.
We want to check for an exact match to see if it has the input tag.
Now, in this case, we now have an ability in our activatable abilities that has the tag that our player
controller passed over.
That means we pressed that input, right?
So now we want to activate it if it's not already active.
So how do we check that?
We can say if and we can get our abilities back and call is active, but we want to activate it if it's
not active.
So we're going to place the negation operator here and we're going to activate it only if it's not active.
So how do we activate it?
Well, the ability system can do that with tri activate ability and it requires a spec handle so we
can take our ability spec and from that we can get the handle and that's how we can activate abilities.
We have to call tri activate ability because there may be things that prevent the ability from being
activated.
So we have to try to activate it.
Now abilities themselves have the concept of input being pressed or not pressed for those abilities
and you can check at any time if an abilities input is being pressed or released and decide yourself
what that means for you.
An ability can be in the input pressed state or input not pressed state, and you can provide your own
custom functionality or meaning for when its input is pressed and released.
To tell an ability that its input is being pressed.
We call the function and we'll put it right here.
Ability spec input pressed and we have pressed and released.
Now this requires the ability spec, so we're going to pass that in.
And what does this do exactly?
Well, it just basically sets a boolean on the ability spec that is keeping track of whether or not
its particular input is pressed.
And we can always go to the declaration or usages.
Here's the implementation of it.
We see that the ability spec itself has an input pressed.
It's a uint8, so it's just a flag we can set it to.
True.
And then it checks itself to see if it's active and if it's a non instance ability.
Well it just says for the spec get that ability and call input pressed on it.
Otherwise if it's not a non instanced ability then this function gets all instances of the ability as
there can be multiple and for each instance it's calling input pressed.
So the ability itself has an input pressed function.
We can go to that function if we want to see and we see that it's a virtual function that's not implemented.
So you can implement something on your gameplay ability by overriding input pressed and input released
if you want something to happen as a result of input being pressed and released.
So there's a lot of customizability here.
The takeaway here is that nothing is really happening other than we're just telling the ability, Hey,
your input is being pressed right now and we're going to do that whether it's active or not.
As long as we're holding the input down, we want the ability to know that its input is pressed and
if the input is released, we want it to know, Hey, your input is released.
So ability input tag released is going to be somewhat similar.
I don't want to just end an ability if its input is released.
I want the ability to determine that because not all abilities need to be canceled or ended when their
ability is no longer pressed.
Some abilities will just press the button and release it right away.
And we don't want to necessarily end that ability as soon as it's released.
All I really want to do here in this function is tell the ability itself, Hey, your input is not being
pressed anymore, so we'll do something similar to what we did in input tag held is we'll copy these.
We'll paste those lines.
And what are we doing?
Well, we're checking if the input tag is valid.
We're looping through our activatable abilities and if the ability matches exactly with the input tag,
what are we doing?
Well, we don't want to activate the ability, so I'm going to remove this if statement and we don't
want to call ability spec input pressed, we want to call ability spec input.
Released and we pass in the ability spec.
So that's all we want to do.
Now our ability will know that its input is released.
Okay, so really all we're doing is if we're pressing any of our inputs, if we have an activatable
ability that's been granted with this input tag, it's going to be activated.
So what we can do is we can test this out with our test ability.
We can set that test abilities, start up input tag to one of our inputs and try to activate it.
So let's hit debug and test this out.
So what we can do is we can go into our ability.
So that's going to be in blueprints, ability system.
Gameplay abilities.
And here's our test gameplay ability blueprint, which we are giving to our character at the beginning
of the game.
Now under input we have start up input tag.
Now replicate input directly was an inherited variable.
We already decided we're not going to use that, but it also happens to be in the category input.
Now our start up input tag.
We can choose an input tag and I'm going to choose the lab input tag and we know that that is associated
with our left mouse button thanks to our input mapping context.
So as soon as I press my left mouse button, I'm going to activate this ability because it will not
be active yet and then it will be active and then after five seconds it will end itself.
So I should only be able to activate it if it's not already active.
Let's test that out.
I'm going to save all and press play.
And press the left mouse button.
And nothing happened.
So I'm going to debug this.
I'm going to place a breakpoint here in ability input tag held just right there to see if we even have
this function called I'm going to press play and left click and we're getting this far.
That's great.
Now this right here is our ability system component.
And if we expand these dropdowns, we can see what our activatable abilities contains.
We should have an activatable ability in there.
Here's activatable abilities opening that we have under items.
We have one.
And if we expand further, we see we have the test gameplay ability.
So we know we have an ability, which means we should make it into this for loop.
So if we go ahead and continue, it looks like we did not make it that far.
In fact, we didn't make it past the return.
Why?
Because look at that input tag is valid.
That's something I do from time to time.
If the input tag is valid, we're returning.
We actually only want to return if the input tag is not valid.
Right?
So I'm going to go ahead and stop and fix this bug.
It actually should be not is valid.
Same thing for the released version.
We're going to say not is valid.
Then we return.
Let's try this again.
That's just one of those little things that you probably noticed right away and put the negation operator
there.
Okay, I'm going to go ahead and open up my test gameplay ability.
I still have my left mouse button tag and if I press play and click, I get my message.
Five seconds later, I should get my test ability ended.
There it is.
And I should only be able to activate this if it's not already active, so I can't spam it and that's
great.
And if I didn't have the delay, I should be able to spam it because it will end itself instantly.
So let's try spamming it.
There we go.
In fact, we're spamming it even if I hold that button down because we're activating on ability input
tag held.
So that's something to be aware of.
We could be activating an ability every single frame if we're ending it instantly, right?
So something to be aware of, but our abilities are going to choose when they should end themselves.
So that shouldn't be a problem.
Okay, great.
So we now have the ability, for lack of a better word, to activate abilities using input, and we're
identifying those abilities based on their input tag.
So there are a few moving parts to this, but it's actually really versatile and really well put together
because we can associate abilities with their input tag and all we need to do is set the input tag for
a given ability to assign it to that specific input.
And we do that by having that input tag in the ability specs, dynamic ability tags.
So if we were to remove the input tag from an ability specs dynamic ability tags, we'd no longer be
able to activate it or deactivate it.
And if we remove the input tag from dynamic ability tags and then add another input tag to its dynamic
ability tags, then we can activate it using that other input associated with that other input tag.
Excellent.
So now that we can activate abilities and we know how to end them, we're all set up and ready to start
really architecting some interesting abilities.
So we'll do that in the videos to come.
So great job and I'll see you soon.