Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Yesterday, I've read a presentation from GDC Canada about Attribute / Behaviour entity system and I think it's pretty great. However, I'm not sure how to use it practially, not just in theory. First of all, I'll quickly explain you how this system works.


Each game entity (game object) is composed of attributes (= data, which can be accessed by behaviours, but also by 'external code') and behaviours (= logic, which contain OnUpdate() and OnMessage()). So, for example, in a Breakout clone, each brick would be composed of (example!): PositionAttribute, ColorAttribute, HealthAttribute, RenderableBehaviour, HitBehaviour. The last one could look like this (it's just a non-working example written in C#):

void OnMessage(Message m)
{
    if (m is CollisionMessage) // CollisionMessage is inherited from Message
    {
        Entity otherEntity = m.CollidedWith; // Entity CollisionMessage.CollidedWith
        if (otherEntity.Type = EntityType.Ball) // Collided with ball
        {
            int brickHealth = GetAttribute<int>(Attribute.Health); // owner's attribute
            brickHealth -= otherEntity.GetAttribute<int>(Attribute.DamageImpact);
            SetAttribute<int>(Attribute.Health, brickHealth); // owner's attribute

            // If health is <= 0, "destroy" the brick
            if (brickHealth <= 0)
                SetAttribute<bool>(Attribute.Alive, false);
        }
    }
    else if (m is AttributeChangedMessage) // Some attribute has been changed 'externally'
    {
        if (m.Attribute == Attribute.Health)
        {
            // If health is <= 0, "destroy" the brick
            if (brickHealth <= 0)
                SetAttribute<bool>(Attribute.Alive, false);
        }
    }
}

If you're interested in this system, you can read more here (.ppt).


My question is related to this system, but generally every component based entity system. I've never seen how any of these really work in real computer games, because I can't find any good examples and if I find one, it's not documented, there are no comments and so I don't understand it.

So, what do I want to ask? How to design the behaviours (components). I've read here, on GameDev SE, that the most common mistake is to make many components and simply, "make everything a component". I've read that it's suggested to not do the rendering in a component, but do it outside of it (so instead of RenderableBehaviour, it should maybe be RenderableAttribute, and if an entity has RenderableAttribute set to true, then Renderer (class not related to components, but to the engine itself) should draw it on screen?).

But, how about the behaviours / components? Let's say that I've got a level, and in the level, there's a Entity button, Entity doors and Entity player. When player collides with the button (it's a floor button, which is toggled by pressure), it's pressed. When the button gets pressed, it opens the doors. Well, now how to do it?

I've come up with something like this: the player has got CollisionBehaviour, which checks if player collides with something. If he collides with a button, it sends a CollisionMessage to the button entity. The message will contain all necessary informations: who collided with the button. The button has got ToggleableBehaviour, which will receive CollisionMessage. It'll check who did it collide with and if the weight of that entity is big enough to toggle the button, the button gets toggled. Now, it sets the ToggledAttribute of the button to true. Alright, but what now?

Should the button send another message to all other objects to tell them that it has been toggled? I think that if I did everything like this, I'd have thousands of messages and it'd get pretty messy. So maybe this is better: the doors constantly check if the button which is linked to them is pressed or not, and changes its OpenedAttribute accordingly. But then it means that the doors' OnUpdate() method will be constantly doing something (is it really a problem?).

And the second problem: what if I have more kinds of buttons. One is pressed by pressure, second one is toggled by shoting at it, third one is toggled if water is poured on it, etc. This means that I'll have to have different behaviours, something like this:

Behaviour -> ToggleableBehaviour -> ToggleOnPressureBehaviour
                                 -> ToggleOnShotBehaviour
                                 -> ToggleOnWaterBehaviour

Is this how real games work or am I just stupid? Maybe I could have just one ToggleableBehaviour and it'll behave according to the ButtonTypeAttribute. So if it's a ButtonType.Pressure, it does this, if it's a ButtonType.Shot, it does something else...

So what do I want? I'd like to ask you if I'm doing it right, or I'm just stupid and I didn't understand the point of components. I didn't find any good example of how do the components really work in games, I found just some tutorials describing how to make the component system, but not how to use it.

share|improve this question
13  
+1 great question and welcome to gamedev.se. –  ashes999 Feb 12 '12 at 11:59
add comment

1 Answer 1

up vote 38 down vote accepted

Components are great, but it can take some time to find a solution that feels good to you. Don't worry, you'll get there. :)

Organizing components

You're pretty much on the right track, I'd say. I'll try to describe the solution in reverse, starting with the door and ending with the switches. My implementation makes heavy use of events; below I describe how you can use events more efficiently so they don't become a problem.

If you have a mechanism for connecting entities between them, I'd have the switch directly notify the door that it has been pressed, then the door can decide what to do.

If you can't connect entities, your solution is pretty close to what I'd do. I'd have the door listen for a generic event (SwitchActivatedEvent, maybe). When the switches get activated, they post this event.

If you have more than one type of switch, I'd have PressureToggle, WaterToggle and a ShotToggle behaviors too, but I'm not sure the base ToggleableBehaviour is any good, so I'd do away with that (unless, of course, you have a good reason for keeping it).

Behaviour -> ToggleOnPressureBehaviour
          -> ToggleOnShotBehaviour
          -> ToggleOnWaterBehaviour

Efficient event handling

As for worrying that there's too many events flying around, there's one thing you could do. Instead of having every component be notified of every single event that occurs, then have the component check if it's the right type of event, here's a different mechanism...

You can have an EventDispatcher with a subscribe method that looks something like this (pseudocode):

EventDispatcher.subscribe(event_type, function)

Then, when you post an event, the dispatcher checks its type and only notifies those functions that have subscribed to that particular type of event. You can implement this as a map that associates types of events with lists of functions.

This way, the system is significantly more efficient: there's a lot less function calls per event, and components can be sure that they received the right type of event and not have to double check.

I've posted a simple implementation of this some time ago on StackOverflow. It's written in Python, but maybe it can still help you:
http://stackoverflow.com/a/7294148/627005

That implementation is quite generic: it works with any kind of function, not just functions from components. If you don't need that, instead of function, you could have a behavior parameter in your subscribe method — the behavior instance that needs to be notified.

Attributes and behaviors

I've come to use attributes and behaviors myself, instead of plain old components. However, from your description of how you'd use the system in a Breakout game, I think you're overdoing it.

I use attributes only when two behaviors need access to the same data. The attribute helps keep the behaviors separate and the dependencies between components (be they attribute or behaviors) do not become entangled, because they follow very simple and clear rules:

  • Attributes do not use any other components (neither other attributes, nor behaviors), they are self sufficient.

  • Behaviors do not use or know about other behaviors. They only know about some of the attributes (those that they strictly need).

When some data is only needed by one and only one of the behaviors, I see no reason to put it in an attribute, I let the behavior hold it.


@heishe's comment

Wouldn't that problem occur with normal components as well?

Anyway, I don't have to check event types because every function is sure to receive the right type of event, always.

Also, the behaviors' dependencies (ie. the attributes that they need) are resolved on construction, so you don't have to go looking for attributes every on every update.

And lastly, I use Python for my game logic code (the engine is in C++, though), so there's no need for casting. Python does its duck-typing thing and everything works fine. But even if I didn't use a language with duck-typing, I'd do this (simplified example):

class SomeBehavior
{
  public:
    SomeBehavior(std::map<std::string, Attribute*> attribs, EventDispatcher* events)
        // For the purposes of this example, I'll assume that the attributes I
        // receive are the right ones. 
        : health_(static_cast<HealthAttribute*>(attribs["health"])),
          armor_(static_cast<ArmorAttribute*>(attribs["armor"]))
    {
        // Boost's polymorphic_downcast would probably be more secure than
        // a static_cast here, but nonetheless...
        // Also, I'd probably use some smart pointers instead of plain
        // old C pointers for the attributes.

        // This is how I'd subscribe a function to a certain type of event.
        // The dispatcher returns a `Subscription` object; the subscription 
        // is alive for as long this object is alive.
        subscription_ = events->subscribe(event::type<DamageEvent>(),
            std::bind(&SomeBehavior::onDamageEvent, this, _1));
    }

    void onDamageEvent(std::shared_ptr<Event> e)
    {
        DamageEvent* damage = boost::polymorphic_downcast<DamageEvent*>(e.get());
        // Simplistic and incorrect formula: health = health - damage + armor
        health_->value(health_->value() - damage->amount() + armor_->protection());
    }

    void update(boost::chrono::duration timePassed)
    {
        // Behaviors also have an `update` function, just like
        // traditional components.
    }

  private:
    HealthAttribute* health_;
    ArmorAttribute* armor_;
    EventDispatcher::Subscription subscription_;
};

Unlike behaviors, attributes don't have any update function — they don't need to, their purpose is to hold data, not to perform complex game logic.

You can still have your attributes perform some simple logic. In this example, a HealthAttribute might ensure that 0 <= value <= max_health is always true. It can also send a HealthCriticalEvent to other components of the same entity when it drops below, say, 25 percent, but it can't perform logic any more complex than that.


Example of an attribute class:

class HealthAttribute : public EntityAttribute
{
  public:
    HealthAttribute(Entity* entity, double max, double critical)
        : max_(max), critical_(critical), current_(max)
    { }

    double value() const {
        return current_;
    }    

    void value(double val)
    {
        // Ensure that 0 <= current <= max 
        if (0 <= val && val <= max_)
            current_ = val;

        // Notify other components belonging to this entity that
        // health is too low.
        if (current_ <= critical_) {
            auto ev = std::shared_ptr<Event>(new HealthCriticalEvent())
            entity_->events().post(ev)
        }
    }

  private:
    double current_, max_, critical_;
};
share|improve this answer
    
Thank you! This is exactly an asnwer I wanted. I also like your idea of EventDispatcher better than simple message passing to all entities. Now, to the last thing you told me: you basically say that Health and DamageImpact don't have to be attributes in this example. So, instead of attributes, they'd be just private variables of the behaviours? That means, that the "DamageImpact" would get passed thru the event? For example EventArgs.DamageImpact? That sounds good... But if I wanted the brick to change colour according to its health, then Health would have to be an attribute, right? Thank you! –  TomsonTom Feb 12 '12 at 13:37
2  
@TomsonTom Yes, that's it. Having the events hold whatever data the listeners need to know is a very good solution. –  Paul Manta Feb 12 '12 at 13:43
3  
This is a great answer! (as is your pdf) -- When you have a chance, could you elaborate a little on how you handle rendering with this system? This attribute/behavior model is completely new to me, but very intriguing. –  Michael Feb 12 '12 at 16:46
1  
@TomsonTom About rendering, see the answer I gave to Michael. As for collisions, I personally took a shortcut. I used a library called Box2D which is pretty easy to use and handles collisions much better than I could. But I don't use the library directly in my game logic code. Every Entity has an EntityBody, which abstracts away all the ugly bits. Behaviors can then read the position from the EntityBody, apply forces to it, use the joints and motors the body has, etc. Having such a high fidelity physics simulation like Box2D certainly brings new challenges, but they are quite fun, imo. –  Paul Manta Feb 12 '12 at 17:11
1  
@thelinuxlich So you're the developer of Artemis! :D I've seen the Component/ System scheme referenced a few times on the boards. Our implementations indeed have quite a few similarities. –  Paul Manta Feb 14 '12 at 20:03
show 24 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.