Introduction to Component Based Architecture in Games

This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer. When you’re making a game, you need to create objects to represent the entities in your games – like monsters, the player, bullets, and so on. When you first get started, you might think the most logical thing is […] By Ray Wenderlich.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 6 of this article. Click here to view the first page.

Object for Each Component with Message Passing

The next approach is to create an object for each component, and a GameObject would just be a list of components. Each component would contain both properties, and code to run each frame to make that component “do it’s thing.”

For example, here’s a simple version of what a GameObject might look like:

// GameObject.h
@interface GameObject : NSObject
@property (strong) NSMutableArray * components;
@end

// GameObject.m
@implementation GameObject

- (void)update:(float)dt {
  for (Component * component in self.components) {
    [component update:dt];
  }
}
@end

And here’s what a Component subclass might look like:

// HealthComponent.h
@interface HealthComponent: Component

@property (assign) float curHp;
@property (assign) float maxHp;
@property (assign) BOOL alive;

@end

// HealthComponent.m
@implementation HealthComponent

- (void)update:(float)dt {
    if (!self.alive) return;
    if (self.maxHp == 0) return;
        
    if (self.curHp <= 0) {        
        [[SimpleAudioEngine sharedEngine] playEffect:@"boom.wav"];        
        self.alive = FALSE;        
        // Code to remove game object from scene
    }
}

@end

Notice in the above example that the HealthComponent needs to do something outside the "responsibility" of the component, which is to remove the game object from the scene. This has dependencies on other components (such as the render component, which would need to remove the Cocos2D node from the scene in this case). How do you deal with this situation?

One solution is to give components references to other components - but the problem is this ties them together. A more flexible approach is to broadcast a generic "message" - components can optionally subscribe to messages and deal with them appropriately.

This approach is favored by Gareth Jenkins of 36peas. He has a number of blog posts on the subject, such as this one. You may also see components referred to as behaviors - they are the same thing.

Again, here is my personal opinion about this approach:

  • Pros: Flexible approach with minimal cross component dependencies
  • Cons: Code for each component tied tightly to data, making processing that relies on data from multiple components trickier

Entity System Approach

The final approach I will discuss here is the Entity System approach.

The overall goal of this approach is to model your code similarly to how a relational database would do it. Imagine your game objects being a single integer ID in a database, and each component would be a row in a table.

I like using MySQL, so I like thinking about it that way. Imagine I were to look up some of the values for components of a ZapMonster in MonsterWars:

mysql> select * from health where game_object_id=1;
+----------------+--------+--------+-------+
| game_object_id | cur_hp | max_hp | alive |
+----------------+--------+--------+-------+
|  1             | 1      | 5      | 1     |
+----------------+--------+--------+-------+

mysql> select * from move where game_object_id=1;
+----------------+-------+-------+---------+---------+---------+-----------+
| game_object_id | vel_x | vel_y | accel_x | accel_y | max_vel | max_accel |
+----------------+-------+-------+---------+---------+---------+-----------+
|  1             | 3     | 4     | 0       | 1       | 5       | 5         |
+----------------+-------+-------+---------+---------+---------+-----------+

mysql> select * from ai where game_object_id=1;
Empty set (0.00 sec)

In other words, components would literally just be data about an object - no code at all! And each game object would be just an ID. That's the "entity" part of entity system.

On top of that, you would have systems - basically logical pieces of code that would look for objects that have a set of required components, and then do processing on them. The best description I've seen of this comes from Byte56 on this StackOverflow post:

Think of an entity like a key:

Keys also have teeth (dark blue). The teeth of our entity key is the components that make it up. You can tell entities apart by their ID, even if they have the same teeth. So what do keys fit into? Locks. Locks are our systems. For example, a movement system.

The lock only works if our key has teeth for both position and velocity. This system only processes entities that have a position and a velocity.

In the picture above, note that the System Component would work fine if an object had a Sprite Component as well - it's just that the Position and Velocity components are required.

This approach is favored by Adam Martin of t-machine.org. He has written a great number of blog posts on the subject, and has even started a Wiki about entity system achitecture.

I'm not going to show sample code for this approach yet, because this is the approach you're going to take for this tutorial, so you will see it soon enough! :]

Here is my personal opinion about this approach:

  • Pros: Flexible approach with a nice separation between data and systems
  • Cons: Code can get a bit long-winded and tedious at times compared to simpler approaches

In Summary

Believe it or not, there are many other approaches and variants beyond these 3, and even when you choose one of these there are countless ways to separate your model into different components. There is very little agreement over what the "best" approach is!

So really it comes down to your own personal preferences and what you need for your game. In this tutorial, we're going to go with a variant of the Entity System approach I personally like, but feel free to use your own solutions and modifications :]

Calling all Component Based Systems fans! Do you have a favorite approach that differs from the solution I'm going to present here, or do you have improvements to my approach? I would absolutely love it if you were to modify the MonsterWars game with your own personal favorite approach and send it to me along with some notes about the advantages of your approach. I will then be glad to add it to this post so we all can learn and benefit from your knowledge!

Creating the Entity Class

OK, so let's port MonsterWars to my own personal variant of the Entity System approach.

First, download this starter project that is a "bare bones" version of MonsterWars. Build and run, and you'll see the following:

Empty starter project

As you can see, this is a pretty bare bones project. It contains some code to do the basic UI setup (background, buttons, labels and such), but none of the gameplay logic is added yet.

Note: Just because you're using a component based approach doesn't mean every single thing needs to be inside components. You can if you want, but for this game it was easier to pull the background/UI/decoration stuff outside into the main game layer and just use the component based approach for the monsters/game objects.

Next, create a new group called EntitySystem, and a new group under that called Framework.

Then add a new file to the Framework group using the Objective-C class template. Name the new class Entity, and make it a subclass of NSObject.

Open Entity.h and replace the contents with the following:

@interface Entity : NSObject

- (id)initWithEid:(uint32_t)eid;
- (uint32_t)eid;

@end

And replace Entity.m with the following:

#import "Entity.h"

@implementation Entity {
    uint32_t _eid;
}

- (id)initWithEid:(uint32_t)eid  {
    if ((self = [super init])) {
        _eid = eid;
    }
    return self;
}

- (uint32_t)eid {
    return _eid;
}

@end

Pretty simple, eh? As you can see, an Entity is literally just an integer ID - nothing else. It doesn't even have a list of components associated with it - that's done somewhere else.

In fact, you don't even really need an object for an Entity at all! You could just pass the integer ID around. However, I find it useful to have an object wrapper for the Entity for convenience - you'll see why later on in this tutorial.

Contributors

Over 300 content creators. Join our team.