Welcome back.
Now, we've created an ability and we've seen that we can activate abilities as soon as we grant them,
which is great.
But we'd like to be able to activate abilities via input.
So let's discuss about possible ways to do it.
Now, binding input directly to the ability system component was something that we could do before enhanced
input, before enhanced input came around.
There was the option to bind input directly to abilities.
So you press a button and an ability receives that input automatically and either activates or does
whatever it wants to do depending on how you've coded that ability.
Now, this was done by creating an enum with ability input constants, so it was a rigid system as you
would sort of hardcode these enum constants into an ability input enum and those inputs would be linked
to those specific abilities.
Now this was something that we could do before the old input system became deprecated, but now we have
enhanced input and things are done a little bit differently and the changes are for the better, in
my opinion.
Input actions are bound to inputs via the input mapping context.
This is just how enhanced input works.
Now we can decide how to activate abilities in response to these inputs, and we can do this any way
we'd like.
Lyra, the multiplayer Shooter Example project provided by Epic Games, provides us with one example
of linking up enhanced input with the gameplay ability system abilities of that project.
And it's a little bit overengineered.
Lyra is a great example of how to do things, but it can be overly complicated and hard to approach.
Now what we'll do in this course is similar to how Lyra does it, but a little bit less complicated
as we don't need the over flexibility that Lyra provides us with.
So our approach will be a little bit less complicated, but that doesn't mean it will be less useful
or flexible.
We're going to take a data driven approach.
I'd like to be able to change the input to ability mappings at runtime.
In other words, pressing the left mouse button to activate a specific ability should be something that
we should be able to change at runtime.
And thanks to the gameplay ability system's flexibility, this is actually not very hard to do provided
that we code this well.
Now what we're going to do is create a data asset that's going to contain input actions, but we're
going to link up input actions with gameplay tags.
I'd like to have a gameplay tag for each of our inputs.
For example, the one key, the left mouse button, the right mouse button and so on.
And at runtime we should be able to assign various tags to our gameplay abilities.
And then as we activate abilities, every ability will be associated with its input tag, which can
be checked and changed.
So the first step is to make this data asset.
So let's do it.
I'm going to launch the editor.
And we'll make a new C plus plus class for our data asset.
So I'm going to go to C plus plus classes.
Aura public.
And we're going to put this new data asset into a new folder called input.
Let's go ahead and create a new C plus plus class and choose all classes.
And this will be a data asset.
So selecting data asset, we're going to put this in public slash input.
Now this is going to be an input configuration, so I'm going to call this aura input config.
So I'll go ahead and create the class and close the editor.
Now I'm going to go ahead and close all tabs and open my new input config.
So starting with the public folder, I'm going to open input.
Here's ora input config and alt o will open the cpp file.
Now the input config is not going to be terribly complicated.
I just want an array of input actions, but our input actions need to be linked to gameplay tags.
So I want an easy way to link up an input action with a gameplay tag.
And the way that Lyra does it is pretty nice and that's the approach that I'm going to take for our
project.
What we're going to do is create a struct and the struct will be simple.
It will contain an input action and a gameplay tag.
So we'll make this struct here.
We'll call this f ora input action.
And we'll give it a use struct.
With blueprint type and of course a generated body and F or a input action is going to contain an input
action.
Now I'm going to make this a const input action and I'm also going to forward declare the class you
input action so we don't have to include that header file.
And this input action pointer will be called input action and it will be initialized to null pointer.
Now it's const, meaning it can't be changed at runtime, but that doesn't mean we can't set it to a
value.
We're going to set it in the blueprint for this data asset by giving it a new property and making it
either edit anywhere or edit defaults only.
I think edit defaults only makes more sense.
And in addition to this we'll make an F gameplay tag called input tag and this should also be a const
gameplay tag.
We'll initialize it with f gameplay tag.
Just an empty tag.
Now this is not a pointer or a reference, so we are going to include the header file for our gameplay
tag.
That's gameplay tag container dot h and this can be also edit defaults only.
And now that we have this struct, our data asset can contain an array of these that we can set in the
data asset blueprint.
Now we're going to add public didn't need to do that for the struct because struct bodies are public
automatically.
Right?
So let's add this t array.
It's going to be of type for input action.
So we'll have an array of these structs.
We're going to call this ability input actions.
Now this we're going to fill in in the blueprint.
So we're going to give it a new property with edit defaults only.
I'd like to make it blueprint read only, just in case we need to access it.
And now we have a data asset that contains an array of input actions specifically, or input actions
which have a game play tag.
Now this is great because if we have a pointer or a reference to this input config, then we can look
up in this array any of our input actions that have a specific tag.
And I'd like to make a function for performing this lookup.
So we're going to make a function that returns a const.
You input action.
So why do we want to return an input action and not an input action?
Well, we could return an input action, but this is going to be more useful if we can return just the
input action associated with a gameplay tag.
And then this input action is kind of an internal thing to our input config.
We don't have to worry about breaking it open and finding the input action inside.
This function is just going to do all that for us.
So this function will be called find ability input action for tag, which will take a const gameplay
tag by reference called input tag and I'll have an optional boolean be log not found.
We'll set it to false by default and this will be a const function.
So this will simply return the input action associated with a given tag.
So let's generate the definition.
Now, if you'd like a little challenge, why don't you implement this on your own?
Go ahead and pause the video and figure out how to find the input action associated with a given tag
from our Ability Input actions array.
So if you're up for the challenge, go ahead and pause the video now and give it a try.
Okay.
So I want to find the input action associated with the input tag passed into this function.
So I'm simply going to loop through our input actions in ability input actions.
So this will be a range based for loop looping through each for input action.
I'm going to use const reference.
We'll call this action and it's each action in ability input actions.
What are we going to do?
Well, we're going to check to see if it's tag matches.
So we'll say if and I'll say action dot input.
Action.
And this input action could be a null pointer.
So we'll check to see if it's null.
And we'll also check its input tag.
So we'll say action dot input tag.
We'll see if it's equal to input tag.
And if we succeed and get inside this if statement, we'll return this input action.
So we'll say return action, dot input action, and that's it.
Now we have this bool for if we can't find anything right, if we don't find our input action or perhaps
we fail to get inside this if statement and get to this return that means the input action was a null
pointer.
Any of those things happen, then we have a problem.
So we'll say if B log not found, then we'll do a u log.
We'll log out an error.
So for now we're using log temp until we create our own log category and we're going to say error text
and we can just say can't find ability input action for input tag and we'll say the tag.
We'll use a percent s for the string for tag on input config and we'll say what this class is.
So first we have our input tag.
We're going to use star input tag.
Dot to string.
And then for the input config we're going to use the name and we can use the function get name safe.
Passing in this.
And this returns an F string.
We'll have to use the star on this as well.
So we'll log an error if B log not found.
And by the end of this.
We'll have to return a null pointer as we have to return a pointer from this.
Right.
So now we have a function to find an input action associated with a given tag.
And this only works if we link up tags to input actions by filling out this data asset.
Now we also need input tags, right?
We're going to need a bunch of gameplay tags for any of the inputs that we want in our game so we can
add those natively to aura gameplay tags.
So I'd like to do that.
We have our primary and secondary attributes.
I'll put these underneath.
It looks like we have this empty protected section.
Since it's not being used, I'll go ahead and remove that and we'll make an gameplay tag.
For each of our inputs.
So I'm going to call these input tag as the first tag in the hierarchy.
We'll say input tag dot and I'll have an NB for left mouse button.
I'd like one for the right mouse button.
I'll call that RMB and then we'll have one for the number keys.
And really it's not very important which keys we end up mapping to these.
I'm just going to give these tags the names, input tag, dot one, two, three and four.
And I can go into aura gameplay tags dot CP and add those right here in our initialized native gameplay
tags function.
So after secondary attributes, I'm going to actually copy this function call to add native gameplay
tag.
I'm going to copy the comment as well and go all the way down to the bottom and this is going to be
input tags.
So we have gameplay tags, dot and the name of the tag.
This is going to start off with input tag lmdb and we're going to add a native gameplay tag and it's
going to be input tag dot lmdb.
And for the F string we can just say.
Input tag for left mouse button and we can just follow this pattern and make the rest of them.
So we're going to have the right mouse button as well as and it looks like I missed the semicolon there,
as well as keys one, two, three and four.
So we just need to change these.
This one will be input tag, R&B.
We'll change the name as well.
And this will say right mouse button.
Next we'll have input tag one.
The string will say input tag one.
This is error prone, so we have to make sure not to put any typos in.
This will say input tag for instead of left mouse button, we'll say one key.
Next we have input tag two changing the name and its input tag for to key.
And then we have three.
We'll go ahead and change that one.
And finally input tag for.
And there we go.
So now that we've added some native tags for our inputs, then our input tag makes more sense here in
our data asset and we can make some input actions and add them to our data asset.
So what we can do is let's go ahead and compile and launch, make sure we don't have any errors.
And it looks like I have an error here or an input config const properties are not supported.
We're going to have to remove the const from the gameplay tag.
Compiling again.
And that's going to fix that problem so we can have a const pointer.
We can't have a const gameplay tag.
Let's launch the engine now.
And we just need to create our new data asset.
Before we do, let's go to project settings and just make sure that everything looks okay.
In our gameplay tags, we have our input tag category with one through four LNB and R&B and that looks
great.
So we'll make our data asset.
Let's go to Blueprints and we have an input category already where we have our input mapping context
and our input actions.
Let's go ahead and make our data asset.
We'll go to Miscellaneous Data Asset and choose Aura input config.
Select that, call this D a R input config.
We'll go ahead and open this up and we can add some ability input actions, but we need input actions
for all of our inputs and that's going to be relatively straightforward.
Now Move is a two dimensional input, but for our ability inputs, we're going to be one dimensional.
So we'll right click, we'll go to input, choose input action.
We'll call these I a underscore and just keep it simple.
This one will be B, we'll open this up.
It's going to be axis one D float.
And that's it.
And we'll make all of the rest of them.
So we'll make another one.
And put action.
We'll call this I a R&B.
Change it to axis 1d float.
We'll make another one.
I a underscore one again value type axis 1d float and we just need two through three so I can quickly
time lapse the rest of them.
Okay, so we've got them all.
I'm going to save all and go ahead and close out of these input actions.
Just middle mouse clicking on the tab there.
And now we need to make sure that our input mapping context has them mapped to inputs.
So we have our I move, let's go ahead and add the rest of the input actions.
So here's the next one and I'll start with a one and click the keyboard and hit the one key and that's
it.
This is going to be simple.
I'm not going to add any triggers or modifiers.
We're just going to map the I one to the one key.
Let's add another one.
This will be IA2.
I'll click the keyboard and hit two.
We'll add another mapping.
This will be IA3 mapped to three.
And another one.
This will be IA4 mapped to four and then two more for the mouse buttons I'm going to click plus.
This will be in a lab and I'm going to click and click.
That'll give me the left mouse button.
And finally.
R i, a, R and B right there.
I'm going to left click and right click and that'll give me the right mouse button.
And now they're all linked up to the inputs that I want them to have.
But of course we can always switch out input mapping contexts if we want those to be changed.
But now that I have my input mapping context set up, we know that each input action is linked to those
keys.
As long as we're using that input mapping context, which we are, then we can add these to our input
config by clicking plus and expanding it.
And ability input actions is an array of or input actions.
Those are just a struct with an input action and an input tag.
And this data asset is what we will use to link up those input actions with their tags.
So I'm going to choose IA1 and for the tag I'm going to expand input tag and choose one.
And now IA1 is associated with this tag.
And if we call the function we created to look up an input action based on a tag, then we'll get the
correct input action.
So that's a one.
Let's add another one.
We'll choose IA2 for the input tag.
We'll choose input tag two and we'll repeat this process for the rest of them.
Okay.
So now we have our input config filled out.
Now this is cool because we can always swap out our data assets even dynamically at runtime if we want
to swap those out.
We can swap out for other inputs.
We can swap out for other configurations that have different tags, whatever we want really.
But now we have six different input tags associated with different input actions.
And this is what we'll be using to link up our inputs to not only ability activation, but whatever
else we want to link up our inputs to.
And that's the first step in our system.
So excellent job.
Make sure to save all and I'll see you in the next video.