WEBVTT

00:07.250 --> 00:12.080
Okay, we're ready to add some attributes to our attribute set.

00:12.170 --> 00:14.330
So I'd like to close all my tabs.

00:14.330 --> 00:19.670
I'm going to right click, close all tabs, and I'm going to get my attributes set.

00:19.670 --> 00:21.920
I'd like to get the header file open here.

00:22.010 --> 00:27.200
So I'm opening ability system and here's my aura attribute set.

00:27.350 --> 00:33.050
Now it's pretty empty and I would like a public section along with a constructor.

00:33.140 --> 00:40.340
So I'll add the public section, I'll declare the constructor and I'll go ahead and generate the definition

00:40.340 --> 00:41.180
for it.

00:41.510 --> 00:43.790
That's going to open my CPP file.

00:43.880 --> 00:45.830
All right, great.

00:46.160 --> 00:48.410
Now back to the attributes set.

00:48.710 --> 00:53.030
We're going to declare our attributes right here in the public section.

00:53.030 --> 00:59.600
And I think the first attribute that's essential to just about any RPG character is health.

00:59.720 --> 01:05.600
Now, whether you call it health or life, it doesn't really matter as long as you pick a convention

01:05.600 --> 01:12.060
and stick with it for the game project, since health is pretty universal, I'm going to use that now.

01:12.060 --> 01:19.650
We know that attributes have the type gameplay attribute data, so we're going to make a variable of

01:19.680 --> 01:21.600
that type and call it health.

01:21.840 --> 01:26.610
So that's the first thing we need to do is create the variable.

01:26.610 --> 01:32.490
But there are just a couple of other things we need to do when we create an attribute, and these are

01:32.490 --> 01:39.300
the steps that you'll be taking when you create all of your attributes and all of your gas projects.

01:39.660 --> 01:42.000
Now we're going to make this a new property.

01:43.280 --> 01:46.790
And we're also going to make this a replicated variable.

01:46.820 --> 01:50.180
Most of our attributes are going to be replicated.

01:50.210 --> 01:57.410
There will be a couple of exceptions and we'll get to those, but we want our variable to be replicated

01:57.410 --> 01:58.340
to all clients.

01:58.340 --> 02:03.800
So if it changes on the server, then clients will get the updated value.

02:03.800 --> 02:09.500
And if we change it via gameplay effects, we know those are predicted, which means that they'll be

02:09.500 --> 02:15.920
changed locally on a client and the server will be informed of that and so the server can change it.

02:15.920 --> 02:20.330
And once it changes on the server, all other clients need to know about that change.

02:20.330 --> 02:23.090
So it's important that health is replicated.

02:23.090 --> 02:29.030
So to make a variable replicated, we mark it replicated with the replicated specifier.

02:29.360 --> 02:33.920
But for an attribute we need to use a rep notify.

02:33.950 --> 02:41.570
Remember rep notifies are called automatically when a variable replicates, so when the server replicates

02:41.570 --> 02:48.000
the variable down to a client, the client will have the rep notify for that variable triggered as a

02:48.000 --> 02:48.720
result.

02:48.750 --> 02:57.690
So we want a rep notify for health and to do that we use the specifier replicated using equals followed

02:57.690 --> 03:06.390
by the name of the rep notify and a common unreal engine convention is to create a function called on

03:06.390 --> 03:09.810
rep underscore followed by the variable name.

03:09.930 --> 03:11.700
So we haven't created this function.

03:11.700 --> 03:18.480
It doesn't exist, but this is how we mark health to be replicated using a rep notify with that function

03:18.480 --> 03:18.990
name.

03:18.990 --> 03:20.460
So we have to create that.

03:20.460 --> 03:24.240
We have to make a void function called on rep health.

03:25.400 --> 03:30.620
And in order for it to be a rep notify it has to be a you function.

03:30.630 --> 03:37.220
Now rep notifies can take zero arguments or they can take one argument and if they take an argument

03:37.220 --> 03:40.250
it has to be of the type of the replicated variable.

03:40.250 --> 03:43.160
In other words, an gameplay attribute data.

03:43.190 --> 03:50.570
It can be a const reference for this, and if we provide that input parameter, then when on rep health

03:50.570 --> 03:59.120
is called in response to health being replicated, then it will get the old value passed in as an input

03:59.120 --> 03:59.660
parameter.

03:59.660 --> 04:04.070
And this is useful for comparing the new value with the old value.

04:04.070 --> 04:13.280
So we're going to add a const gameplay attribute data reference and this will be called Old Health so

04:13.280 --> 04:15.740
we can generate the definition for this.

04:15.740 --> 04:19.370
And as you can see, we have this old health here.

04:19.370 --> 04:25.020
So when health replicates, it will now be equal to its new replicated value.

04:25.020 --> 04:30.810
But we still know the old value because it's being passed in here and this is just an inherent quality

04:30.810 --> 04:32.010
of rep notifies.

04:32.010 --> 04:37.290
If you add that input parameter, then you get the old value and that's useful.

04:37.320 --> 04:45.960
Now when we set up the rep notify for a replicated attribute in an attribute set, we have to inform

04:45.960 --> 04:48.840
the ability system of that change.

04:48.840 --> 04:55.350
So the ability system can do all the under-the-hood bookkeeping that it needs to do to keep the ability

04:55.350 --> 04:58.590
system together working cohesively.

04:58.650 --> 05:07.920
And to do that we use the macro gameplay attribute, underscore, rep notify, and this requires the

05:07.920 --> 05:15.750
class name and that's going to be this class, the Ora attribute set we're going to pass in that followed

05:15.750 --> 05:23.430
by the name of the replicated attribute that's going to be health, followed by the old value which

05:23.430 --> 05:25.610
is old health.

05:25.610 --> 05:29.150
So we'll pass in the old health value as well.

05:29.150 --> 05:35.450
And now the gameplay ability system will know that health has just been replicated.

05:35.570 --> 05:41.750
Now we're going to need to include ability system component to get rid of that red squiggle.

05:41.750 --> 05:48.770
So I'm going to go ahead and include that header ability system component dot h and writer is going

05:48.770 --> 05:52.670
to be telling us that we can make this function const if we want to.

05:52.670 --> 05:58.310
And of course to do that we would just add const there, but we'd have to add it here as well.

05:58.310 --> 05:59.090
Const.

05:59.390 --> 06:05.570
So this takes care of informing the ability system that we're replicating a value, that its value has

06:05.570 --> 06:12.590
just come down from the server and changed and now the ability system can register that change and keep

06:12.620 --> 06:16.940
track of its old value just in case anything needs to be rolled back.

06:16.940 --> 06:23.900
Remember, in the case of prediction, the server can roll changes back and undo them if it deems that

06:23.900 --> 06:26.570
those are not valid changes.

06:26.570 --> 06:29.630
Okay, so that's almost everything.

06:29.750 --> 06:37.130
But if you've taken my multiplayer course, you know that to mark a variable as replicated, there's

06:37.130 --> 06:38.510
another step we need to do.

06:38.510 --> 06:45.560
There's a specific function that classes must have in order to register variables for replication,

06:45.560 --> 06:47.900
and that function belongs here.

06:47.900 --> 06:54.980
Just under the constructor in the public section, it's a virtual function, it's void and it's called

06:54.980 --> 06:58.220
Get Lifetime replicated.

06:59.860 --> 07:11.230
Props and get lifetime replicated props has an input parameter of type T array holding F lifetime property.

07:11.230 --> 07:17.230
This t array is a reference and it's called out lifetime props.

07:17.710 --> 07:21.580
This is a const function and we're overriding it.

07:21.580 --> 07:29.020
So kind of a long function signature there, but this is the function we must override to replicate

07:29.020 --> 07:29.830
any variables.

07:29.830 --> 07:37.780
And if we go ahead and generate the definition for it, then this is the place where we register variables

07:37.780 --> 07:38.920
for replication.

07:38.920 --> 07:43.180
And to do that we use do rep lifetime.

07:43.600 --> 07:44.650
Now do rep.

07:44.650 --> 07:49.240
Lifetime has a number of flavors, one of which is condition notify.

07:49.270 --> 07:53.460
That's the one we use for replicated attributes.

07:53.470 --> 07:58.510
This requires the class name you or an attribute set.

07:58.510 --> 08:00.470
It requires the variable itself.

08:00.470 --> 08:01.490
That's health.

08:01.520 --> 08:03.440
Next is a condition.

08:03.440 --> 08:08.590
And if we type Conda underscore, we'll see that we have a number of conditions.

08:08.600 --> 08:12.320
Now, you don't need to know a whole lot about this system.

08:12.320 --> 08:13.580
To do this course.

08:13.580 --> 08:16.610
You just need to know about the one we're using here.

08:16.610 --> 08:18.230
We're going to use condition none.

08:18.230 --> 08:23.000
In other words, we don't want to set any conditions for this variable to replicate.

08:23.030 --> 08:24.980
We always want to replicate it.

08:25.100 --> 08:28.970
And finally, we can add one more option.

08:28.970 --> 08:31.490
Rep notify Always.

08:31.880 --> 08:35.560
Now, let me just explain what this line is doing here for a moment.

08:35.570 --> 08:42.470
It's registering health to be replicated that's required for anything that you would like to replicate.

08:42.470 --> 08:45.230
It's going to replicate without any conditions.

08:45.230 --> 08:47.960
And there are other conditions you could set.

08:47.960 --> 08:51.950
For instance, if you wanted to only replicate to the owner.

08:51.980 --> 08:56.780
There's an option for that, but we don't want any conditions, so we're setting it to none.

08:57.020 --> 09:06.170
And rep notify always means that if the value is set on the server, replicate it and on the client,

09:06.200 --> 09:09.740
that value will be updated and set.

09:09.860 --> 09:16.580
Now compare that to on changed, which is the default option with on changed.

09:16.610 --> 09:24.830
If you set the value of health on the server and that value hasn't changed, then there will be no replication.

09:24.860 --> 09:31.040
This is a sort of optimization if you're setting it to the same value that it already has, there's

09:31.040 --> 09:33.590
no need to replicate it in most cases.

09:33.590 --> 09:41.420
But for gas we want to replicate it anyway because if we're setting it, we may want to respond to the

09:41.420 --> 09:42.620
act of setting it.

09:42.620 --> 09:50.780
Whether we've set it to a new value or its own same value, you may want to respond anyway, even if

09:50.780 --> 09:52.880
its numerical value hasn't changed.

09:52.880 --> 09:55.310
And for that reason we use always.

09:55.310 --> 10:05.300
So that is the last step to creating an attribute and the attribute is set to replicate with a rep notify.

10:05.330 --> 10:09.410
But that doesn't mean we can't have additional property specifiers.

10:09.410 --> 10:12.160
You might want this to be accessible in blueprints.

10:12.170 --> 10:14.300
We can make this blueprint read only.

10:14.840 --> 10:22.190
I typically like to in case I'd like to access these from Blueprint and I typically give them a category

10:22.190 --> 10:23.150
as well.

10:24.740 --> 10:30.140
And this is going to be a specific type of attribute.

10:30.170 --> 10:33.320
It's related to the well being of the character, right?

10:33.320 --> 10:35.030
It's the character's health.

10:35.030 --> 10:37.820
So we can refer to these as anything we like.

10:37.820 --> 10:43.580
I'm going to call these health and other associated attributes, vital attributes.

10:44.240 --> 10:50.030
Okay, So now that we have health, let's do this process one more time together and then I'm going

10:50.030 --> 10:54.230
to set you loose to do it on your own with some additional attributes.

10:54.230 --> 10:56.270
So we'll do this one more time.

10:56.270 --> 11:01.490
And when we create health, we typically like to create a max health attribute.

11:01.490 --> 11:04.430
So I'd like to do this for max health as well.

11:04.430 --> 11:14.990
So I'm going to create an gameplay attribute data called Max Health, and I'll give it the same property

11:15.020 --> 11:20.540
just about the same with one key difference it's rep notify has to be different.

11:20.570 --> 11:28.430
We need a rep notify for max health so we'll create a void function on rep underscore max health and

11:28.430 --> 11:33.170
it's going to take a const gameplay attribute data reference only.

11:33.170 --> 11:38.720
It's going to be called old Max health and it'll be const, why not?

11:38.720 --> 11:47.090
And we can generate its definition and it's going to need the gameplay attribute rep notify I can copy

11:47.090 --> 11:49.400
and paste it only instead of health.

11:49.400 --> 11:56.720
It's going to be the max health attribute and instead of old health we pass in old max health seeing

11:56.720 --> 11:58.250
a bit of repetition here.

11:58.250 --> 11:59.450
And also we.

11:59.490 --> 12:03.660
You can't forget that the rep notify needs to be a you function.

12:03.660 --> 12:12.300
And finally, we have the last step of registering max health and get lifetime replicated props so we

12:12.300 --> 12:13.740
can copy this line.

12:13.740 --> 12:16.410
And instead of health we'll have max health.

12:16.410 --> 12:20.580
And now it's registered for replication and we're done.

12:20.760 --> 12:24.990
So adding a new attribute involves a number of steps.

12:25.020 --> 12:31.080
You first declare the variable, you make it replicated, you create a rep, notify that can accept

12:31.080 --> 12:32.460
the old value.

12:32.490 --> 12:41.580
You define that rep, notify and add the boilerplate gameplay attribute rep notify macro to it and then

12:41.580 --> 12:48.510
you add that replicated variable to get lifetime replicated props with condition none and rep notify

12:48.510 --> 12:49.460
always.

12:49.470 --> 12:53.070
Now we refer to all these steps as boilerplate.

12:53.100 --> 13:00.190
Boilerplate is just a required number of steps to get something set up and working and with attributes

13:00.190 --> 13:01.330
in the attribute set.

13:01.330 --> 13:03.310
It's very much boilerplate.

13:03.340 --> 13:06.310
You're going to do this for all of your gas projects.

13:06.310 --> 13:14.100
Anything that requires that you have attributes, you've got these boilerplate steps to follow.

13:14.110 --> 13:19.510
So now that we've done this a couple times, you should feel pretty confident being able to do this

13:19.540 --> 13:20.740
on your own.

13:21.040 --> 13:24.160
So I'm going to give this to you as a quest.

13:24.370 --> 13:27.370
I'd like you to create two new attributes.

13:27.370 --> 13:35.530
I'd like you to make Mana and I'd also like you to make a max mana attribute and go through all that

13:35.530 --> 13:38.850
boilerplate that we went through with health and max health.

13:38.860 --> 13:44.710
So go ahead and pause the video here and add mana and max mana now.

13:47.890 --> 13:51.340
Okay, so we're ready for mana and Max mana.

13:51.430 --> 13:56.780
So if I'm careful, I should be able to do some copying and pasting here.

13:56.800 --> 14:06.220
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

14:06.220 --> 14:13.450
change their names and looking at their uproperty macros, I see that I need these to change and actually

14:13.450 --> 14:18.640
I'm noticing that max health it looks like I made a mistake copying this.

14:18.640 --> 14:20.620
This should be on rep max health.

14:20.620 --> 14:26.440
So copying these pointed out that I forgot to change that there and you might have noticed that.

14:26.620 --> 14:29.830
So that should be on rep max health for the max health variable.

14:29.830 --> 14:39.940
Mana should be on rep mana and max mana should be on rep max mana and these can be the vital attributes.

14:40.660 --> 14:41.170
Okay.

14:41.170 --> 14:42.460
Good thing I caught that.

14:42.460 --> 14:45.430
That would have been a bug waiting to happen.

14:45.430 --> 14:46.150
Right.

14:46.150 --> 14:53.990
And I can go ahead and duplicate these two functions as well and change them to mana and max mana.

14:53.990 --> 15:00.440
So this one will be on rep mana and instead of old health it's going to take old mana.

15:00.440 --> 15:03.590
And then this one can be on rep Max Mana.

15:03.590 --> 15:11.870
So I could change that and it will take old Max Mana and I can go ahead and create the function definitions

15:11.870 --> 15:14.840
and looks like it only made my mana.

15:14.870 --> 15:17.150
I'm going to generate max mana.

15:17.180 --> 15:17.990
There we go.

15:18.530 --> 15:21.800
And we need the boilerplate macro.

15:21.800 --> 15:24.440
We'll go ahead and copy it in to both of them.

15:24.440 --> 15:36.680
And for this one it's going to be simply mana and old mana and this one will be Max Mana and old Max

15:36.680 --> 15:37.370
Mana.

15:39.090 --> 15:47.280
Now the last step, and I actually forgot this when I initially recorded this video is to register Mana

15:47.280 --> 15:51.680
and Max Mana up here and get lifetime replicated props.

15:51.690 --> 15:58.140
So it's important to remember all those boilerplate steps as you can easily miss one.

15:58.140 --> 16:02.310
And that's exactly what I did when I recorded the video.

16:02.310 --> 16:04.290
So don't do what I did.

16:04.290 --> 16:05.580
Make sure you remember.

16:05.580 --> 16:10.860
So we do need do rep lifetime condition notify here.

16:10.860 --> 16:18.090
So I'm going to copy and paste those and change this to mana and max mana.

16:19.780 --> 16:24.070
So now these variables are registered for replication.

16:24.070 --> 16:30.190
So because I forgot these two lines when I recorded the video, you're going to see in the next few

16:30.190 --> 16:34.120
videos that these are missing, they should be there.

16:34.120 --> 16:39.970
So in future videos, if you see that these two lines are not there, don't go and remove them.

16:39.970 --> 16:41.440
You do need them there.

16:41.440 --> 16:47.770
If they're not there, then Mana and Max Mana will not replicate in a multiplayer game.

16:47.770 --> 16:49.240
So that's why we need them there.

16:49.270 --> 16:51.610
That's why you should have them there.

16:51.610 --> 17:00.880
And don't worry in the next few videos they'll be missing here, but they should be there so you can

17:00.880 --> 17:04.810
copy and paste like I did and change what you need to change.

17:04.810 --> 17:10.660
Just be prepared for some potential mistakes if you copy and paste.

17:10.660 --> 17:14.170
But if you're careful, you can save some time that way.

17:14.170 --> 17:19.610
That's the thing with these boilerplate types of things is there's really no getting around it.

17:19.610 --> 17:28.340
There's no engineering your way out of having to type out these steps for new attributes, unfortunately.

17:28.340 --> 17:34.460
But that's one of the few sort of meticulous things in the gas system that we have to deal with.

17:34.910 --> 17:35.300
Okay.

17:35.300 --> 17:41.210
So now we have four attributes in our attributes set and that's great.

17:41.210 --> 17:42.650
So great job.

17:42.650 --> 17:44.330
I'll see you in the next video.
