Okay, we're ready to add some attributes to our attribute set.
So I'd like to close all my tabs.
I'm going to right click, close all tabs, and I'm going to get my attributes set.
I'd like to get the header file open here.
So I'm opening ability system and here's my aura attribute set.
Now it's pretty empty and I would like a public section along with a constructor.
So I'll add the public section, I'll declare the constructor and I'll go ahead and generate the definition
for it.
That's going to open my CPP file.
All right, great.
Now back to the attributes set.
We're going to declare our attributes right here in the public section.
And I think the first attribute that's essential to just about any RPG character is health.
Now, whether you call it health or life, it doesn't really matter as long as you pick a convention
and stick with it for the game project, since health is pretty universal, I'm going to use that now.
We know that attributes have the type gameplay attribute data, so we're going to make a variable of
that type and call it health.
So that's the first thing we need to do is create the variable.
But there are just a couple of other things we need to do when we create an attribute, and these are
the steps that you'll be taking when you create all of your attributes and all of your gas projects.
Now we're going to make this a new property.
And we're also going to make this a replicated variable.
Most of our attributes are going to be replicated.
There will be a couple of exceptions and we'll get to those, but we want our variable to be replicated
to all clients.
So if it changes on the server, then clients will get the updated value.
And if we change it via gameplay effects, we know those are predicted, which means that they'll be
changed locally on a client and the server will be informed of that and so the server can change it.
And once it changes on the server, all other clients need to know about that change.
So it's important that health is replicated.
So to make a variable replicated, we mark it replicated with the replicated specifier.
But for an attribute we need to use a rep notify.
Remember rep notifies are called automatically when a variable replicates, so when the server replicates
the variable down to a client, the client will have the rep notify for that variable triggered as a
result.
So we want a rep notify for health and to do that we use the specifier replicated using equals followed
by the name of the rep notify and a common unreal engine convention is to create a function called on
rep underscore followed by the variable name.
So we haven't created this function.
It doesn't exist, but this is how we mark health to be replicated using a rep notify with that function
name.
So we have to create that.
We have to make a void function called on rep health.
And in order for it to be a rep notify it has to be a you function.
Now rep notifies can take zero arguments or they can take one argument and if they take an argument
it has to be of the type of the replicated variable.
In other words, an gameplay attribute data.
It can be a const reference for this, and if we provide that input parameter, then when on rep health
is called in response to health being replicated, then it will get the old value passed in as an input
parameter.
And this is useful for comparing the new value with the old value.
So we're going to add a const gameplay attribute data reference and this will be called Old Health so
we can generate the definition for this.
And as you can see, we have this old health here.
So when health replicates, it will now be equal to its new replicated value.
But we still know the old value because it's being passed in here and this is just an inherent quality
of rep notifies.
If you add that input parameter, then you get the old value and that's useful.
Now when we set up the rep notify for a replicated attribute in an attribute set, we have to inform
the ability system of that change.
So the ability system can do all the under-the-hood bookkeeping that it needs to do to keep the ability
system together working cohesively.
And to do that we use the macro gameplay attribute, underscore, rep notify, and this requires the
class name and that's going to be this class, the Ora attribute set we're going to pass in that followed
by the name of the replicated attribute that's going to be health, followed by the old value which
is old health.
So we'll pass in the old health value as well.
And now the gameplay ability system will know that health has just been replicated.
Now we're going to need to include ability system component to get rid of that red squiggle.
So I'm going to go ahead and include that header ability system component dot h and writer is going
to be telling us that we can make this function const if we want to.
And of course to do that we would just add const there, but we'd have to add it here as well.
Const.
So this takes care of informing the ability system that we're replicating a value, that its value has
just come down from the server and changed and now the ability system can register that change and keep
track of its old value just in case anything needs to be rolled back.
Remember, in the case of prediction, the server can roll changes back and undo them if it deems that
those are not valid changes.
Okay, so that's almost everything.
But if you've taken my multiplayer course, you know that to mark a variable as replicated, there's
another step we need to do.
There's a specific function that classes must have in order to register variables for replication,
and that function belongs here.
Just under the constructor in the public section, it's a virtual function, it's void and it's called
Get Lifetime replicated.
Props and get lifetime replicated props has an input parameter of type T array holding F lifetime property.
This t array is a reference and it's called out lifetime props.
This is a const function and we're overriding it.
So kind of a long function signature there, but this is the function we must override to replicate
any variables.
And if we go ahead and generate the definition for it, then this is the place where we register variables
for replication.
And to do that we use do rep lifetime.
Now do rep.
Lifetime has a number of flavors, one of which is condition notify.
That's the one we use for replicated attributes.
This requires the class name you or an attribute set.
It requires the variable itself.
That's health.
Next is a condition.
And if we type Conda underscore, we'll see that we have a number of conditions.
Now, you don't need to know a whole lot about this system.
To do this course.
You just need to know about the one we're using here.
We're going to use condition none.
In other words, we don't want to set any conditions for this variable to replicate.
We always want to replicate it.
And finally, we can add one more option.
Rep notify Always.
Now, let me just explain what this line is doing here for a moment.
It's registering health to be replicated that's required for anything that you would like to replicate.
It's going to replicate without any conditions.
And there are other conditions you could set.
For instance, if you wanted to only replicate to the owner.
There's an option for that, but we don't want any conditions, so we're setting it to none.
And rep notify always means that if the value is set on the server, replicate it and on the client,
that value will be updated and set.
Now compare that to on changed, which is the default option with on changed.
If you set the value of health on the server and that value hasn't changed, then there will be no replication.
This is a sort of optimization if you're setting it to the same value that it already has, there's
no need to replicate it in most cases.
But for gas we want to replicate it anyway because if we're setting it, we may want to respond to the
act of setting it.
Whether we've set it to a new value or its own same value, you may want to respond anyway, even if
its numerical value hasn't changed.
And for that reason we use always.
So that is the last step to creating an attribute and the attribute is set to replicate with a rep notify.
But that doesn't mean we can't have additional property specifiers.
You might want this to be accessible in blueprints.
We can make this blueprint read only.
I typically like to in case I'd like to access these from Blueprint and I typically give them a category
as well.
And this is going to be a specific type of attribute.
It's related to the well being of the character, right?
It's the character's health.
So we can refer to these as anything we like.
I'm going to call these health and other associated attributes, vital attributes.
Okay, So now that we have health, let's do this process one more time together and then I'm going
to set you loose to do it on your own with some additional attributes.
So we'll do this one more time.
And when we create health, we typically like to create a max health attribute.
So I'd like to do this for max health as well.
So I'm going to create an gameplay attribute data called Max Health, and I'll give it the same property
just about the same with one key difference it's rep notify has to be different.
We need a rep notify for max health so we'll create a void function on rep underscore max health and
it's going to take a const gameplay attribute data reference only.
It's going to be called old Max health and it'll be const, why not?
And we can generate its definition and it's going to need the gameplay attribute rep notify I can copy
and paste it only instead of health.
It's going to be the max health attribute and instead of old health we pass in old max health seeing
a bit of repetition here.
And also we.
You can't forget that the rep notify needs to be a you function.
And finally, we have the last step of registering max health and get lifetime replicated props so we
can copy this line.
And instead of health we'll have max health.
And now it's registered for replication and we're done.
So adding a new attribute involves a number of steps.
You first declare the variable, you make it replicated, you create a rep, notify that can accept
the old value.
You define that rep, notify and add the boilerplate gameplay attribute rep notify macro to it and then
you add that replicated variable to get lifetime replicated props with condition none and rep notify
always.
Now we refer to all these steps as boilerplate.
Boilerplate is just a required number of steps to get something set up and working and with attributes
in the attribute set.
It's very much boilerplate.
You're going to do this for all of your gas projects.
Anything that requires that you have attributes, you've got these boilerplate steps to follow.
So now that we've done this a couple times, you should feel pretty confident being able to do this
on your own.
So I'm going to give this to you as a quest.
I'd like you to create two new attributes.
I'd like you to make Mana and I'd also like you to make a max mana attribute and go through all that
boilerplate that we went through with health and max health.
So go ahead and pause the video here and add mana and max mana now.
Okay, so we're ready for mana and Max mana.
So if I'm careful, I should be able to do some copying and pasting here.
I'm going to copy both of these and paste them and I know that this will be mana and max mana, so I'll
change their names and looking at their uproperty macros, I see that I need these to change and actually
I'm noticing that max health it looks like I made a mistake copying this.
This should be on rep max health.
So copying these pointed out that I forgot to change that there and you might have noticed that.
So that should be on rep max health for the max health variable.
Mana should be on rep mana and max mana should be on rep max mana and these can be the vital attributes.
Okay.
Good thing I caught that.
That would have been a bug waiting to happen.
Right.
And I can go ahead and duplicate these two functions as well and change them to mana and max mana.
So this one will be on rep mana and instead of old health it's going to take old mana.
And then this one can be on rep Max Mana.
So I could change that and it will take old Max Mana and I can go ahead and create the function definitions
and looks like it only made my mana.
I'm going to generate max mana.
There we go.
And we need the boilerplate macro.
We'll go ahead and copy it in to both of them.
And for this one it's going to be simply mana and old mana and this one will be Max Mana and old Max
Mana.
Now the last step, and I actually forgot this when I initially recorded this video is to register Mana
and Max Mana up here and get lifetime replicated props.
So it's important to remember all those boilerplate steps as you can easily miss one.
And that's exactly what I did when I recorded the video.
So don't do what I did.
Make sure you remember.
So we do need do rep lifetime condition notify here.
So I'm going to copy and paste those and change this to mana and max mana.
So now these variables are registered for replication.
So because I forgot these two lines when I recorded the video, you're going to see in the next few
videos that these are missing, they should be there.
So in future videos, if you see that these two lines are not there, don't go and remove them.
You do need them there.
If they're not there, then Mana and Max Mana will not replicate in a multiplayer game.
So that's why we need them there.
That's why you should have them there.
And don't worry in the next few videos they'll be missing here, but they should be there so you can
copy and paste like I did and change what you need to change.
Just be prepared for some potential mistakes if you copy and paste.
But if you're careful, you can save some time that way.
That's the thing with these boilerplate types of things is there's really no getting around it.
There's no engineering your way out of having to type out these steps for new attributes, unfortunately.
But that's one of the few sort of meticulous things in the gas system that we have to deal with.
Okay.
So now we have four attributes in our attributes set and that's great.
So great job.
I'll see you in the next video.