WEBVTT

00:06.890 --> 00:08.030
Welcome back.

00:08.060 --> 00:10.940
Now, our system is looking pretty good so far.

00:11.060 --> 00:17.390
We have the ability to broadcast our initial values, and we've done so with our primary attributes.

00:17.390 --> 00:24.590
We've added those to our map that maps gameplay tags to function pointers, and those pointers point

00:24.590 --> 00:26.750
to our static accessor functions.

00:26.780 --> 00:29.360
Now this is great for initial values.

00:29.360 --> 00:34.160
That's something that we get broadcast when our widget is constructed.

00:34.190 --> 00:37.370
Our attribute menu calls, broadcast initial values.

00:37.370 --> 00:43.040
It's blueprint callable, but we also need to update the values as they change.

00:43.040 --> 00:51.140
So if strength changes, we want to broadcast the latest value of strength up to our widgets so they

00:51.140 --> 00:52.070
can update.

00:52.100 --> 00:59.900
Now, in order to do that, we could have a callback function or even just a lambda that will broadcast

00:59.900 --> 01:03.230
the value of our attributes as soon as they change.

01:03.230 --> 01:09.480
As we know that the ability system component has delegates that it broadcasts when they change.

01:09.510 --> 01:15.360
Now, we would bind those callbacks or lambdas here in bind callbacks to dependencies and it would look

01:15.360 --> 01:16.390
like this.

01:16.410 --> 01:23.700
We would take our ability system component and we would use get gameplay attribute value change delegate,

01:23.730 --> 01:26.310
which requires a gameplay attribute.

01:26.310 --> 01:32.640
And we could pass one in, which means we'd need to cast to or attribute set.

01:32.640 --> 01:39.690
So we would do something like take this line here and paste it there and then use that attribute set

01:40.980 --> 01:46.290
to call one of the attribute accessors like get strength attribute, for instance.

01:46.410 --> 01:51.750
This returns a delegate that we can bind a callback or a lambda to.

01:51.780 --> 01:59.640
So for example, let's say we called add lambda here and inside these parentheses we could create a

01:59.640 --> 02:00.300
lambda.

02:00.330 --> 02:05.370
Here's our square brackets, our parentheses and the lambda body.

02:05.610 --> 02:08.910
Now we do need to worry about the lambda signature.

02:08.910 --> 02:14.940
But before we fill that out, instead of just getting our strength attribute, why don't we just wrap

02:14.940 --> 02:18.420
this in a for loop and loop through all the attributes?

02:18.450 --> 02:19.320
We can do that.

02:19.320 --> 02:29.160
We can say for we can do an auto reference called pair and we can loop through the attribute set tags.

02:29.160 --> 02:30.190
Two attributes.

02:30.210 --> 02:34.580
Now, if we loop through this map, we can take these lines here.

02:34.590 --> 02:41.760
I can control C to cut them and simply paste them in and I can just remove them here.

02:41.880 --> 02:45.720
So now these lines are inside of the for loop.

02:45.720 --> 02:52.560
And now that we're in this for loop and we have access to each pair, which maps the tag to the attribute

02:52.560 --> 02:55.350
accessor, we can just call that attribute accessor.

02:55.350 --> 03:02.220
Here we can call pair dot value with parentheses for the function call.

03:02.220 --> 03:07.560
And now we're getting the gameplay attribute value change delegate for every single attribute in our

03:07.560 --> 03:08.130
map.

03:08.160 --> 03:10.750
Now we can worry about this lambda here.

03:10.770 --> 03:17.730
Now for get gameplay attribute value change delegate whatever we bind to this delegate needs the correct

03:17.730 --> 03:18.270
signature.

03:18.270 --> 03:18.950
Right?

03:18.960 --> 03:23.790
So what was the signature that our function or lambda needed to have?

03:23.820 --> 03:29.910
Well, it needed to take a const reference to F on attribute change data.

03:29.910 --> 03:33.240
So we have a const reference, we'll call that data.

03:33.240 --> 03:36.260
And now we can do what we like in here.

03:36.270 --> 03:43.080
Now, really all we need to do is get our attribute info, get an F or a attribute info struct, make

03:43.080 --> 03:49.350
sure that its attribute value is correct and broadcast that struct up to any widgets.

03:49.380 --> 03:57.330
So just like we've done down here, we can make a local F or attribute info called info and we can get

03:57.330 --> 04:01.830
our attribute info data asset and find attribute info for tag.

04:02.100 --> 04:05.070
I'm going to copy that line and paste it here.

04:05.280 --> 04:07.860
Now attribute info has red squiggles.

04:07.860 --> 04:12.180
We have to capture this if we want to use it.

04:12.210 --> 04:15.660
Now we also have red squiggles on pair dot key.

04:16.170 --> 04:18.090
Well, we can capture the pair.

04:18.120 --> 04:21.600
Now you can capture by value by reference.

04:21.630 --> 04:22.170
Really?

04:22.170 --> 04:23.340
Whichever you want.

04:23.370 --> 04:30.630
We don't want to capture by reference because by the time this attribute changes and the delegate gets

04:30.630 --> 04:38.160
broadcast, this pair variable, which is local to this for loop will have long since gone out of scope.

04:38.160 --> 04:40.800
So we just want to capture pair by value.

04:40.800 --> 04:46.620
This will cause the lambda to have a copy stored locally within it.

04:46.620 --> 04:50.400
So we have a copy of the key, which is the gameplay tag.

04:50.400 --> 04:51.600
That's what we want.

04:51.630 --> 04:55.530
Now we want to set the attribute info attribute value.

04:55.530 --> 05:03.230
So we're going to say info dot attribute value and we want the current numerical value.

05:03.240 --> 05:05.970
Well we can get pair dot value.

05:05.970 --> 05:06.360
That's a.

05:06.480 --> 05:07.190
Pointer.

05:07.200 --> 05:12.390
We're going to call that function to get the attribute itself and from the attribute we'll call get

05:12.420 --> 05:20.280
numeric value into which we must pass the attribute set I can pass in as however, that means I must

05:20.280 --> 05:22.010
capture the attribute set.

05:22.020 --> 05:26.930
So not only do we capture pair, we're also going to capture as as well.

05:26.940 --> 05:28.900
So that pointer will be there.

05:28.920 --> 05:30.770
So now we have info.

05:30.780 --> 05:34.350
Its attribute value is filled in, we can broadcast it.

05:34.350 --> 05:37.770
I can just copy this line and paste it here.

05:37.800 --> 05:45.540
Now, whenever the attribute changes, the lambda will be called in response to the attribute change

05:45.540 --> 05:47.070
delegate broadcast.

05:47.160 --> 05:50.820
So now we just need to make sure this is working.

05:50.820 --> 05:56.730
Whenever any of our attributes change, we want to make sure that that change is reflected in our attribute

05:56.730 --> 05:57.410
menu.

05:57.420 --> 06:03.780
But let's also go ahead and add the rest of our attributes to the tags to Attributes Map.

06:04.050 --> 06:06.020
We have our primary attributes.

06:06.030 --> 06:09.660
Why don't we get all of our secondary attributes added to it as well?

06:09.660 --> 06:13.200
So we'll go back to Aura attribute set and do that.

06:13.380 --> 06:20.850
Now we've only added to this map gameplay tags and attribute accessors for the primary attributes.

06:20.850 --> 06:23.430
Let's do it for all the attributes, shall we?

06:23.430 --> 06:33.120
So this will get a primary attributes comment, but let's do this for all of the secondary attributes

06:33.120 --> 06:33.930
as well.

06:35.640 --> 06:38.250
So I'm going to go ahead and do that.

06:38.250 --> 06:38.700
Now.

06:38.700 --> 06:40.230
We know what to do, right?

06:40.230 --> 06:46.680
We put the gameplay tag and the attribute accessor, so let's go ahead and time lapse this part.

07:59.490 --> 07:59.970
Okay.

07:59.970 --> 08:06.750
We now have all of our attributes, primary and secondary, that we want to see in our attribute menu,

08:06.750 --> 08:13.950
and our widget controller is going to make sure that our attribute menu will receive data whenever those

08:13.980 --> 08:15.360
attributes change.

08:15.480 --> 08:21.840
So now we can go ahead and test this out and make sure that our attributes are updating as they change.

08:23.280 --> 08:26.610
So the first thing I want to do is open our attribute menu.

08:26.640 --> 08:32.940
Make sure we get all the attributes and that their values are correct and it looks like we do.

08:32.970 --> 08:35.460
There's all of our secondary attributes.

08:35.460 --> 08:37.260
There's all of our primaries.

08:37.260 --> 08:38.190
They're all here.

08:38.190 --> 08:39.450
And that looks great.

08:39.480 --> 08:45.330
Now I'd like to change the values of them so that we can make sure that those changes are reflected

08:45.330 --> 08:47.040
in the attribute menu.

08:47.400 --> 08:53.630
Now we do have our test actor, which is applying a test effect, right?

08:53.640 --> 08:56.460
GE test attribute based.

08:56.490 --> 08:59.430
We can go back to that and open it.

08:59.460 --> 09:05.280
And if we scroll down, we see that it's setting our health, but now we can change any attribute we

09:05.280 --> 09:05.600
want.

09:05.610 --> 09:11.820
So what I'm going to do is I'm going to remove all the modifiers by hitting the trashcan.

09:12.970 --> 09:17.330
And add one new modifier and change one of my attributes.

09:17.350 --> 09:19.510
Let's just change strength.

09:19.510 --> 09:20.500
For example.

09:20.500 --> 09:27.040
We'll add to it with modifier op add and we'll add a value of 15 to it.

09:27.070 --> 09:28.390
Let's compile that.

09:28.390 --> 09:35.890
And now if I press play, I see that my strength is ten, and if I walk into this test effect actor

09:35.920 --> 09:38.230
now my strength is 25.

09:38.500 --> 09:39.670
Now that's great.

09:39.670 --> 09:45.820
It worked when the attribute menu was closed, but let's make sure that that change happens if the attribute

09:45.820 --> 09:47.110
menu is already open.

09:47.110 --> 09:53.410
So here we see strength is ten, and as we walk into the effect actor, it goes up to 25.

09:53.560 --> 10:00.730
And we can also check to make sure that our derived attributes are updating as their backing attributes

10:00.730 --> 10:01.210
change.

10:01.210 --> 10:05.420
For example, armor and armor penetration are dependent on resilience.

10:05.440 --> 10:10.750
Well, let's take our test attribute and change this to add to resilience.

10:10.750 --> 10:13.810
Instead, let's see how that works.

10:13.850 --> 10:15.980
So I'm going to open my menu.

10:16.020 --> 10:17.000
Here's resilience.

10:17.000 --> 10:18.020
It's 12.

10:18.050 --> 10:21.410
Armor and armor penetration are nine and four, respectively.

10:21.410 --> 10:28.610
And as I walk into the effect actor, not only did resilience go up to 27, but armor went up to 13

10:28.610 --> 10:30.890
and armor penetration went up to seven.

10:30.890 --> 10:37.550
So we see that all of our mathematical relationships are kept up and everything looks great.

10:37.700 --> 10:44.630
So now we have this nice, robust system where our attribute menu has widgets, each of which have their

10:44.630 --> 10:52.130
own attribute tags, and they all respond to delegates broadcast up from our widget controller and they

10:52.130 --> 10:53.570
can check that info.

10:53.570 --> 10:59.930
And if it has the tag that those widgets have assigned to them, they'll use that info to update themselves.

11:00.450 --> 11:02.100
So this looks really good.

11:02.250 --> 11:09.180
And if we hadn't gone through the trouble of making that map, then we'd have to manually bind callbacks

11:09.180 --> 11:14.760
or lambdas to each of our 14 attributes, which is a lot of work.

11:14.760 --> 11:19.170
And it would make this file really large for no reason.

11:19.530 --> 11:22.290
Now it looks like I've got an include for or a gameplay tags.

11:22.290 --> 11:23.930
I could probably remove that.

11:23.930 --> 11:25.620
It looks like it's not being used.

11:26.220 --> 11:27.960
Okay, this is looking good.

11:28.110 --> 11:31.470
Now, before we wrap up, I think there's one last thing we can do.

11:31.470 --> 11:33.450
I see some repeated code here.

11:33.480 --> 11:39.810
This doesn't follow the dry principle, so I can create a function that will perform these three lines

11:39.810 --> 11:40.500
of code.

11:40.530 --> 11:45.150
All it really needs is a gameplay tag and a gameplay attribute.

11:45.180 --> 11:47.310
Let's go ahead and make that function real quick.

11:47.310 --> 11:51.960
So an attribute menu widget controller will make this a private function.

11:52.950 --> 11:56.370
Just internal to this class, It'll be void.

11:56.370 --> 11:57.900
And we'll call this.

11:58.060 --> 12:01.410
Oh, why don't we call it broadcast attribute info?

12:02.350 --> 12:09.070
So broadcast attribute info it will take a const gameplay tag.

12:11.130 --> 12:17.220
Called attribute tag and a const F gameplay attribute.

12:18.420 --> 12:21.930
That'll also be a reference and we'll call this attribute.

12:22.320 --> 12:28.110
Let's go ahead and generate the definition and we'll perform these three lines that we've repeated ourselves

12:28.110 --> 12:28.860
with.

12:29.070 --> 12:35.220
Now find attribute info for tag needs the gameplay tag that's going to be attribute tag.

12:35.220 --> 12:41.100
We'll pass that in and then get numeric value that comes from the attribute.

12:41.100 --> 12:45.450
So we're going to replace pair dot value with attribute.

12:45.450 --> 12:47.970
And this just needs an attribute set.

12:48.000 --> 12:51.090
We don't need it to be cast to aura attribute set.

12:51.120 --> 12:57.180
We can just pass in attribute set and this can be a const function.

12:59.880 --> 13:03.560
I'll go ahead and make a const like so.

13:03.570 --> 13:09.800
And now broadcast attribute info can be reused in both of these other functions.

13:09.810 --> 13:11.850
So we'll call it here.

13:12.150 --> 13:14.850
Broadcast Attribute info.

13:15.060 --> 13:21.330
This one needs the attribute tag that's pair key and the attribute itself.

13:21.330 --> 13:28.530
That's pair dot value, but we're calling the value function like so.

13:28.530 --> 13:33.720
And I can get rid of these three lines and I can call the function down here as well.

13:34.430 --> 13:36.710
Broadcast attribute info.

13:36.740 --> 13:41.840
We can use pair key and pair dot value.

13:42.500 --> 13:46.280
Calling the function and we can get rid of these three lines.

13:47.420 --> 13:48.020
Okay.

13:48.020 --> 13:54.590
I'll go ahead and remove the empty space here and we're checking attribute info here.

13:54.680 --> 13:57.140
Might as well check it up here as well.

13:58.070 --> 14:00.680
So now we have a nice function.

14:00.680 --> 14:04.100
We can call and we can test this out as well.

14:04.130 --> 14:08.450
I can go ahead and close out of the editor saving all.

14:09.370 --> 14:11.260
And I can run this in debug mode.

14:11.260 --> 14:17.440
Before I do, I'm noticing that we're no longer using as here in our capture list.

14:17.470 --> 14:23.590
We don't need to capture that and we can go ahead and hit debug and test this out.

14:27.370 --> 14:34.210
So back in the editor, I'm going to open up these assets and make sure that I'm still changing my resilience

14:34.450 --> 14:36.730
with my test effect actor.

14:36.760 --> 14:41.230
I can take a look, make sure all of these look correct and up.

14:41.230 --> 14:43.510
My resilience there looks good.

14:43.510 --> 14:45.340
I can even do it multiple times.

14:47.300 --> 14:49.100
And it looks like things are working.

14:49.100 --> 14:50.430
This is great.

14:50.450 --> 14:58.130
So kind of a lot of work to get a small result, but the result of this is actually not insignificant.

14:58.160 --> 15:00.650
We now have code that's reusable.

15:00.650 --> 15:03.050
We have code that doesn't need to change.

15:03.050 --> 15:08.340
If we change something in our attribute set, such as adding new attributes.

15:08.360 --> 15:13.910
All we have to do in our attribute set is make sure that our attributes are associated with their attribute

15:13.940 --> 15:18.490
tags, which we do here in the attribute set constructor.

15:18.500 --> 15:24.320
So as long as we want an attribute associated with a given tag, we just add it to our map here and

15:24.320 --> 15:28.460
then we can always loop through that map if we want to do something for all attributes.

15:28.460 --> 15:35.540
So this is a very powerful thing and it saved us a lot of tedious repetition, which is always good.

15:35.570 --> 15:37.010
So excellent job.

15:37.010 --> 15:38.480
We now have an attribute set.

15:38.480 --> 15:39.530
It's looking good.

15:39.530 --> 15:45.020
It's now showing attribute values and responding to their changes, which is all great.

15:45.050 --> 15:51.420
Now we have this notion of gaining points and spending them on attributes to upgrade them and we'll

15:51.420 --> 15:54.780
implement that as we keep developing this project.

15:54.780 --> 15:57.630
But for now, our attribute set is looking good.

15:57.750 --> 16:02.910
Aside from probably some visual tweaks that we may wish to make, the framework is there.

16:03.180 --> 16:07.830
So once again, excellent job and I'll see you in the next video.
