WEBVTT

00:00.180 --> 00:00.950
Hello again!

00:00.960 --> 00:07.080
In this video, we are going to look at how the entity manager performs operations on entity objects

00:07.080 --> 00:07.950
in our game.

00:10.220 --> 00:14.840
Most of these are actually fairly straightforward, so I will go through them when we look at the code.

00:15.650 --> 00:20.240
As an example, get_all(), which will return all the entities of a given type.

00:21.080 --> 00:25.070
This will just look up the type in the grouped_entities map.

00:25.250 --> 00:28.010
So we get the hash code of the entity type.

00:28.490 --> 00:32.000
We find the element in the map, which has that hash code as its key.

00:32.420 --> 00:37.790
And then we return the value of the map, which will be the vector of alias pointers.

00:38.390 --> 00:44.900
So for example, if we call get_all() with brick as the type parameter, this will return a vector of

00:44.900 --> 00:47.090
pointers to all the bricks in the game.

00:47.840 --> 00:50.780
The difficult part is going to be the handle_collision().

00:51.170 --> 00:58.130
We had the ball and the paddle and the vector of bricks as local variables. So we could access them directly.

00:58.730 --> 01:03.050
We no longer have that: the entity objects are hidden away, inside the entity manager.

01:03.170 --> 01:05.120
So we need to do something else.

01:06.880 --> 01:09.010
The obvious way is to write code like this.

01:09.010 --> 01:12.400
So we get all the ball objects, for example.

01:12.640 --> 01:14.050
Then we iterate over them.

01:14.800 --> 01:18.040
Then this will give us a pointer to the base class.

01:18.130 --> 01:20.800
So we convert that to a pointer to a ball.

01:21.580 --> 01:23.290
Then we do the same thing for the bricks.

01:23.680 --> 01:28.780
And then, when we get down here, we have a pointer to one ball and a pointer to one brick. And then

01:28.780 --> 01:33.670
we can call handle_collision(). And then we need to do that again for the ball and the paddle.

01:33.700 --> 01:39.550
And if we add other entities, we need to do that, for every possible combination of entities which can

01:39.550 --> 01:40.510
collide with each other.

01:40.900 --> 01:45.070
And that is going to be an awful lot of code, and it is going to make our game loop look very untidy.

01:46.690 --> 01:51.070
So instead, we are going to write a function which will abstract away most of that functionality.

01:51.640 --> 01:53.920
This will be the apply_all() function.

01:53.950 --> 01:59.680
So we can call this and this will apply a function to all entities which have a given type.

02:00.460 --> 02:06.850
For example, we can call apply_all() with brick as the type parameter. And then we can pass a lambda expression,

02:07.150 --> 02:11.020
which takes a brick as argument and then calls update().

02:11.380 --> 02:15.550
So this will call update(), for every brick in the game.

02:16.750 --> 02:17.500
So, apply_all()

02:17.500 --> 02:18.570
will basically do

02:18.580 --> 02:20.710
what all that code in the game loop did.

02:21.310 --> 02:25.360
So we call get_all(), to get the vector of aliases for that type.

02:26.170 --> 02:30.310
Then we iterate over the vector. That will give us a pointer to the base class.

02:30.310 --> 02:36.130
So we cast that to the dynamic type. And then we call the function, with that element as argument.

02:37.480 --> 02:38.650
So we have code like this.

02:38.650 --> 02:39.820
So this is in the game loop.

02:39.820 --> 02:47.800
We have a entity manager object, then we call its apply_all() member function, with ball as the parameter,

02:48.460 --> 02:54.340
and we give that a callable object. And this callable object will in turn call apply_all() again, with

02:54.340 --> 02:58.600
brick as the parameter and this will call handle collision.

02:58.930 --> 03:05.230
And then we have the ball and the brick as arguments to handle_collision(). The way that the scoping rules

03:05.230 --> 03:07.930
for lambdas work, makes this a bit complicated.

03:08.590 --> 03:12.400
We need to be able to call the manager object inside a lambda expression.

03:12.670 --> 03:14.860
So we need to capture the manager.

03:15.700 --> 03:22.210
And because we are actually inside a member function of the manager, we need to capture "this". And also,

03:22.210 --> 03:27.130
we need to capture the ball. As otherwise, that will not be visible inside here.

03:29.220 --> 03:30.270
And one final point.

03:30.270 --> 03:33.960
When we have templates, we need to think about how we are going to organize the code.

03:34.440 --> 03:38.940
In this case, we have member templates which are going to be called from the game manager, but not

03:38.940 --> 03:41.040
from anywhere else in the code.

03:41.580 --> 03:47.040
So the simplest way to do this will be to write the member template definitions inline in the game

03:47.070 --> 03:51.300
manager header above the definition of the game manager class.

03:51.510 --> 03:56.760
And that way the compiler will always be able to see the member template definitions, when it has to

03:56.760 --> 03:57.750
instantiate them.

03:59.130 --> 04:02.040
So here is our new version of the game header.

04:02.280 --> 04:08.010
We have to add some extra headers for the unique_ptr and runtime type information.

04:09.980 --> 04:11.600
We create some aliases, 

04:11.780 --> 04:14.930
as otherwise, our code is going to be full of colons and angle brackets!

04:15.680 --> 04:22.100
This uses the C++11 "using" keyword, which is easier to read than the old typedef.

04:23.270 --> 04:27.320
The vector of unique_ptrs to entity is going to be "entity_vector".

04:27.350 --> 04:31.220
So this will be the all_entities member of the entity manager.

04:31.850 --> 04:37.490
And then we have an entity_alias_vector, which will be a vector of pointers to entities.

04:38.180 --> 04:45.500
So that is going to be the value type for the map, which is the grouped_entities. And the key type will

04:45.500 --> 04:49.370
be size_t, because that is what the hash code function returns.

04:50.750 --> 04:54.140
Then we have our create() member function, which we looked at before.

04:55.160 --> 04:59.180
Then we have declarations of the non-template member functions.

04:59.720 --> 05:05.540
The get_all() member function, which just looks up the type in the grouped_entities map, and returns

05:05.540 --> 05:10.130
the alias vector. And then the apply_all() function.

05:10.130 --> 05:15.820
So this will call get_all(). That will retrieve the vector of entities.

05:15.830 --> 05:23.360
So entity_group will now be a vector of all the entities of this type, T. And then we iterate over this vector

05:23.360 --> 05:24.170
of pointers.

05:24.530 --> 05:29.330
We cast it down from pointer to base class to pointer to the derived type.

05:29.840 --> 05:30.870
Then we dereference it.

05:30.870 --> 05:32.540
So that gives us the object.

05:33.110 --> 05:35.870
And then we call the function with the object as argument.

05:37.550 --> 05:42.470
In the source file we have the definitions of the non-template functions.

05:43.430 --> 05:48.050
The refresh() memory function is called at the end of every game loop iteration.

05:48.410 --> 05:53.030
This will go through all the entities in the game, and it will delete all the entities which have been

05:53.030 --> 05:53.780
destroyed.

05:54.200 --> 06:00.080
And we actually have two vectors now, one with the alias pointers and one with the unique_ptrs.

06:01.040 --> 06:07.640
We need to delete the alias pointers first, because they are just shadowing the unique_ptrs.

06:08.000 --> 06:12.530
If we do it the other way around, then the alias pointers are going to be "dangling" pointers.

06:13.980 --> 06:20.760
We have this expression that we had before, where we call remove_if(), to logically remove all the elements

06:20.760 --> 06:22.350
which have been destroyed.

06:22.350 --> 06:26.220
And then the erase() call, which will actually delete them from the vector.

06:26.220 --> 06:30.180
So that will delete all the destroyed objects from the alias vector.

06:30.930 --> 06:35.280
And then we do the same again with the unique_ptr vector.

06:37.740 --> 06:41.610
To destroy all the entities we just call the clear() member functions.

06:41.670 --> 06:44.300
Again, we have to deal with the alias pointers first.

06:44.310 --> 06:48.780
So we call the map::clear() member function, then the vector::clear() member function.

06:50.410 --> 06:51.700
To update all the entities,

06:51.700 --> 06:56.200
we just have range for loops. And for drawing them, again.

06:56.920 --> 07:00.970
And then the basic part of the game [loop] is the same.

07:01.990 --> 07:04.780
It is just different when we get to the update stage.

07:04.960 --> 07:10.240
So instead of updating each entity directly, we just call the update member function of the manager.

07:11.290 --> 07:17.140
Then we have the code that we saw before, for handling collisions between the ball and the brick. And

07:17.140 --> 07:19.690
we do the same again, for the ball and the paddle.

07:20.590 --> 07:24.130
And then finally, we call the refresh() member function of the manager.

07:24.130 --> 07:27.430
So that will delete all the objects which have been destroyed.

07:30.040 --> 07:36.010
OK. And finally, just to prove that the game does still work after all this refactoring!

07:36.310 --> 07:37.240
So there we are.

07:38.610 --> 07:42.880
Okay, so there is quite a lot to "get your head around" with the entity manager.

07:42.910 --> 07:47.110
You may need to go back and look at it a few times, and think about it.

07:47.920 --> 07:50.230
If it is too hard for you, then it does not really matter.

07:50.410 --> 07:53.150
We are not going to actually go back to this code again.

07:53.170 --> 07:58.690
You can regard this as a library if you like. So you can just include this code in your programs and

07:58.690 --> 08:02.620
not actually be concerned about the details of how it works, internally.

08:03.710 --> 08:05.440
Okay, so that is it for this video.

08:05.560 --> 08:06.520
I will see you next time.

08:06.520 --> 08:08.830
But until then, keep coding!
