Introduction to Component Based Architecture in Games

Ray Wenderlich
Learn how to add artificial intelligence to a simple game!

Learn how to convert this game into a component based architecture!

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 to create a base class called “Game Object” that contains the common code. Then you can create subclasses for Monsters, and maybe subclasses of Monsters for specific types of monsters.

For simple games, this works quite fine and is quite easy to program. However, as your game get larger and more complex, this architecture begins to cause some problems in practice.

In this tutorial, you’re going to:

  • See for yourself why the inheritance-based architecture causes problems in practice
  • Learn how something called “component based architecture” can solve this problem
  • Learn about of some of the different ways to implement component based architecture
  • Implement one such method yourself in an example game, known as “entity systems!”

In this tutorial you will be working on a game made with Cocos2D for the iPhone. It is helpful (but not absolutely necessary) if you have some prior experience with this framework before going through this tutorial. To learn more about Cocos2D, check out some of the other Cocos2D tutorials on this site.

Without further ado, let’s get started!

Introducing MonsterWars

In this tutorial, you will be working on a simple game called MonsterWars. Download the starter project open it in Xcode, and build and run to check it out. You’ll see something like the following:

Sample game with inheritance based architecture

I didn’t have time to add instructions, so here’s how you play:

  • You get money every so often, which you can see in the upper left. You can use it to buy units, by tapping the buttons at the bottom.
  • There are three enemy types: Quirk (fast and cheap), Zap (ranged attackers), and Munch (slow but has AOE chomp).
  • You can switch your units from attacking to defending by tapping on your castle (the green one on the left).
  • If your units destroy the enemy’s castle, you win!

Play the game, and see if you are smart enough to beat the AI!

Note: This game comes from a previous tutorial on this site, where we showed you how to develop the artificial intelligence for this game. If you’re curious curious how the AI works, check it out!

Once you win (or give up due to the AI’s brilliant strategy), look at the code in the project to get familiar with how things are put together. Then keep reading to start seeing some of the drawbacks of this object-oriented game object method!

A Shooting Castle: Overview

This game is pretty good so far, but one thing that would make the game even more fun (IMHO) is if the castle could shoot at incoming enemies.

This would be nice for the game design for a number of reasons:

  • If you’re losing due to a big attack on your castle, you have a better chance to come back since your castle is helping out by attacking.
  • It adds some additional strategy go the game – you want the enemy to attack your castle because you can get some “free hits” on them by your castle.
  • It will make the games last a bit longer, for more fun and back and forth.

Let’s take a look at the current game and see how you could fit this in.

Right now, the game has the following object oriented architecture:

Inheritance based architecture

As you can see, when this was developed, the design only called for Monsters to shoot, so the properties related to shooting were put inside the Monster class.

However, now you want the castles to shoot (which are represented by the Player object). So you have two options:

  1. Rework the object hierarchy. You could switch around the object hierarchy – maybe make Player a subclass of Monster.
  2. Move the code to a common base class. Alternatively you could move the ranged properties and code up to a common base class. The common base class of Monster and Player is GameObject.

Both of these options are equally bad, but for this tutorial you’re going to try option #2 so you can see what it looks like. I already did #1 for you in the sample project to show you what it looks like – Laser derives from Monster, even though arguably a Laser is not really a monster!

A Shooting Castle: Implementation

OK, so you’re going to move the shooting code out of Monster and into a common base class: GameObject.

To do this, open Monster.h and comment out these properties:

/*
@property (assign) BOOL isRanged;
@property (assign) float rangedRange;
@property (assign) float rangedDamage;
@property (assign) float rangedDamageRate;
@property (assign) float rangedLastDamageTime;
@property (strong) NSString * rangedSound;
*/

Then open Monster.m and comment out the updateRanged: method:

/*
- (void)updateRanged:(ccTime)dt {    
    // Lots of code here...    
}
*/

Finally, inside update: comment out the call to updateRanged:

[self updateRanged:dt];

Then, open GameObject.h and add the properties you removed from Monster.h:

@property (assign) BOOL isRanged;
@property (assign) float rangedRange;
@property (assign) float rangedDamage;
@property (assign) float rangedDamageRate;
@property (assign) float rangedLastDamageTime;
@property (strong) NSString * rangedSound;

And open GameObject.m and add the method you removed from Monster.m:

- (void)updateRanged:(ccTime)dt {
 
    if (!self.isRanged) return;
 
    GameObject * enemy = [self.layer closestEnemyToGameObject:self];
    if (!enemy) {
        enemy = [self.layer playerForTeam:[self.layer oppositeTeam:self.team]];
    }
    if (!enemy) return;
 
    float distance = ccpDistance(self.position, enemy.position);
    static float WIGGLE_ROOM = 5;
    if (ABS(distance) <= (self.rangedRange + WIGGLE_ROOM) && CACurrentMediaTime() - self.rangedLastDamageTime > self.rangedDamageRate) {
 
        [[SimpleAudioEngine sharedEngine] playEffect:self.rangedSound];
 
        self.rangedLastDamageTime = CACurrentMediaTime();
 
        Laser * laser = [self.layer createLaserForTeam:self.team];
        laser.position = self.position;
        laser.meleeDamage = self.rangedDamage;
 
        CGPoint direction = ccpNormalize(ccpSub(enemy.position, self.position));
        static float laserPointsPerSecond = 100;
        static float laserDistance = 1000;
 
        CGPoint target = ccpMult(direction, laserDistance);
        float duration = laserDistance / laserPointsPerSecond;
 
        laser.rotation = -1 * CC_RADIANS_TO_DEGREES(ccpToAngle(direction));
        laser.zOrder = 1;
 
        [laser runAction:
         [CCSequence actions:
          [CCMoveBy actionWithDuration:duration position:target],
          [CCCallBlock actionWithBlock:^{
             [laser removeFromParentAndCleanup:YES];
         }], nil]];
    }
 
}

And finally call this in update:, right after the check if maxHp is 0:

[self updateRanged:delta];

Now that the properties and logic are in the base class, you can set up your castles to shoot. Open Player.m and add the following code inside initWithSpriteFrameName:

self.isRanged = YES;
self.rangedRange = 200;
self.rangedDamageRate = 2.0;
self.rangedDamage = 5;
self.rangedSound = @"pew.wav";

Build and run, and now your castle should shoot at the enemies, adding a new layer of fun and strategy!

A shooting castle

However, your code is a big mess! Let’s take a look and discuss why it’s getting messy – and likely to get even messier in the future.

Drawbacks of Object Oriented Game Architecture

Even though this game is fairly simple, you can already see a number of problems with object oriented game architecture as the game gets more complicated:

  • As you extend the game design, you start having more and more “exceptions” to the original rules. This makes you want to move more and more code to the base GameObject class, making it long and convoluted.
  • Alternatively you might have a hierarchy that doesn’t make logical sense – i.e. is a Laser really a Monster?
  • Code for various “systems” for the game is all jumbled together. Wouldnt it be better if all of the code related to shooting was in one place, and all the code for movement in canother place?

These sorts of problems is exactly what component based game architecture is meant to solve! Let’s take a look.

Introduction to Component Based Architecture

The basic idea behind component based architecture is to prefer composition over inheritance.

This is a fancy way of saying “instead of inheriting from an object to reuse code/get functionality, build your game objects out of sub-components instead.”

Still confused? Here’s a diagram of an alternative way to organize two examples of objects from this game: the AI Player and the Zap Monster:

Component Based Architecture Diagram

In this diagram, you see that both of these objects are instances of the same class – GameObject. However, these GameObjects don’t do much – they are just collection of components that do all the work.

So when adding an object to your game, you just add all the components that the object you’re trying to add needs. In this example:

  • The AI Player needs to be rendered to the screen (as a castle), with a health bar, associated with a team, has properties as a player (such as coins), has a gun to shoot, some artificial intelligence.
  • The Zap Monster needs to be rendered to the screen (as a zap monser), with a health bar, associated with a team, with a gun to shoot, and the ability to move.

The nice thing about organizing things about components like this is it makes it a lot easier to tweak your game design. Want your castle to start moving around? Just add a move component!

Note: Component based architecture also helps you do some quite fancy things. Instead of hard coding what components are in each game object, you could read a definition from an external file instead. This makes it really easy to create different behavior for your game objects without having to code, which is great for designers and quickly tweaking/iterating your game design.

That’s the basic idea behind component based architecture. However, the trickiest thing about component based architecture are there are about a zillion ways to implement it.

Each programmer/studio seems to have their own preference and what works best for them. This makes trying to learn how to do it difficult because there are so many different approaches discussed out there.

So let’s take a look at some popular approaches, then you will pick one of them and convert this MonsterWars game to a component based architecture!

Component Based Architecture Approaches

As I mentioned, there is a wide variety of approches to component based architecture out there. Here are a few:

Simplest Method: Massive GameObject with On/Off Switches

In this method, you create a massive GameObject class (or structure) with every property you might need. For each “group” or “component” of properties, you add an on/off boolean to say whether that “component” is there. Here’s what it might look like for MonsterWars:

@interface GameObject : NSObject
 
// Render component
@property (assign) BOOL hasRender;
@property (strong) CCSprite * sprite;
 
// Health component
@property (assign) BOOL hasHealth;
@property (assign) float curHp;
@property (assign) float maxHp;
@property (assign) BOOL alive;
 
// Team component
@property (assign) BOOL hasTeam;
@property (assign) int team;
@property (assign) BOOL attacking;
 
// Gun component
@property (assign) BOOL hasGun;
@property (assign) float rangedRange;
@property (assign) float rangedDamage;
@property (assign) float rangedDamageRate;
@property (assign) float rangedLastDamageTime;
@property (strong) NSString * rangedSound;
 
// Move component
@property (asisgn) BOOL hasMove;
@property (assign) CGPoint velocity;
@property (assign) CGPoint acceleration;
@property (assign) float maxVelocity;
@property (assign) float maxAcceleration;
 
// Damage component
@property (assign) BOOL hasDamage;
@property (assign) float meleeDamage;
@property (assign) BOOL meleeDestroySelf;
@property (assign) float meleeDamageRate;
@property (assign) float meleeLastDamageTime;
@property (assign) BOOL meleeAoe;
@property (strong) NSString * meleeSound;
 
// Player Component
@property (assign) BOOL hasPlayer;
@property (assign) int coins;
@property (assign) double lastCoinDrop;
 
// AI Component
@property (assign) BOOL hasAI;
@property (strong) AIState * currentSTate;
 
// Human Component
@property (assign) BOOL hasHuman;
@end

Then inside your GameObject’s update method, you’d check if each component was there, and process it if so:

- (void)update:(float)dt {
 
  if (self.hasRender) {
    [self updateRender:dt];
  } 
  if (self.hasHealth) {
    [self updateHealth:dt];
  }
  if (self.hasTeam) {
    [self updateTeam:dt];
  }
  if (self.hasGun) {
    [self updateGun:dt];
  }
  if (self.hasMove) {
    [self updateMove:dt];
  }
  if (self.hasDamage) {
    [self updateDamage:dt];
  }
  if (self.hasPlayer) {
    [self updatePlayer:dt];
  }
  if (self.hasAI) {
    [self updateAI:dt];
  }
  if (self.hasHuman) {
    [self updateHuman:dt];
  }
}

This method was used by Phil Hassey in the development of his game Dynamite Jack (albeit a C/C++ based version). You can read more about it in his blog post on the matter.

If you think about it, this approach is not too far away from taking the Object-Oriented example above to the extreme end – just moving up all the code to the highest level GameObject, and just turning various features on and off.

My personal opinion about this approach:

  • Pros: Definitely the simplest option, easy to understand and work with.
  • Cons: Doesn’t have as much flexibility as some of the other approaches, code might have more of a tendency to get bloated for larger games.

Update: Adam Martin pointed out another con about this approach – it’s much more difficult to delete code with this approach than in some of the other techniques described below. For example, in the Entity System approach you can delete a system or add a new component and everything just works – no errors or dependencies involved! This is great for quick and dirty prototyping. On the other hand, this approach makes things a bit trickier, especially when you have old incompatible serialized game states lying around or if you want to make changes at runtime.

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.

Creating the Component Class and a Subclass

Next create a new group under EntitySystem called Components.

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

And then you’re done :] This is a completely empty class, all the interesting stuff will be in subclasses.

Again, this is a class that is optional and you don’t even really need. I like having it for a bit of type safety though.

Let’s create two subclass for now so you can see some examples. Add a new file to the Components group using the Objective-C class template. Name the new class RenderComponent, and make it a subclass of Component.

Replace the contents of RenderComponent.h with the following:

#import "Component.h"
#import "cocos2d.h"
 
@interface RenderComponent : Component
 
@property (strong) CCSprite * node;
 
- (id)initWithNode:(CCSprite *)node;
 
@end

And replace RenderComponent.m with the following:

#import "RenderComponent.h"
 
@implementation RenderComponent
 
- (id)initWithNode:(CCSprite *)node {
    if ((self = [super init])) {
        self.node = node;
    }
    return self;
}
 
@end

As you can see, this class is literally just data – along with a convenience method to initialize the data. According to the Entity System design, this class should have no code to actually operate upon the data – that is the job of the systems, which you’ll develop later.

Let’s see another example. Add a new file to the Components group using the Objective-C class template. Name the new class HealthComponent, and make it a subclass of Component.

Replace the contents of HealthComponent.h with the following:

#import "Component.h"
 
@interface HealthComponent : Component
 
@property (assign) float curHp;
@property (assign) float maxHp;
@property (assign) BOOL alive;
 
- (id)initWithCurHp:(float)curHp maxHp:(float)maxHp;
 
@end

And replace HealthComponent.m with the following:

#import "HealthComponent.h"
 
@implementation HealthComponent
 
- (id)initWithCurHp:(float)curHp maxHp:(float)maxHp {
    if ((self = [super init])) {
        self.curHp = curHp;
        self.maxHp = maxHp;
        self.alive = YES;
    }
    return self;
}
 
@end

Again, just data and a convenience method.

Creating the Entity Manager

Next you need to create an object that acts as the “database”, where you can look up entities, get their list of components, and so on. In this game, you’ll call this class the Entity Manager.

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

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

#import "Entity.h"
#import "Component.h"
 
@interface EntityManager : NSObject
 
- (uint32_t) generateNewEid;
- (Entity *)createEntity;
- (void)addComponent:(Component *)component toEntity:(Entity *)entity;
- (Component *)getComponentOfClass:(Class)class forEntity:(Entity *)entity;
- (void)removeEntity:(Entity *)entity;
- (NSArray *)getAllEntitiesPosessingComponentOfClass:(Class)class;
 
@end

And replace EntityManager.m with the following:

#import "EntityManager.h"
 
@implementation EntityManager {
    NSMutableArray * _entities;
    NSMutableDictionary * _componentsByClass;
    uint32_t _lowestUnassignedEid;
}
 
- (id)init {
    if ((self = [super init])) {        
        _entities = [NSMutableArray array];
        _componentsByClass = [NSMutableDictionary dictionary];
        _lowestUnassignedEid = 1;
    }
    return self;
}
 
@end

This just initializes the data structures you’ll be using for the EntityManager. You’ll have a list of all the entities (which remember are just integers!), then you’ll have a dictionary that contains a list of each type of components (RenderComponents, HealthComponents, etc). Finally, you’ll keep track of the lowest unassigned entity ID to use.

Next add this method to generate a new entity ID:

- (uint32_t) generateNewEid {
    if (_lowestUnassignedEid < UINT32_MAX) {
        return _lowestUnassignedEid++;
    } else {
        for (uint32_t i = 1; i < UINT32_MAX; ++i) {
            if (![_entities containsObject:@(i)]) {
                return i;
            }
        }
        NSLog(@"ERROR: No available EIDs!");
        return 0;
    }
}

As you can see, it simply returns the next highest number – until it wraps around to the maximum integer, in which case it looks through the list of entities for an available entity ID.

Next add this method to create a new Entity:

- (Entity *)createEntity {
    uint32_t eid = [self generateNewEid];
    [_entities addObject:@(eid)];
    return [[Entity alloc] initWithEid:eid];
}

This is as simple as generating a new entity ID and adding it to the list. It returns the wrapper Entity object for convenience.

Next add this method to add a component to an entity, and a method to get a component for an entity of a particular class:

- (void)addComponent:(Component *)component toEntity:(Entity *)entity {
    NSMutableDictionary * components = _componentsByClass[NSStringFromClass([component class])];
    if (!components) {
        components = [NSMutableDictionary dictionary];
        _componentsByClass[NSStringFromClass([component class])] = components;
    }    
    components[@(entity.eid)] = component;
}
 
- (Component *)getComponentOfClass:(Class)class forEntity:(Entity *)entity {
    return _componentsByClass[NSStringFromClass(class)][@(entity.eid)];
}

The addComponent method first looks inside the dictionary of components by class, where the key is the name of the class (in string version), and the value is a second dictionary. The second dictionary’s key is the entity ID, and the value is the component itself. The getComponentOfClass simply reverses this lookup.

Note: With this approach, there can only be one instance of a component per entity. You may have a game where you want to have multiple instances of a component per entity (such as two guns) – if that’s the case, you can change these data structures a bit so instead of the value of the second dictionary being a component, it would be a list of components instead.

Next add this method to remove an entity:

- (void)removeEntity:(Entity *)entity {
    for (NSMutableDictionary * components in _componentsByClass.allValues) {
        if (components[@(entity.eid)]) {
            [components removeObjectForKey:@(entity.eid)];
        }
    }
    [_entities removeObject:@(entity.eid)];
}

This simply removes all components for an entity from the data structures, then removes the entity itself.

Finally, add this helper method:

- (NSArray *)getAllEntitiesPosessingComponentOfClass:(Class)class {
    NSMutableDictionary * components = _componentsByClass[NSStringFromClass(class)];
    if (components) {
        NSMutableArray * retval = [NSMutableArray arrayWithCapacity:components.allKeys.count];
        for (NSNumber * eid in components.allKeys) {
            [retval addObject:[[Entity alloc] initWithEid:eid.integerValue]];
        }
        return retval;
    } else {
        return [NSArray array];
    }
}

This is a helper method to find all the entities that have a particular component. Think of it as a “select * from component_name” query.

And that’s it for the Entity Manager for now. There’s only a few more steps before you’re ready to try this out!

Creating the Systems

So far you’ve created the entity part of the entity system (Ientity, Component, and Component subclasses), along with the database part (EntityManager). Now it’s time to create the system part – i.e. the code that actually does something useful!

Inside the EntitySystem group, create a new subgroup called Systems.

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

Then replace System.h with the following code:

@class EntityManager;
 
@interface System : NSObject
 
@property (strong) EntityManager * entityManager;
 
- (id)initWithEntityManager:(EntityManager *)entityManager;
 
- (void)update:(float)dt;
 
@end

And replace System.m with the following:

#import "System.h"
 
@implementation System
 
- (id)initWithEntityManager:(EntityManager *)entityManager {
    if ((self = [super init])) {
        self.entityManager = entityManager;
    }
    return self;
}
 
- (void)update:(float)dt {   
}
 
@end

So you can see the base System subclass is pretty simple – it has a reference to the entity manager, and an update method.

Now let’s create a subclass that will be responsible for a) figuring out when an object is alive or dead, and doing some basic logic upon death, and b) drawing the health bar to the screen.

To do this, add a new file to the Systems group using the Objective-C class template. Name the new class HealthSystem, and make it a subclass of NSObject.

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

#import "System.h"
 
@interface HealthSystem : System
 
- (void)draw;
 
@end

Then open HealthSystem.m and replace the contents with the following:

#import "HealthSystem.h"
#import "EntityManager.h"
#import "HealthComponent.h"
#import "RenderComponent.h"
#import "SimpleAudioEngine.h"
 
@implementation HealthSystem
 
- (void)update:(float)dt {
 
    // 1
    NSArray * entities = [self.entityManager getAllEntitiesPosessingComponentOfClass:[HealthComponent class]];
    for (Entity * entity in entities) {
 
        // 2
        HealthComponent * health = (HealthComponent *) [self.entityManager getComponentOfClass:[HealthComponent class] forEntity:entity];
        RenderComponent * render = (RenderComponent *) [self.entityManager getComponentOfClass:[RenderComponent class] forEntity:entity];
 
        // 3
        if (!health.alive) return;
        if (health.maxHp == 0) return;
        if (health.curHp <= 0) {
            [[SimpleAudioEngine sharedEngine] playEffect:@"boom.wav"];
            health.alive = FALSE;
 
            // 4
            if (render) {            
                [render.node runAction:
                 [CCSequence actions:
                  [CCFadeOut actionWithDuration:0.5],
                  [CCCallBlock actionWithBlock:^{
                     [render.node removeFromParentAndCleanup:YES];
                     [self.entityManager removeEntity:entity];
                 }], nil]];
            } else {
                [self.entityManager removeEntity:entity];
            }
        }
    }    
}
 
@end

There’s a good bit of code here, so let’s go over it section by section:

  1. Uses the helper method you wrote earlier to get all of the entities that have HealthComponents associated with them.
  2. For each of these entities, looks up the HealthComponent and tries to see if there’s a RenderComponent too. The HealthComponent is guaranteed (since you just searched for it), but the RenderComponent might be nil.
  3. This is the same code that was in the previous version of MonsterWars – no change here.
  4. If the object has died, checks to see if there’s a render node. If there is, it fades out the node, then removes it from the entity manager (and screen). Otherwise it just immediately removes it from the entity manager.

Next add the draw method:

- (void)draw {    
    NSArray * entities = [self.entityManager getAllEntitiesPosessingComponentOfClass:[HealthComponent class]];
    for (Entity * entity in entities) {
 
        HealthComponent * health = (HealthComponent *) [self.entityManager getComponentOfClass:[HealthComponent class] forEntity:entity];
        RenderComponent * render = (RenderComponent *) [self.entityManager getComponentOfClass:[RenderComponent class] forEntity:entity];        
        if (!health || !render) continue;
 
        int sX = render.node.position.x - render.node.contentSize.width/2;
        int eX = render.node.position.x + render.node.contentSize.width/2;
        int actualY = render.node.position.y + render.node.contentSize.height/2;
 
        static int maxColor = 200;
        static int colorBuffer = 55;
        float percentage = ((float) health.curHp) / ((float) health.maxHp);
        int actualX = ((eX-sX) * percentage) + sX;
        int amtRed = ((1.0f-percentage)*maxColor)+colorBuffer;
        int amtGreen = (percentage*maxColor)+colorBuffer;
 
        glLineWidth(7);
        ccDrawColor4B(amtRed,amtGreen,0,255);
        ccDrawLine(ccp(sX, actualY), ccp(actualX, actualY));
    }    
}

This starts out just as last time, except this time to draw the health bar both the health component and render component are required (like the “key” discussed earlier), so it bails if these are not there.

Otherwise, the rest of this code is just the same as it was in the previous version of Monster Wars.

So overall, notice that this one system works primarily on the HealthComponent data, but it uses aspects of the RenderComponent data to get its job done. This is quite typical when looking at systems!

Putting it All Together

It’s almost time to try this out – you just need to add some code to put everything together!

Open HelloWorldLayer.m and import these headers at the top of the file:

#import "EntityManager.h"
#import "HealthSystem.h"
#import "RenderComponent.h"
#import "HealthComponent.h"

Also define these instance variables:

EntityManager * _entityManager;
HealthSystem * _healthSystem;
Entity * _aiPlayer;
Entity * _humanPlayer;

Then implement addPlayers as follows:

- (void)addPlayers {
 
    CGSize winSize = [CCDirector sharedDirector].winSize;
    _entityManager = [[EntityManager alloc] init];
    _healthSystem = [[HealthSystem alloc] initWithEntityManager:_entityManager];
 
    // Create AI
    CCSprite * aiSprite = [[CCSprite alloc] initWithSpriteFrameName:@"castle2_def.png"];
    aiSprite.position = ccp(winSize.width - aiSprite.contentSize.width/2, winSize.height/2);
    [_batchNode addChild:aiSprite];
 
    _aiPlayer = [_entityManager createEntity];
    [_entityManager addComponent:[[RenderComponent alloc] initWithNode:aiSprite] toEntity:_aiPlayer];
    [_entityManager addComponent:[[HealthComponent alloc] initWithCurHp:200 maxHp:200] toEntity:_aiPlayer];
 
    // Create human
    CCSprite * humanSprite = [[CCSprite alloc] initWithSpriteFrameName:@"castle1_def.png"];
    humanSprite.position = ccp(humanSprite.contentSize.width/2, winSize.height/2);
    [_batchNode addChild:humanSprite];
 
    _humanPlayer = [_entityManager createEntity];
    [_entityManager addComponent:[[RenderComponent alloc] initWithNode:humanSprite] toEntity:_humanPlayer];
    [_entityManager addComponent:[[HealthComponent alloc] initWithCurHp:200 maxHp:200] toEntity:_humanPlayer];
 
}

As you can see, now adding a game object is a matter of creating an Entity (i.e. integer), and then adding a bunch of components to it.

Note: Adam Martin pointed out that you might want to make a static initializer for each component so you can call something like [HealthComponent healthWithCurHp:200 maxHp:200] instead of the alloc/init method shown above. Since you’re creating components so often, this little bit of savings adds up. In addition, it’s even better if you use C++ structures for components instead of objects, since it saves you time in implementaiton.

Finally, implement update: and draw as follows:

- (void)update:(ccTime)dt {
    [_healthSystem update:dt];
 
    // Test code to decrease AI's health
    static float timeSinceLastHealthDecrease = 0;
    timeSinceLastHealthDecrease += dt;
    if (timeSinceLastHealthDecrease > 1.0) {
        timeSinceLastHealthDecrease = 0;
        HealthComponent * health = (HealthComponent *) [_entityManager getComponentOfClass:[HealthComponent class] forEntity:_aiPlayer];
        if (health) {
            health.curHp -= 10;
            if (health.curHp <= 0) {
                [self showRestartMenu:YES];
            }
        }
    }
 
}
 
- (void)draw {
    [_healthSystem draw];
}

Every tick, it gives each system time to update – which right now is just the health system. There is also some test code in there to decrease the AI’s health every second, just so you can see something happening.

And that’s it – you can finally perform your first test of a basic Entity System framework! Build and run, and you’ll see the castles appear on the screen, with their health rendering appropriately:

Entity System Test

And to see one of the benefits of the entity system, comment out the lines that add the HealthComponents to the AI and Human. Build and run, and the game will still work fine – the castles just won’t have health bars! Then put them back when you’re done.

Entity Factory

Up to this point, you added the code to create an entity directly inside the HelloWorldLayer class. But usually when using an Entity System it’s a lot easier to create a central class to create “templates” of entities that you can then customize.

For example, the central class would create a “basic Zap monster” for you with the appropriate components set, and then you can modify values for the particular Zap monster you want. This makes it easier for you to decide what components are attached to each entity at a central location.

We will call this central “entity making class” an entity factory. Create a new file in the Framework group using the Objective-C class template. Name the new class EntityFactory, and make it a subclass of NSObject.

Then replace EntityFactory.h with the following:

@class Entity;
@class EntityManager;
@class CCSpriteBatchNode;
 
@interface EntityFactory : NSObject
 
- (id)initWithEntityManager:(EntityManager *)entityManager batchNode:(CCSpriteBatchNode *)batchNode;
 
- (Entity *)createHumanPlayer;
- (Entity *)createAIPlayer;
- (Entity *)createQuirkMonster;
 
@end

And replace EntityFactory.m with the following:

#import "EntityFactory.h"
#import "cocos2d.h"
#import "EntityManager.h"
#import "RenderComponent.h"
#import "HealthComponent.h"
 
@implementation EntityFactory {
    EntityManager * _entityManager;
    CCSpriteBatchNode * _batchNode;
}
 
- (id)initWithEntityManager:(EntityManager *)entityManager batchNode:(CCSpriteBatchNode *)batchNode {
    if ((self = [super init])) {
        _entityManager = entityManager;
        _batchNode = batchNode;
    }
    return self;
}
 
- (Entity *)createHumanPlayer {
    CCSprite * sprite = [[CCSprite alloc] initWithSpriteFrameName:@"castle1_def.png"];
    [_batchNode addChild:sprite];
 
    Entity * entity = [_entityManager createEntity];
    [_entityManager addComponent:[[RenderComponent alloc] initWithNode:sprite] toEntity:entity];
    [_entityManager addComponent:[[HealthComponent alloc] initWithCurHp:200 maxHp:200] toEntity:entity];
    return entity;
}
 
- (Entity *)createAIPlayer {
    CCSprite * sprite = [[CCSprite alloc] initWithSpriteFrameName:@"castle2_def.png"];
    [_batchNode addChild:sprite];
 
    Entity * entity = [_entityManager createEntity];
    [_entityManager addComponent:[[RenderComponent alloc] initWithNode:sprite] toEntity:entity];
    [_entityManager addComponent:[[HealthComponent alloc] initWithCurHp:200 maxHp:200] toEntity:entity];
    return entity;
}
 
- (Entity *)createQuirkMonster {
    CCSprite * sprite = [[CCSprite alloc] initWithSpriteFrameName:@"quirk1.png"];
    [_batchNode addChild:sprite];
 
    Entity * entity = [_entityManager createEntity];
    [_entityManager addComponent:[[RenderComponent alloc] initWithNode:sprite] toEntity:entity];
    [_entityManager addComponent:[[HealthComponent alloc] initWithCurHp:5 maxHp:5] toEntity:entity];
    return entity;
}
 
@end

This is the same code you wrote earlier, just brought into this helper class, so that anyone who has access to the EntityFactory can easily create game objects of any given “template”. It’s also a convenient place if you want to read these “template” settings out of a file instead!

There’s also a new method in here to create a Quirk monster, as a test.

Now let’s put it to use. Go back to HelloWorldLayer.m and make the following changes:

// Add this import to the top of the file
#import "EntityFactory.h"
 
// Add this private instance variable
EntityFactory * _entityFactory;
 
// Replace addPlayers with this:
- (void)addPlayers {
 
    CGSize winSize = [CCDirector sharedDirector].winSize;
    _entityManager = [[EntityManager alloc] init];
    _healthSystem = [[HealthSystem alloc] initWithEntityManager:_entityManager];
    _entityFactory = [[EntityFactory alloc] initWithEntityManager:_entityManager batchNode:_batchNode];
 
    _aiPlayer = [_entityFactory createAIPlayer];
    RenderComponent * aiRender = (RenderComponent *) [_entityManager getComponentOfClass:[RenderComponent class] forEntity:_aiPlayer];
    if (aiRender) {
        aiRender.node.position = ccp(winSize.width - aiRender.node.contentSize.width/2, winSize.height/2);
    }
 
    _humanPlayer = [_entityFactory createHumanPlayer];
    RenderComponent * humanRender = (RenderComponent *) [_entityManager getComponentOfClass:[RenderComponent class] forEntity:_humanPlayer];
    if (humanRender) {
        humanRender.node.position = ccp(humanRender.node.contentSize.width/2, winSize.height/2);
    }    
 
}
 
// Replace quirkButtonTapped: with this:
- (void)quirkButtonTapped:(id)sender {
    NSLog(@"Quirk button tapped!");
 
    [[SimpleAudioEngine sharedEngine] playEffect:@"spawn.wav"];
 
    Entity * entity = [_entityFactory createQuirkMonster];
    RenderComponent * render = (RenderComponent *) [_entityManager getComponentOfClass:[RenderComponent class] forEntity:entity];
    if (render) {        
        CGSize winSize = [CCDirector sharedDirector].winSize;
        float randomOffset = CCRANDOM_X_Y(-winSize.height * 0.25, winSize.height * 0.25);
        render.node.position = ccp(winSize.width * 0.25, winSize.height * 0.5 + randomOffset);
    }
}

This modifies the code to use the new entity factory – this way, there’s a nice central place that defines what components are on each “template.” It’s not a big deal right now, but later when you want each system to be able to create objects, you’ll be able to use the entity factory to do this rather than duplicating the same information in multiple places.

Finally, add this helper macro to the bottom of Supporting Files\Prefix.pch:

#define CCRANDOM_X_Y(__X__, __Y__) (((__Y__) - (__X__)) * (arc4random() / (float)0xffffffff) + (__X__))

Build and run, and now you can tap the quirk button to spawn some Quirks!

Spawning Monsters

Moving Monsters

Let’s try making our monsters move now – that will be a good example of adding another component and another system.

Let’s start with the Component. Create a new file in the Components group using the Objective-C class template. Name the new class MoveComponent, and make it a subclass of Component.

Then replace MoveComponent.h with the following:

#import "Component.h"
 
@interface MoveComponent : Component
 
@property (assign) CGPoint moveTarget;
 
@property (assign) CGPoint velocity;
@property (assign) CGPoint acceleration;
 
@property (assign) float maxVelocity;
@property (assign) float maxAcceleration;
 
- (id)initWithMoveTarget:(CGPoint)moveTarget maxVelocity:(float)maxVelocity maxAcceleration:(float)maxAcceleration;
 
@end

And MoveComponent.m with the following:

#import "MoveComponent.h"
 
@implementation MoveComponent
 
- (id)initWithMoveTarget:(CGPoint)moveTarget maxVelocity:(float)maxVelocity maxAcceleration:(float)maxAcceleration {
    if ((self = [super init])) {
        self.moveTarget = moveTarget;
        self.velocity = CGPointZero;
        self.acceleration = CGPointZero;
        self.maxVelocity = maxVelocity;
        self.maxAcceleration = maxAcceleration;
    }
    return self;
}
 
@end

As usual, this is just a data class with the information related to movement. Note that this system takes an “input variable” – the target that the object should move to.

Next, create a new file in the Systems group using the Objective-C class template. Name the new class MoveSystem, and make it a subclass of System.

Then replace MoveSystem.m with the following:

#import "MoveSystem.h"
#import "EntityManager.h"
#import "MoveComponent.h"
#import "RenderComponent.h"
 
@implementation MoveSystem
 
- (CGPoint)arriveEntity:(Entity *)entity withMoveComponent:(MoveComponent *)move renderComponent:(RenderComponent *)render {
 
    CGPoint vector = ccpSub(move.moveTarget, render.node.position);
    float distance = ccpLength(vector);
 
    float targetRadius = 5;
    float slowRadius = targetRadius + 25;
    static float timeToTarget = 0.1;
 
    if (distance < targetRadius) {
        return CGPointZero;
    }
 
    float targetSpeed;
    if (distance > slowRadius) {
        targetSpeed = move.maxVelocity;
    } else {
        targetSpeed = move.maxVelocity * distance / slowRadius;
    }
 
    CGPoint targetVelocity = ccpMult(ccpNormalize(vector), targetSpeed);
 
    CGPoint acceleration = ccpMult(ccpSub(targetVelocity, move.velocity), 1/timeToTarget);
    if (ccpLength(acceleration) > move.maxAcceleration) {
        acceleration = ccpMult(ccpNormalize(acceleration), move.maxAcceleration);
    }
    return acceleration;
}
 
- (CGPoint)separateEntity:(Entity *)entity withMoveComponent:(MoveComponent *)move renderComponent:(RenderComponent *)render {
 
    CGPoint steering = CGPointZero;
    NSArray * entities = [self.entityManager getAllEntitiesPosessingComponentOfClass:[RenderComponent class]];
    for (Entity * otherEntity in entities) {
 
        if (otherEntity.eid == entity.eid) continue;
        RenderComponent * otherRender = (RenderComponent *) [self.entityManager getComponentOfClass:[RenderComponent class] forEntity:otherEntity];
 
        CGPoint direction = ccpSub(render.node.position, otherRender.node.position);
        float distance = ccpLength(direction);
        static float SEPARATE_THRESHHOLD = 20;
 
        if (distance < SEPARATE_THRESHHOLD) {
            direction = ccpNormalize(direction);
            steering = ccpAdd(steering, ccpMult(direction, move.maxAcceleration));
        }
    }
    return steering;
}
 
- (void)update:(float)dt {
 
    NSArray * entities = [self.entityManager getAllEntitiesPosessingComponentOfClass:[MoveComponent class]];
    for (Entity * entity in entities) {
 
        MoveComponent * move = (MoveComponent *) [self.entityManager getComponentOfClass:[MoveComponent class] forEntity:entity];
        RenderComponent * render = (RenderComponent *) [self.entityManager getComponentOfClass:[RenderComponent class] forEntity:entity];
        if (!move || !render) continue;
 
        CGPoint arrivePart = [self arriveEntity:entity withMoveComponent:move renderComponent:render];
        CGPoint separatePart = [self separateEntity:entity withMoveComponent:move renderComponent:render];
        CGPoint newAcceleration = ccpAdd(arrivePart, separatePart);
 
        // Update current acceleration based on the above, and clamp
        move.acceleration = ccpAdd(move.acceleration, newAcceleration);
        if (ccpLength(move.acceleration) > move.maxAcceleration) {
            move.acceleration = ccpMult(ccpNormalize(move.acceleration), move.maxAcceleration);
        }
 
        // Update current velocity based on acceleration and dt, and clamp
        move.velocity = ccpAdd(move.velocity, ccpMult(move.acceleration, dt));
        if (ccpLength(move.velocity) > move.maxVelocity) {
            move.velocity = ccpMult(ccpNormalize(move.velocity), move.maxVelocity);
        }
 
        // Update position based on velocity
        CGPoint newPosition = ccpAdd(render.node.position, ccpMult(move.velocity, dt));
        CGSize winSize = [CCDirector sharedDirector].winSize;
        newPosition.x = MAX(MIN(newPosition.x, winSize.width), 0);
        newPosition.y = MAX(MIN(newPosition.y, winSize.height), 0);
        render.node.position = newPosition;
 
    }
}
 
@end

This is a big block of code, but I’m not going to review it because it is the same code as we covered in the previous AI tutorial, except it has been converted to use the Entity System, in the manner we discussed already in this tutorial. Take a look and make sure it makes sense to you.

Next, let’s add this new Component to the Quirk template. Open EntityFactory.m and make the following changes:

// Add to top of file
#import "MoveComponent.h"
 
// Add to bottom of createQuirkMonster before the return
[_entityManager addComponent:[[MoveComponent alloc] initWithMoveTarget:ccp(200, 200) maxVelocity:100 maxAcceleration:100] toEntity:entity];

Finally, set up the new MoveSystem in HelloWorldLayer.m:

// Add to top of file
#import "MoveSystem.h"
 
// Add new private instance variable
MoveSystem * _moveSystem;
 
// Add in addPlayers, right after creating the healthSystem
_moveSystem = [[MoveSystem alloc] initWithEntityManager:_entityManager];
 
// Add in update, right after calling update on the healthSystem
[_moveSystem update:dt];

And that’s it! Build and run, and spawn a few Quirks, and they will move toward their target (right now set to 200, 200):

Moving monsters with components

Want to try something really fun? See how easy it is to make your castle move. Inside EntityFactory.m, add this to the bottom of createAIPlayer, right before the return statement:

[_entityManager addComponent:[[MoveComponent alloc] initWithMoveTarget:ccp(400, 200) maxVelocity:100 maxAcceleration:100] toEntity:entity];

Build and run, and you have a moving castle! (Maybe it would be cool if you converted the castle into a big robot, that moved up and down!)

Where To Go From Here?

Now that you have a firm understanding of how to make components and systems that use components, converting the rest of the project to the Entity System model is a fairly straightforward port – but time consuming.

It would take forever to explain each step in this tutorial, so instead I have done this for you. Go ahead and download the completed port and check it out. You can also look at the git history that comes with the project to see how I built it up one piece at a time.

Note I’m not convinced this is the most elegant way to do this, but it is a full working example of a simple component based game – which are in rare supply! :]

As I mentioned earlier, if any of you have ideas for how to make this project better, feel free to update the project and send me an updated version! As mentioned earlier, I’ll post any alternative versions here so others can learn and benefit.

I hope this article has helped you learn the basics about component based architecture and get some ideas for how you might like to approach it with your games. Remember, the key is to prefer composition over inhertance – how you make that happen is a matter of style and preference! :]

Huge thanks to Adam Martin for tech reviewing this tutorial.

If you have any comments, questions, or suggestions for this tutorial, please join the forum discussion below!


This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer.

Ray Wenderlich

Ray is an indie software developer currently focusing on iPhone and iPad development, and the administrator of this site. He’s the founder of a small iPhone development studio called Razeware, and is passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

User Comments

53 Comments

[ 1 , 2 , 3 , 4 ]
  • 1. Component as a Container of other objects?


    Component as a container of other components. Indeed.. There can be generic and specialized containers that are also components that can be added to other containers. In my efforts and others a container component is called a component manager.

    Regarding inventory storage this can be achieved in many ways. One could store a typed collection (in my efforts I have a custom collections API where the collections are also data components). One could create a custom data component all call it Bag or Inventory or whatever and provide whatever mechanism makes sense to store items. Particular to my efforts one can also store generically multiple items under a given type with generic add / remove method and ability to retrieve an iterator for that type. In my efforts components are stored by meta-type and type. One could directly store components with a meta-type of InventoryItem and retrieve them independently or again in my efforts retrieve an iterator over a particular meta-type. There are many ways to solve this problem implicitly and this does depend partly on what the component architecture API supports.

    2. How could components be used for e.g. A* pathfinding on a grid?


    Good question and this raises general concerns about component architectures. You want to store your data in the most effective way to implement whatever algorithm is necessary. Often this means putting as much data together in one component as necessary in order to not have to reference multiple separate components to complete a task. It is not particularly effective to implicitly retrieve / modify / add / remove many components in a tight loop one by one especially in algorithms that are intensive. A* is not outright brute force, but most likely needs to be as fast as possible because a lot of other things are going on in any given game. With a general purpose component architecture you'd probably want to create a "GridData" component that stores a publicly accessible grid in a multidimensional array and an "AStarSystem" That retrieves the GridData component and directly accesses the grid data.

    I've also recently played around making a specialized ComponentManager for storing grid like data; IE "GridManager".. This was in response to Paul Gestwicki's post (there is some sample Java source code just not component oriented per se, but discussion about how to make it component oriented at the end of the comments):
    http://paulgestwicki.blogspot.com/2013/04/java-generics-trivia-tale-of-syntax.html
    MichaelEGR
  • There might be a bug about AIState.

    The "enter" function of the first AIState that added to the AIComponent will never get called.
    icecastle
  • I'm not sure about implementing the EntityManager in C++.
    I have to create
    Code: Select all
    std::vector<unsigned int> _entities

    and
    Code: Select all
    std::map<std::string, std::map<unsigned int, Component>> _componentByClass

    Am I right?
    I'm not sure of the data structure of _componentByClass because I don't know much about the Objective-C
    aratnon
  • One question I have regarding the entity system is, what happens it you need different behaviour for one set of entities, such as the way they move? For example, the movement system would find all entities that have the required components and then move them using the same logic. But what happens if some of those entities need to move in a different way?

    Do you need to add more member variables to your components, and therefore bloat the movement system to be able to handle different movement logic? Or, would you create a new component for the entities, and thus another "type" of movement system to handle those entities? Either way, it doesn't feel right, unless I'm missing something?
    wilberolive
  • Don't you think you are breaking a lot of encapsulation with your entityManager class? It look like that every System class can do whatever logic they want on every entities they like.
    gamugamu
  • So useful to me, this tutorial help me so much when i development "Battle City 2D" game by this architecture. this is harder than object object oriented, you can refer to my game at google play store
    winner15689
  • I implemented an ECS on top of cocos2d-x recently. My first blog post about it : http://www.chaonis.com/2014/02/entity-s ... -x-part-1/
    simonracz
  • I'm trying to apply this same component architecture to a modified version of PestControl but I'm running into a design issue. I have the human player move when and to the location touched on screen but want my ai players to move differently.

    The ai player should just wander around freely until a human player comes in range causing the ai player to chase the human. The ai player will chase the human until the human is out of range again and then it goes back to wandering around. I have the gist of getting those actions working but I'm getting caught up in componentizing the two.

    Since they are very different movement systems would you recommend I have two components and two systems, one for a human and one for a ai? Or would you try to make the one move component/system work for both? If the latter, what is the best route to take to combine?

    Any advice would be greatly appreciated!
    ches
[ 1 , 2 , 3 , 4 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in September: iOS 8 App Extensions!

Sign Up - September

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    Loading ... Loading ...

Our Books

Our Team

Tutorial Team

  • Toby Stephens

... 50 total!

Update Team

  • Riccardo D'Antoni

... 14 total!

Editorial Team

  • Alexis Gallagher

... 23 total!

Code Team

  • Orta Therox

... 3 total!

Translation Team

  • Miguel Angel
  • Victor Grushevskiy

... 33 total!

Subject Matter Experts

... 4 total!