XNA Lesson 4 -- Difficulty: Easy - Moderate |
July 12th, 2007 -- Game Entity management, a lesson in Inheritance and Abstraction (OOP) |
Many games you will create or play have basic entities. An entity could be anything that is derived from a general class that would hold the basic information for most of things you would see in your game's world. For instance, the "Infection" game my team and I are working on have Players, Bots, Powerups, and Weapons. The base entity class would contain things that all objects residing in the game world would need, such as position, rotation, a forward vector, an image/model/texture, and some basic virtual functions that all entities would likely need.
After giving your game's entities a class heirarchy you'll need a way to manage them inside your game. I highly recommend a list for the base class Entity. Your main game or world could hold this list, and each loop you should update everything in the list. Things included in the update should be position update and movement, collision checks, collision effects, A.I. effects.
The base entity Update() function could be fairly simple:
| // Any base class function that will be used in derived classes // should be 'virtual' public virtual void Update(GameTime gameTime, float GameSpeed) { if ( this.CollisionEnabled ) CheckForCollisions(); } |
The player class, which is derived from the Entity class could ellaborate on top the base entity's method
| // Functions derived from virtual functions in parent classes should // have identical names and parameters, except 'virtual' becomes 'override' public override void Update(GameTime gameTime, float GameSpeed) { // This will call the base entity Update() method // which will check this player for collisions base.Update(gameTime, GameSpeed); UpdatePhysics(gameTime, GameSpeed); UpdateForwardVect(); } |
The bot class, which is derived from the player class would ellaborate as well:
| public override void Update(GameTime gameTime, float GameSpeed) { // This will call the Player class Update() method // which will call the Entity class Update() method, // which will check this bot for collisions and then // return to the Player class to finish that Update() // which will update the bot's physics and forward vector. base.Update(gameTime, GameSpeed); UpdateAIDesires(); } |
By calling base.Update() in a derived class, like Player, you will be calling the parent class' Update() function. In this case, the player class is derived from the Entity class. The beauty of this is the space you will save, and you can have the player update() function only perform update tasks specific to players. This saves you from having to include the collision check in the player's update() function because it is included already in player's parent class update function.
This concept, combined with the list of Entities we mentioned earliar in the lesson will save us even more coding, quite a bit actually. And it will make the game flow much easier to understand. Here is an example of an update() loop for a game with and without the abstract list.
This is a game update() using an abstract list with derived classes like we described earliar:
| // This is inside the main game.cs for XNA protected override void Update(GameTime gameTime) { // Run each entity's Update() function // This 'for' statement will run through every entity in the game // Including players, bots, weapons, powerups, obstacles, etc.... // and will update everything in them that is important. for (int i = 0; i < entityList.Count; i++) entityList[i].Update(gameTime, gameSpeed); CheckInputs(gameTime); // Check keyboard/mouse/gamepad for input Player.CheckForCollisions(); // Check if player has collided with an enemy CheckForGarbageEntities(); // Check if any entities need removed from game base.Update(gameTime); // XNA's default way of updating, not required, but recommended } |
Now here is a game update() using no abstract lists or derived classes:
| protected override void Update(GameTime gameTime) { for (int i = 0; i < playerList.Count; i++) playerList[i].Update(gameTime, gameSpeed); for (int i = 0; i < botList.Count; i++) botList[i].Update(gameTime, gameSpeed); for (int i = 0; i < powerupList.Count; i++) powerupList[i].Update(gameTime, gameSpeed); for (int i = 0; i < weaponList.Count; i++) weaponList[i].Update(gameTime, gameSpeed); for (int i = 0; i < obstacleList.Count; i++) obstacleList[i].Update(gameTime, gameSpeed); CheckInputs(gameTime); // Check keyboard/mouse/gamepad for input Player.CheckForCollisions(); // Check if player has collided with an enemy CheckForGarbageEntities(); // Check if any entities need removed from game base.Update(gameTime); // XNA's default way of updating, not required, but recommended } |
The mess you see above is only a glimpse of the hassle. This just shows the problems you'd have without an abstract entity list in your game. But if you're not using Inheritance to derive your classes than think of the extra lines of code you'll need in EVERY class that would've been an entity! For a bot for instance, you'd be checking the AI, physics, forward vector, and collision in the update loop, rather than before where we just checked the AI, and then the inheritance took care of the rest.
Are there downsides to an entity linked list like the one we've just made? A few, but the downsides are far outweighed by the benefits. One downside to a list like this is that, while looping through it, we may want to make changes to the list itself, like if you defeated an enemy with a bullet. We may have the bullet and enemy in the list, and need them removed, but we wouldn't want to make changes to the list right in the middle of looping through it. What kinds of problems would that cause? Check out this basic scenario:
We have a linked list of 4 entities, and in order they are: Player 1, Player 2, Powerup, and Player 3. Now let us say during this game loop Player 1 has collided with the powerup, and will be picking it up. Here is how it would unfold during our 'for' update() loop.
We'll assume we're using the same 'for' statement from earliar:
| for (int i = 0; i < entityList.Count; i++) entityList[i].Update(gameTime, gameSpeed); |
Loop 1, Player 1 is not checked against himself. List still contains 4 entities.
Loop 2, Player 1 is checked for collision against Player 2, and there is no collision. List still contains 4 entities.
Loop 3, Player 1 is checked for collision against the Powerup, and grabs the powerup. List now contains 3 entites.
Loop 4...........never happens!
Why no loop 4? We'll after loop 3 there was an entity removed from the list as the player took the powerup from the game world and into their possession. This changed the entityList.Count to 3, and when the for loop checks for another iteration it will see that the variable 'i' is now equal to 'entityList.Count' and will terminate. This will result in player 1 not being checked for collision against player 3 this game loop.
How would we fix something like this. There are probably various ways, but since we're dealing with deleting entities, and we have a nice heirarchy set up through entities, we can simple give the Entity class a Destroy() function, which could set a bool inside of entity to tell the game it needs deleted. Then at the end of our game loop we can call a CheckForGarbageEntities() function which will loop through all entites (in reverse order) and delete them. If we didn't go in reverse order, we'd run into the same problem of missing certain entities because of the changing list size. Here's a snippet:
| private void CheckForGarbageEntities() { // Move through list in reverse order so as now to be affected // by the changing length of the list for (int i = entityList.Count - 1; i > 0; i--) { if (entityList[i].MarkedForDeletion) DestroyEntity(EntityList[i]); } } |
The key to the entity list's accuracy is to not make any changes to its size during the game loop. In fact, if you're adding entities in the middle of the game loop, you may want a function to place those in at the end of the game loop as well. If you added a bullet into the list during the 'for' loop it would checked for collision for certain entities but possibly not others, which may lead to undesirable effects. Please note that in this example that EntityList is not an stl linked-list, it is just a standard C# List container. It is much easier to use a standard list because of the ability to reference the list by index, however, if we'd been using a linked-list in this example, we wouldn't need to dispose of entities after each game loop. With a linked-list we could remove an entity from the list in the middle of the 'for' loop, and the linked-list would connect the gaps and continue executing until the end of the list was reached. Linked-lists are find for in-order accessing like this. But if we needed to access random entities out of this list, which is often the case in other parts of a game, then a List container is much faster, with an access time of O(1) vs O(n) for Linked-Lists.
My BombDizzle project has a decent example of setting up an entity heirarchy and setting up a simple garbage collection system for entities. It is open source, feel free to reference it if you'd like.
