WEBVTT

00:07.020 --> 00:08.180
Welcome back.

00:08.190 --> 00:11.280
So we know that our broadcast is working.

00:11.280 --> 00:19.740
We can broadcast the initial values to the widgets, any widgets that bind to our attribute info delegate.

00:19.830 --> 00:23.940
Now, so far, all we're doing is getting our strength attribute.

00:23.940 --> 00:31.110
In fact, we're just getting the strength attribute gameplay tag from our gameplay tags and finding

00:31.110 --> 00:36.240
the attribute info struct from our attribute info data asset.

00:36.240 --> 00:44.580
And then we're setting the attribute value for that by getting our attributes set and getting the numerical

00:44.580 --> 00:46.170
value of the strength.

00:46.170 --> 00:52.530
And once we package all that up into the info struct, we broadcast it up to the widget side so the

00:52.530 --> 00:54.150
widgets can receive it.

00:54.330 --> 01:01.920
Now if we follow this pattern, we have to do this manually for each individual attribute and we have

01:01.920 --> 01:03.000
a lot of them.

01:03.120 --> 01:11.590
In other words, we'd have to say take this local variable info and perhaps rename it to strength info.

01:11.590 --> 01:19.390
So we'd call it strength info and instead of info we're using strength info to set the attribute value

01:19.390 --> 01:24.130
to the actual strength value and then broadcast strength info.

01:24.130 --> 01:35.650
And then we have to copy these lines and paste them below and call this next one intelligence info and

01:35.650 --> 01:43.330
we'd have to find attribute info for the tag and we'd have to change this tag to attributes, primary

01:43.330 --> 01:51.100
intelligence, and then we'd have to take intelligence info and set its attribute value to the value

01:51.100 --> 01:51.880
of intelligence.

01:51.880 --> 01:59.530
So we'd take our attribute set and call it intelligence, and then we'd take our attribute info, delegate

01:59.530 --> 02:06.190
and broadcast intelligence info, and then we'd have to copy this again and do it again for resilience

02:06.190 --> 02:06.970
and vigor.

02:06.970 --> 02:09.730
And then into our secondary attributes.

02:09.730 --> 02:12.940
We'd start with armor, armor penetration and so on.

02:12.940 --> 02:18.550
As you can see, this becomes a very lengthy and tedious and repetitive process.

02:18.580 --> 02:20.860
Now that's okay.

02:20.860 --> 02:28.480
It would work, but it'd be a lot nicer if we didn't have to do that, because every time we add a new

02:28.480 --> 02:35.110
attribute to our attribute set, not only do we have all the boilerplate code to add in the attribute

02:35.110 --> 02:42.130
set class, but then we'd have a bunch of boilerplate stuff to add to our widget controller and that

02:42.130 --> 02:42.790
sucks.

02:42.790 --> 02:44.500
We don't want to have to do that.

02:44.530 --> 02:51.640
Instead, I'd like to just have some kind of generic algorithm right here that doesn't care about what

02:51.640 --> 02:53.440
the attributes are or how many.

02:53.470 --> 02:57.940
There are, however many attributes there are in the attributes set.

02:57.940 --> 02:59.920
I want them all broadcast.

02:59.920 --> 03:07.030
I want to get all of their attribute info from the attribute info data asset, no matter how many or

03:07.030 --> 03:13.210
what they are and just broadcast them all and let the widgets receive them and sort out the information

03:13.210 --> 03:13.990
from there.

03:13.990 --> 03:15.670
So how do we do this?

03:15.700 --> 03:21.310
Well, there are a number of ways, and we're going to start with the easiest way, or perhaps, in

03:21.310 --> 03:26.800
my opinion, the easiest way, and then we're going to refine it and make it more sophisticated.

03:27.130 --> 03:29.290
But you have to start somewhere.

03:29.500 --> 03:34.000
And so where we're going to start is I'd like to make a map.

03:34.030 --> 03:42.550
I'd like to make a map data structure, mapping gameplay tags to something, right?

03:42.550 --> 03:45.100
Something like gameplay attributes.

03:45.100 --> 03:52.180
But the thing is, our attribute set has the attribute accessors to get those gameplay attributes.

03:52.180 --> 03:56.050
Here's our attribute set, here's the attribute accessors.

03:56.050 --> 04:00.610
And thanks to that we have a getter for the gameplay attribute.

04:00.640 --> 04:06.880
Now I don't want to map tags to the gameplay attribute data, I don't want to map tags to the float

04:06.880 --> 04:07.600
value.

04:07.600 --> 04:10.270
That is the attributes numerical value.

04:10.270 --> 04:12.580
I want the gameplay attribute.

04:12.610 --> 04:12.970
Why?

04:13.000 --> 04:20.950
Because there are delegates broadcast when attributes change and those broadcast gameplay attributes.

04:20.950 --> 04:24.880
And I want a map that maps tags to those attributes.

04:24.880 --> 04:31.390
But the only way to get those attributes is through the getters like taking our attribute set and calling

04:31.390 --> 04:33.460
get strength attribute.

04:33.460 --> 04:37.240
And this returns an gameplay attribute.

04:37.270 --> 04:43.300
Now this is a static function and whenever I want my attribute, I want to get it through this static

04:43.300 --> 04:44.080
function.

04:44.080 --> 04:52.060
And if I'd like a map that maps gameplay tags to say this static function, I could use a delegate.

04:53.050 --> 05:00.070
So for example, I can go into aura, attribute set and scroll all the way up to the top and I can declare

05:00.070 --> 05:06.640
a delegate here that's capable of having a function bound to it, such as my.

05:06.720 --> 05:11.730
Attribute accessors those static functions that return gameplay attributes.

05:11.940 --> 05:18.990
Now, if we're going to bind one of those, we have to use a delegate that can bind a function that

05:18.990 --> 05:20.400
returns a value.

05:20.430 --> 05:23.600
For example, we can declare a delegate.

05:23.610 --> 05:27.270
It doesn't need to be dynamic, it doesn't need to be multicast.

05:27.270 --> 05:29.360
It can just be a delegate.

05:29.370 --> 05:33.270
And we have the option for ret val or return value.

05:33.480 --> 05:39.780
If we declare a delegate that has a return value, we put the type of the return value first.

05:39.810 --> 05:45.800
Now I want to be able to bind a function to this that can return an gameplay attribute.

05:45.810 --> 05:56.070
In other words, the return type is F gameplay attribute and I'm going to call this delegate F attribute

05:56.070 --> 05:57.240
signature.

05:58.380 --> 06:05.790
So this delegate type is a delegate that can have a function bound to it, which takes zero arguments

06:05.790 --> 06:09.810
but returns a value of type gameplay attribute.

06:09.960 --> 06:18.840
So then what we can do is we can have a map that maps gameplay tags to these delegates and then we should

06:18.840 --> 06:21.710
be able to always access that map.

06:21.720 --> 06:29.070
And using a gameplay tag, we can find the static function that returns the gameplay attribute.

06:29.400 --> 06:31.410
Okay, so let's make that map.

06:31.440 --> 06:33.590
So we'll make this map here.

06:33.600 --> 06:36.540
I'm going to put it just above all of our attributes.

06:36.930 --> 06:46.890
This will be a t map mapping from gameplay tag to our delegate type we just declared, which is F attribute

06:46.890 --> 06:47.850
signature.

06:47.850 --> 06:53.910
So we'll go ahead and make that map and we'll call this tags two attributes.

06:54.930 --> 07:04.540
Now all that we need to do is just add a key value pair to map gameplay tags to each of our attributes

07:04.540 --> 07:08.140
and we can do that in the attribute set constructor.

07:08.320 --> 07:16.390
So I'm going to open the attribute set constructor here and we're going to add to our tags two attributes

07:16.390 --> 07:17.150
map.

07:17.170 --> 07:24.100
So we're going to call Add and we'll start by adding a couple of our attributes using their tags.

07:24.130 --> 07:30.370
Now to get our attribute tags, we have to include the header file where those are defined and that's

07:30.370 --> 07:34.150
in our gameplay tags.

07:34.180 --> 07:35.260
Dot h.

07:35.290 --> 07:38.680
So if we include that, we can access them.

07:38.680 --> 07:44.500
And I'd like to just make an F or a gameplay tags local variable.

07:44.980 --> 07:55.150
I'll make it a const for gameplay tags reference called gameplay tags and call f or a gameplay tags

07:55.240 --> 07:56.290
get.

07:57.100 --> 08:05.170
And now we can access those gameplay tags and I'm going to add two tags to attributes gameplay tags

08:05.170 --> 08:08.680
dot and we can use attributes.

08:08.680 --> 08:10.300
Primary strength.

08:10.390 --> 08:18.550
Now Tags two attributes takes a gameplay tag for the key and a delegate for the value, and that delegate

08:18.580 --> 08:22.480
has the type F attribute signature.

08:22.690 --> 08:29.410
So we'll call this strength delegate and we can pass that in strength delegate.

08:29.620 --> 08:36.460
Now we haven't bound anything to strength delegate, but it's capable of having a function bound to

08:36.460 --> 08:40.210
it that can return a gameplay attribute struct.

08:40.240 --> 08:47.050
Now our accessors are static, so if we're going to bind one of those static functions to strength delegate,

08:47.080 --> 08:52.930
we have to take strength delegate and we have to use bind static.

08:53.290 --> 08:59.200
This is one of the bind functions for delegates, specifically for static functions and it can take

08:59.200 --> 09:09.730
a static function as an input so we can pass in you or a attribute set, get strength attribute just

09:09.730 --> 09:10.540
like that.

09:10.840 --> 09:13.540
So bind static can take that static function.

09:13.540 --> 09:20.200
Now we're in or attribute set so we don't have to fully qualify the name writers graying it out telling

09:20.200 --> 09:25.240
me that that's redundant we could just pass in get strength attribute that's totally fine.

09:25.840 --> 09:34.870
And now our map has a key value pair the attributes Primary strength gameplay tag is the key and the

09:34.870 --> 09:40.750
value is this delegate with get strength attribute bound to it.

09:40.840 --> 09:47.110
So yes, this adds another boilerplate step to the attribute set, but the attribute set already has

09:47.110 --> 09:54.070
boilerplate steps, so one more is okay, but this removes the necessity to have boilerplate in the

09:54.070 --> 09:55.200
widget controller.

09:55.210 --> 09:56.110
Here's why.

09:56.140 --> 09:59.800
Let's say we have all of our attributes in this map.

09:59.800 --> 10:04.300
I'm not going to add them all right now I'm just going to add the next one for intelligence.

10:04.300 --> 10:09.370
So I'll make an attribute signature called intelligence delegate.

10:09.400 --> 10:17.200
I'll take intelligence, delegate and use bind, static and bind, get intelligence attribute to it.

10:17.230 --> 10:25.870
Then in our tags to Attributes map, I'm going to add gameplay tags, attributes, primary intelligence.

10:25.870 --> 10:30.340
And for the value, it's going to be the intelligence delegate.

10:30.370 --> 10:39.370
Now our map has two key value pairs mapping gameplay tags to the delegate with the attribute accessor

10:39.370 --> 10:40.510
bound to it.

10:40.600 --> 10:42.820
So I just have these two attributes.

10:42.820 --> 10:45.100
Just imagine that I bound all of them.

10:45.100 --> 10:45.700
Okay?

10:45.700 --> 10:52.390
And now it's going to be easier in our widget controller because we don't have to do this manually for

10:52.390 --> 10:54.010
each attribute anymore.

10:54.040 --> 10:57.940
Now all we need to do is loop through that map.

10:58.030 --> 10:58.900
So here's what we can do.

10:58.900 --> 11:07.030
We can make a for loop and I can say auto reference pair and we're going to loop through the attribute

11:07.030 --> 11:11.890
set tags, two attributes map and looping through this tags.

11:11.890 --> 11:13.270
Two attributes map.

11:13.270 --> 11:14.500
What do we want to do?

11:14.500 --> 11:20.470
Well, we want to get the attribute info so we can take our attribute info.

11:23.120 --> 11:26.030
And call find attribute info for tag.

11:26.030 --> 11:32.930
And the tag is something we have in this loop because we have the pear and the pear maps gameplay tags

11:32.930 --> 11:33.980
to attributes.

11:33.980 --> 11:42.170
So for the attribute we pass in our pear key that's the gameplay attribute and find attribute info for

11:42.170 --> 11:45.590
tag will return that for attribute info.

11:45.590 --> 11:54.590
So we'll make a non-const f or attribute info called info and initialize it with this.

11:54.920 --> 11:57.920
But we also need the actual attribute value.

11:57.920 --> 12:02.660
So we need to take info dot attribute value and we need to set that.

12:02.690 --> 12:12.740
Well, we have access to the attribute itself because our pear dot value is a delegate and our delegate

12:12.770 --> 12:14.660
has a function bound to it.

12:14.660 --> 12:18.590
Each of these has the attribute accessor bound to it.

12:18.620 --> 12:22.490
Now because it's a delegate, we need to execute that delegate.

12:22.490 --> 12:24.900
So we call dot execute on it.

12:24.930 --> 12:31.800
That is going to result in returning a gameplay attribute because we're executing the function bound

12:31.800 --> 12:32.520
to it.

12:32.550 --> 12:39.300
In other words, we could create a local gameplay attribute called attribute.

12:39.300 --> 12:47.520
I'm just going to call it Attr and initialize it with this expression that works because executing this

12:47.520 --> 12:52.680
delegate will execute that attribute accessor which returns a gameplay attribute.

12:52.680 --> 13:00.300
But attribute value is a float, so we need the actual value of that attribute.

13:00.330 --> 13:02.640
Well, we can get that from any attribute.

13:02.670 --> 13:06.390
What we do is we take that attribute just to make this clear.

13:06.390 --> 13:07.560
I have the attribute here.

13:07.560 --> 13:12.300
It's called Attr and we call get numeric value.

13:12.330 --> 13:15.780
Now get numeric value requires an attribute set.

13:15.810 --> 13:16.260
Why?

13:16.290 --> 13:21.540
Because the gameplay attribute itself is returned from a static function.

13:21.540 --> 13:26.190
So we need to know which attribute set that this attribute is associated with.

13:26.190 --> 13:30.750
Well, we have the attribute set its A's right, so we can pass that in.

13:30.750 --> 13:34.710
Now attribute here doesn't have to be a local variable.

13:34.710 --> 13:41.790
We could simply take this expression here and replace attribute here with it and remove that line.

13:41.790 --> 13:43.020
That's fine as well.

13:43.200 --> 13:51.240
But now we have our attribute info and we've set its attribute value and we can broadcast it so we can

13:51.240 --> 13:58.110
take our attribute info delegate that we were broadcasting before and we can broadcast the attribute

13:58.110 --> 13:58.770
info.

13:59.100 --> 14:06.750
So now broadcast initial values doesn't have to know how many attributes there are, doesn't have to

14:06.750 --> 14:08.940
know what attributes there are.

14:09.030 --> 14:14.850
It doesn't really matter as long as we can loop through this map and as long as all of the attributes

14:14.850 --> 14:16.890
we care about are in this map.

14:16.890 --> 14:24.630
Now, so far, this map just contains two pairs mapping the gameplay tag to the delegate that has the

14:24.630 --> 14:28.560
attribute accessor bound to it strength and intelligence.

14:28.560 --> 14:30.990
So we can see if this works.

14:30.990 --> 14:34.080
By running this, I'm going to go ahead and hit the debug button.

14:36.240 --> 14:37.760
And now back in the editor.

14:37.770 --> 14:41.990
I'm just simply going to press play and open the attribute menu.

14:42.000 --> 14:43.530
And now check this out.

14:43.560 --> 14:47.880
We now have strength and we have intelligence with their values.

14:47.910 --> 14:53.790
Everything else is blank, but at least we have those two because we added those to our map and we're

14:53.790 --> 14:57.120
now using the map to drive these values.

14:57.330 --> 15:05.010
So this is a great step in the right direction because the attribute menu widget controller now is pretty

15:05.010 --> 15:06.930
automated and generic.

15:06.930 --> 15:15.120
And as long as we add the correct attribute tags and use our delegate, we can fill in that map and

15:15.120 --> 15:16.590
the attribute set here.

15:16.590 --> 15:19.290
But this is actually a little clunky.

15:19.320 --> 15:25.440
You see, if we have a new attribute that we want in this map, we have three whole lines of code here.

15:25.440 --> 15:26.820
We have to make a delegate.

15:26.850 --> 15:29.070
We have to bind a static function to it.

15:29.070 --> 15:32.060
And then we have to add that to the map.

15:32.070 --> 15:39.190
And because this is a gameplay tag mapped to a delegate, it then over here in the widget controller,

15:39.220 --> 15:45.710
we have to loop through the map and call execute on each of these values.

15:45.730 --> 15:50.260
This just makes it all a little bit clunky and sort of inconvenient.

15:50.530 --> 15:53.810
Now what if we didn't have to use a delegate at all?

15:53.830 --> 16:00.410
If we go back into our attribute set constructor, what are we doing here when we call bind static?

16:00.430 --> 16:04.210
Well, we're passing in a function and bind.

16:04.210 --> 16:08.080
Static takes in an input of this type.

16:08.080 --> 16:16.780
Here it says type name T base, static delegate instance and a whole bunch of really kind of horrendous

16:16.780 --> 16:18.430
template input parameters.

16:18.430 --> 16:19.840
It looks a little bit messy, right?

16:19.840 --> 16:21.190
It looks a little crazy.

16:21.190 --> 16:28.300
But really, though, this type that binds static takes is something that we could use as our type for

16:28.300 --> 16:29.410
our map.

16:29.410 --> 16:37.090
Really, if we could just somehow wrap our minds around this template based static delegate instance.

16:37.290 --> 16:44.370
Well, let's go into Aura attribute set for a second and let's just see what happens if we get rid of

16:44.400 --> 16:46.560
that delegate altogether.

16:46.560 --> 16:54.210
And instead of mapping a gameplay tag to a delegate, what if we map a gameplay tag to that type that

16:54.210 --> 16:55.740
binds static takes?

16:55.740 --> 16:59.030
Really this t based static delegate instance?

16:59.040 --> 17:05.460
If we look at the comment there, it says binds a raw C plus plus pointer global function delegate.

17:05.490 --> 17:09.900
These delegates are really just types that can store function pointers.

17:09.900 --> 17:17.640
And in fact, if I just click on this here and right, or at least I can just copy this t static delegate

17:17.640 --> 17:20.940
instance, I'm going to copy it like this.

17:21.120 --> 17:23.130
T Tbase static delegate instance.

17:23.130 --> 17:29.310
I'm going to copy that name, come back into aura attribute set and just paste it right here and I'm

17:29.310 --> 17:34.920
going to right click on it and go to declaration or usages so I can just see it here.

17:35.250 --> 17:37.200
So what's going on here?

17:37.230 --> 17:41.520
Well, we see that it's defined as a class and it's a template.

17:42.080 --> 17:45.200
And we have a declaration right here with a comment.

17:45.200 --> 17:49.520
It says implements a delegate binding for regular cplusplus functions.

17:49.520 --> 17:57.560
And we see where the classes defined here that this is a template class with a template type parameter

17:57.560 --> 17:58.970
called RET val type.

17:59.000 --> 18:01.670
To me that sounds like return value type.

18:01.670 --> 18:06.710
And then we have type name with triple dots called param types.

18:06.710 --> 18:10.940
Now the triple dots is known as a variadic template type parameter.

18:10.970 --> 18:17.930
Variadic meaning there's a variable number of arguments you can pass in any number of arguments.

18:17.930 --> 18:20.960
The number of arguments is variable.

18:20.990 --> 18:22.880
Then we have a user policy.

18:22.910 --> 18:24.590
We have no idea what that is.

18:24.590 --> 18:25.130
Right.

18:25.130 --> 18:30.710
And then another variadic template input parameter of type var types.

18:30.710 --> 18:39.140
Now here in the angle brackets, we see that we have ret val type parentheses, param types, dot dot

18:39.140 --> 18:39.620
dot.

18:39.650 --> 18:47.580
Now this is in the form of a function signature, specifically a function pointer return value type

18:47.580 --> 18:50.010
parentheses parameter types.

18:50.010 --> 18:51.240
And look at this.

18:51.240 --> 19:00.150
We have this using which is basically saying let's create this func pointer alias and set it equal to

19:00.150 --> 19:03.060
this type here because this type here is so hideous.

19:03.090 --> 19:07.110
We want to use a func pointer as a sort of alias for it.

19:07.140 --> 19:10.410
Using like this is a lot like typedef.

19:10.410 --> 19:13.020
It's creating this f func pointer alias.

19:13.020 --> 19:19.290
And this right here is a function pointer with the return type of ret val type.

19:19.290 --> 19:26.850
That's a template input and an input parameter list that's variable depends on what you pass into the

19:26.850 --> 19:30.180
template when you create a T based static delegate instance.

19:30.180 --> 19:33.420
So all of this I know is a lot to take in.

19:33.450 --> 19:37.170
It doesn't matter really how well we understand this.

19:37.170 --> 19:43.820
As long as we understand that we can create a T based static delegate instance specifying a function

19:43.820 --> 19:44.420
type.

19:44.420 --> 19:50.660
In other words, specifying what the function's return type should be, what its input parameter types

19:50.660 --> 19:51.620
should be.

19:51.620 --> 19:54.890
And of course we need to specify a user policy.

19:55.220 --> 20:01.970
So how do we create one of these that's capable of receiving a function or a function pointer of the

20:01.970 --> 20:08.270
type we're interested in, one that takes in no inputs and returns an gameplay attribute?

20:08.300 --> 20:09.680
Well, here's what we're going to do.

20:09.710 --> 20:12.230
We're going to make a T based static delegate instance.

20:12.230 --> 20:15.170
We're going to open up these template angle brackets.

20:15.170 --> 20:17.780
And look, the first one is the function type.

20:17.930 --> 20:26.210
Well, the function type that we're interested in is a function that returns an F gameplay attribute

20:26.210 --> 20:28.400
and takes zero input parameters.

20:28.400 --> 20:31.820
So this is the type for a function with that signature.

20:31.820 --> 20:34.040
Now we need a user policy though.

20:34.070 --> 20:35.240
What's that?

20:35.360 --> 20:43.550
Well, this is referring to a delegate user policy and there just happens to exist in F default delegate

20:43.650 --> 20:47.570
set user policy and we can use that.

20:47.600 --> 20:50.870
We can even check out its usages.

20:50.870 --> 20:58.280
And here in delegate base dot h, here's that struct and there are some comments talking about how when

20:58.280 --> 21:03.230
you extend delegates you should implement a policy struct like this and pass it as the second template

21:03.230 --> 21:10.550
argument to t delegate and t multicast delegate this policy struct containing three classes called and

21:10.550 --> 21:16.280
we have some info here and for our purposes we can just use the default policy.

21:16.400 --> 21:27.320
Now what we really want here is a function pointer t base static delegate instance has a public function

21:27.320 --> 21:27.920
pointer.

21:27.920 --> 21:36.710
Basically this is a member that we can use that assembles those template inputs into a function pointer

21:36.710 --> 21:38.270
with the correct signature.

21:38.270 --> 21:44.190
So what we can do is use the double colons and use F func pointer.

21:44.190 --> 21:50.940
And now we have this type which is a function pointer that can take zero input parameters and return

21:50.940 --> 21:52.920
an gameplay attribute.

21:52.920 --> 21:56.580
So we could call this function pointer if we wanted to.

21:57.030 --> 22:03.280
Now C plus plus function pointers have pretty awful syntax as we've seen.

22:03.300 --> 22:10.680
This function pointer is a type alias for this, which is the syntax for a function pointer, for a

22:10.680 --> 22:17.400
function that returns the retval type and takes in the arguments that we specify.

22:17.430 --> 22:25.200
So now that we have this function pointer, we have the ability to bind a function to it that returns

22:25.200 --> 22:27.750
an gameplay attribute and takes zero inputs.

22:27.780 --> 22:36.720
We can prove that by going into our attribute set CP and taking our function pointer.

22:36.750 --> 22:43.380
We called it function pointer and setting it equal to get intelligence attribute for example.

22:45.370 --> 22:52.480
So this is essentially a variable that can hold a function with this signature kind of crazy, right?

22:53.200 --> 23:02.380
And if you were to take function pointer and call it with parentheses, well, it would return an gameplay

23:02.380 --> 23:03.220
attribute.

23:08.830 --> 23:10.030
Like this.

23:10.620 --> 23:12.000
So that's kind of cool.

23:12.360 --> 23:14.030
What are the implications of this?

23:14.040 --> 23:18.660
Well, we can now get away with not having to bind a delegate anymore.

23:18.840 --> 23:20.880
We can simply use this type.

23:20.880 --> 23:28.200
So if I take this type here and replace F attribute signature now tags to attributes maps, gameplay

23:28.200 --> 23:33.810
tags to well function pointers with this specific signature.

23:33.810 --> 23:40.080
So I can go back to or attribute set and not even bother with the delegate.

23:40.080 --> 23:48.060
Instead we can simply add the gameplay tag and the static function directly get strength attribute.

23:48.090 --> 23:54.630
We don't need the parentheses there, it's just get strength attribute without the parentheses passing

23:54.630 --> 23:59.360
in the function name returns that function's address its pointer.

23:59.370 --> 24:02.190
So same with our intelligence.

24:02.190 --> 24:03.690
We don't need a delegate.

24:03.720 --> 24:10.350
We can just pass in get intelligence attribute without calling it just the function pointer.

24:10.350 --> 24:17.070
Now this is a lot more compact and we can also get rid of that delegate altogether and aura attribute

24:17.070 --> 24:17.370
set.

24:17.400 --> 24:21.210
We can scroll up to the top and completely nuke that one.

24:21.360 --> 24:23.400
No need for that anymore.

24:24.060 --> 24:28.830
But now our tags two attributes map has an awful type.

24:28.830 --> 24:29.460
Right?

24:29.460 --> 24:34.740
This looks pretty bad and function pointer was just for demonstration purposes.

24:34.740 --> 24:36.060
I'm going to get rid of that.

24:36.210 --> 24:42.750
So now the map type is a little scary, but now it's just tags two function pointers.

24:42.750 --> 24:49.830
And if we go back to attribute menu widget controller, notice that our key value pair is no longer

24:49.830 --> 24:56.310
mapping gameplay tags to delegates, but it's mapping gameplay tags to pointers.

24:56.310 --> 25:01.080
And this is the type for a function pointer of this particular signature.

25:01.080 --> 25:07.800
So now when we want to get the attribute value, we just have to take the value which is a function

25:07.800 --> 25:08.730
pointer.

25:08.730 --> 25:14.080
In other words, pair value to this is a function pointer, right?

25:14.080 --> 25:20.290
And to call the function using the function pointer, we use simply parentheses like a regular function

25:20.290 --> 25:20.740
call.

25:20.740 --> 25:29.110
And this will give us that attribute from which we can call get numeric value passing in the attribute

25:29.110 --> 25:29.770
set.

25:29.950 --> 25:36.640
So now we no longer need that execute call that we had to tack on because before it was a delegate.

25:36.670 --> 25:40.800
Now it's just a function pointer we can call it, and get that value.

25:40.810 --> 25:43.660
So it's a little bit cleaner, a little bit nicer.

25:43.660 --> 25:48.910
Why don't we just check to make sure that this can run and that we get our strength and intelligence

25:48.910 --> 25:49.780
values?

25:51.690 --> 25:53.190
Okay, so pressing play.

25:53.190 --> 25:54.960
Opening the attribute menu.

25:54.990 --> 25:58.710
We have strength and intelligence and it looks great.

25:59.680 --> 26:00.100
Okay.

26:00.100 --> 26:03.490
So we're a step in the right direction, I think.

26:03.640 --> 26:08.230
Notice that auto deduces the pair to this type.

26:08.230 --> 26:16.780
So it's reducing that big scary type down to this, just a simple function pointer.

26:16.810 --> 26:18.910
So that's kind of interesting, right?

26:18.940 --> 26:24.160
What if we took that and replaced this big scary type with just that?

26:24.940 --> 26:28.330
If we go ahead and just run in debug mode like this.

26:29.790 --> 26:33.060
And we go ahead and press play and open up that menu.

26:33.090 --> 26:34.320
Well, same thing.

26:34.320 --> 26:35.190
It works.

26:35.190 --> 26:36.660
It works just fine.

26:36.660 --> 26:43.380
So really, we can get away with not using that big scary type and we can just use the raw cplusplus

26:43.380 --> 26:46.110
syntax for a function pointer.

26:46.110 --> 26:51.240
So this might have been a learning experience for you, but you didn't know that you could map gameplay

26:51.240 --> 26:53.010
tags to function pointers.

26:53.280 --> 26:54.660
Kind of cool.

26:55.450 --> 27:02.290
Now, oftentimes function pointer syntax is usually pretty nasty.

27:02.290 --> 27:06.160
I mean, nobody likes to look at function pointer syntax.

27:06.160 --> 27:09.700
It's usually something that we hide away.

27:09.700 --> 27:16.630
It's like that ugly decoration in your home that you stick in the storage under your stairs.

27:16.630 --> 27:18.430
It's just something you don't want to look at.

27:18.430 --> 27:24.820
And there are ways to hide this away in favor of something a little bit more attractive.

27:25.150 --> 27:29.500
So one way to do that is creating an alias.

27:30.140 --> 27:35.690
So we'd like to hide this, but I'm going to control Z back to where we had it here when it was our

27:35.900 --> 27:37.880
base static delegate instance.

27:37.880 --> 27:44.590
And this is the type that I'd like to sweep under the rug and hide in favor of a more attractive identifier.

27:44.600 --> 27:47.960
So here's what I'm going to do just above the class.

27:47.960 --> 27:50.480
Outside of it, we're not in the class anymore.

27:50.510 --> 27:52.310
I'm going to make a typedef.

27:52.700 --> 27:55.910
So typedef creates a type definition.

27:55.910 --> 28:03.260
It says take some type that perhaps you feel is extremely ugly and painful to look at like this one

28:03.260 --> 28:05.840
and then give me an alias that I can use instead.

28:05.840 --> 28:10.460
So attribute func Pointer, for example.

28:11.070 --> 28:17.480
And this now attribute funk pointer is equivalent to this big monster right here.

28:17.490 --> 28:22.440
So I can take Attribute Funk pointer and I can replace this with it.

28:22.440 --> 28:22.790
Now.

28:22.800 --> 28:29.880
Unreal Coding standards say that a typedef should have the prefix of the thing that it represents.

28:29.910 --> 28:37.410
Now it represents this t based static delegate instance, which is a template type, but it's not really

28:37.410 --> 28:38.670
a template type anymore.

28:38.670 --> 28:42.180
We're specifying that type to be gameplay attribute.

28:42.180 --> 28:46.110
So we're going to use an F as the prefix rather than a t.

28:46.140 --> 28:50.340
A T would imply that this attribute funk pointer is a template, but it's not.

28:50.340 --> 28:53.280
It's specific to this signature here.

28:53.820 --> 29:00.660
So now that we have F attribute func pointer, we can replace this big scary type here in the map with

29:00.660 --> 29:02.970
f attribute func pointer instead.

29:02.970 --> 29:08.730
And now this looks a little bit easier to deal with and we can just throw that typedef up there and

29:08.730 --> 29:09.720
forget about it.

29:09.750 --> 29:14.490
Now that's one way, but T based static delegate instance is a template.

29:14.490 --> 29:17.580
And what if we wanted this to be more generic?

29:17.610 --> 29:21.190
Because right now it's pretty specific to functions with this signature?

29:21.210 --> 29:29.040
Well, we can't make this alias a template, but modern cplusplus has ways to achieve that.

29:29.340 --> 29:32.550
Here's how we can achieve an alias that is a template.

29:32.550 --> 29:41.550
First of all, we say template and in angle brackets we say class t t is the template type parameter

29:41.550 --> 29:44.320
and we use the using keyword.

29:44.340 --> 29:49.800
So we say using and we create our alias name.

29:49.800 --> 29:51.930
It's going to be a true template now.

29:51.930 --> 30:01.410
So we're going to call this T, we'll call it T attribute func pointer and we set it equal to and then

30:01.410 --> 30:03.690
we use that big hideous type.

30:04.170 --> 30:05.700
So I'm going to paste it there.

30:05.700 --> 30:14.910
But the difference here is that this the function type, it's now our template type and there's a semicolon

30:14.910 --> 30:15.270
there.

30:15.270 --> 30:20.760
But if this is a template type alias, we have to use type name here.

30:20.760 --> 30:23.520
That's just the syntax for Cplusplus.

30:23.580 --> 30:31.230
So now this t attribute func pointer is an alias for this big ugly type only.

30:31.230 --> 30:39.420
It's a template, so it's essentially a function pointer capable of storing the address of a function

30:39.450 --> 30:41.760
of any function signature we choose.

30:41.760 --> 30:49.350
So now our map can map from gameplay tags to t attribute func pointers, but being a template, we have

30:49.350 --> 30:54.900
to specify the template type and the template type is the whole function signature for.

30:54.910 --> 30:59.830
So that's going to be F gameplay attribute with parentheses.

30:59.830 --> 31:05.560
This is a function that returns an F gameplay attribute and takes zero inputs.

31:05.680 --> 31:10.870
And now our t attribute func pointer is actually more versatile.

31:10.870 --> 31:16.180
In fact, it doesn't really have anything to do with attributes, it just takes static functions.

31:16.180 --> 31:24.790
So this could be renamed to t static func pointer and if we rename it there we have to rename it here.

31:24.790 --> 31:32.050
So anyway, that's just some modern cplusplus that we can use if we want our type alias to be a true

31:32.050 --> 31:38.440
template and then we can use it to create a function pointer that can point to any type of function.

31:38.440 --> 31:43.750
We just specify it here and the video is getting a little long, so we're going to wrap it up pretty

31:43.750 --> 31:44.170
quickly.

31:44.170 --> 31:50.530
But just to show you what I mean, we could make a T static function pointer to point to any function

31:50.530 --> 31:51.220
signature.

31:51.220 --> 32:01.110
So say we want it to point to a function that returns a float and takes in an int 32 as well as a float

32:01.110 --> 32:02.910
and another N 32.

32:02.910 --> 32:04.590
And we call this.

32:05.250 --> 32:07.680
Random function pointer.

32:08.010 --> 32:14.160
Well, this function pointer is capable of storing the address of a function with this signature, which

32:14.160 --> 32:15.380
would look like this.

32:15.390 --> 32:17.330
It would have a float return type.

32:17.340 --> 32:21.750
We'll call this random function and it'll take an int 32.

32:21.780 --> 32:31.050
We can even say int 32 I it can take a float, we'll call it F and an int 32 called I to.

32:32.440 --> 32:38.020
Just as an example, and I'll just provide the function definition here.

32:38.020 --> 32:46.030
Just return zero dot f and this can be a static function and random function pointer is capable of storing

32:46.030 --> 32:46.990
its address.

32:46.990 --> 32:55.450
So just really quick, I'm going to go into the attribute set constructor and say random function pointer

32:55.480 --> 33:06.400
equals random function and we can even call it, we can say float f equals and we can take that random

33:06.400 --> 33:13.090
function pointer and call it and it requires an int, a float and an int.

33:13.870 --> 33:14.950
Isn't that crazy?

33:15.220 --> 33:25.330
So it's just an example showing that our type alias is more versatile than just being able to store

33:25.330 --> 33:30.880
a function with a specific signature, which is what our delegate initially was capable of.

33:30.880 --> 33:37.550
It could only bind a function with a specific signature, but now we have this t static function pointer

33:37.550 --> 33:41.600
that can bind to any function.

33:41.960 --> 33:43.130
So pretty cool.

33:43.560 --> 33:49.960
So now that our tags to attributes map looks like this and we have this type alias.

33:49.980 --> 33:53.370
I'll go ahead and leave this comment here for now and say.

33:54.450 --> 34:01.320
TypeDef is specific to the game play attribute.

34:04.240 --> 34:14.950
Signature, but t static func pointer is generic to any signature chosen.

34:15.780 --> 34:16.500
Okay.

34:16.500 --> 34:23.910
So the last thing to do is just to make sure this is working and just to show that this is a great deal

34:23.940 --> 34:25.590
better and easier to set up.

34:25.590 --> 34:34.890
We can take our tags to attributes, map and add and let's add gameplay tags, dot resilience.

34:34.890 --> 34:41.820
We'll get that tag and we'll use get resilience attribute without calling the function.

34:41.820 --> 34:45.600
We're passing in the pointer again and we'll do vigor as well.

34:45.600 --> 34:55.470
So take tags two attributes dot add adding its gameplay tag so there's vigor and we'll use get vigor

34:55.620 --> 34:56.700
attribute.

34:58.510 --> 35:05.320
Now this adds it to the map, and that's all we have to do because our widget controller simply loops

35:05.320 --> 35:06.310
through the map.

35:06.310 --> 35:08.800
So let's go ahead and test this out.

35:10.400 --> 35:13.010
And pressing play and opening our menu.

35:13.040 --> 35:18.790
We have strength, intelligence, resilience and vigor with all the correct values.

35:18.800 --> 35:25.130
So now we've set ourselves up to be able to expand this system a lot easier.

35:25.130 --> 35:29.900
When we add new attributes, we don't have to touch the code in the widget controller.

35:29.900 --> 35:33.830
We touch the attribute set, which is where the attributes are defined.

35:33.860 --> 35:40.070
This is the only place that we should have to do anything different when we add a new type of attribute.

35:40.070 --> 35:47.690
And all we need to do now is the added step of adding that attribute to our map, which maps gameplay

35:47.690 --> 35:54.490
tags to attribute accessors, specifically the static functions that return those gameplay attributes.

35:54.500 --> 35:59.420
We can pass those functions straight in as pointers to our map.

35:59.540 --> 36:01.220
Pretty sophisticated.

36:01.400 --> 36:08.420
So as just a quick recap, this is possible because we've created a static function pointer to static

36:08.420 --> 36:19.440
func pointer which is a template and it's a type alias for this big scary expression where T is the

36:19.440 --> 36:27.330
function signature and we have a map mapping gameplay tags to t static func pointers specifying the

36:27.330 --> 36:34.500
type that function signature to be a function that returns an gameplay attribute and takes zero input

36:34.500 --> 36:35.460
parameters.

36:35.490 --> 36:42.990
We call this tags two attributes and in our attribute set constructor we're creating that association

36:42.990 --> 36:51.900
between gameplay tags and the attribute accessors so that way we can always access the gameplay attribute

36:51.900 --> 36:54.210
by simply using the tags.

36:54.210 --> 37:00.810
Two attributes map and we're doing that in the widget controller by simply looping through this map,

37:00.810 --> 37:05.250
finding the attribute info for that particular gameplay tag.

37:05.250 --> 37:07.350
That's the key in the pairs.

37:07.680 --> 37:13.830
And then we're using the value that function pointer, calling it to get the attribute and from the

37:13.830 --> 37:20.400
attribute getting its numerical value as that's a function on gameplay attributes that requires us to

37:20.400 --> 37:22.020
pass in the attribute set.

37:22.050 --> 37:29.400
We set that on our attribute info and broadcast it up so our widgets can receive it and show that info.

37:29.550 --> 37:34.590
So this is all really nice, but this is only broadcasting initial values.

37:34.590 --> 37:36.840
What about when the values change?

37:37.020 --> 37:42.180
Well, that's something we're going to have to take care of and we'll take care of that next.

37:42.360 --> 37:44.460
So thank you for sticking with me.

37:44.460 --> 37:48.690
I know this was a lengthy lecture, but there was a lot to learn about here.

37:48.690 --> 37:55.290
And now we have some very useful and a bit more advanced cplusplus skills in our tool belt that we can

37:55.290 --> 37:56.030
use.

37:56.040 --> 37:58.710
So excellent job and I'll see you soon.
