10
\$\begingroup\$

In order to get components to be able to update every frame (and leave this functionality out of components that don't need to) I got the idea to make an UpdateComponent component. Other components like MovableComponent (which holds velocity) would inherit from the IUpdatable abstract class. This forces MovableComponent to implement an Update(gametime dt) method and another RegisterWithUpdater() that gives UpdateComponent a pointer to the MovableComponent. Many components could do this and then UpdateComponent could call all of their Update(gametime dt) methods without having to care about who or what they are.

My questions are:

  1. Does this seem like anything that is normal or used by anyone? I cannot find anything on the subject.
  2. How could I maintain an order to the components like physics then position change? Is this even necessary?
  3. What are other ways of insuring that components that should be process every frame are in fact processed?

EDIT
I think I will be concidering how to give the entity manager a list of types that are updatable. Then ALL of the components of that type can update rather than managing it per entity (which are just indexes in my system anyway).

Still. My questions remain valid to me. I don't know if this is reasonabl/normal, or what others tend to do.

Also, the people at Insomniac are awesome! /EDIT

Boiled down code for previous example:

class IUpdatable
{
public:
    virtual void Update(float dt) = 0;
protected:
    virtual void RegisterAsUpdatable() = 0;
};

class Component
{
    ...
};

class MovableComponent: public Component, public IUpdatable
{
public:
    ...
    virtual void Update(float dt);
private:
    ...
    virtual void RegisterWithUpdater();
};

class UpdateComponent: public Component
{
public:
    ...
    void UpdateAll();
    void RegisterUpdatable(Component* component);
    void RemoveUpdatable(Component* component);
private:
    ...
    std::set<Component*> updatables_;
};
\$\endgroup\$
5
  • \$\begingroup\$ Do you have an example for a component that is not updatable? That seems pretty useless. Put the update function as a virtual function into the component. Then just update all components in the entity, no need for the "UpdateComponent" \$\endgroup\$ Aug 17, 2011 at 19:12
  • \$\begingroup\$ Some components are more like data holders. Like PositionComponent. Many objects can have a position, but if they are stationary, which could be a majority, all those extra virtual calls could build up. Or say a HealthComponent. That doesn't need to do anything every frame, just get modified when it needs to. Maybe the overhead isn't that bad but my UpdateComponent was an attempt to not have Update() in EVERY component. \$\endgroup\$
    – ptpaterson
    Aug 17, 2011 at 19:18
  • 5
    \$\begingroup\$ A component that only holds data and no functionality to change it over time is by definition not a component. There must be some functionally in the component that changes over time, hence needs an update function. If your component does not need an update function, then you know its not a component and you should rethink the design. An update function in the health component makes sense, for instance you want your NPC to heal from damage over some time. \$\endgroup\$ Aug 17, 2011 at 20:01
  • \$\begingroup\$ @Maik My point was NOT that are components that will never/ can never change. I agree that's silly. They just don't need to update every frame, rather be notified to change their info when necessary. In the instance of the health over time, there would be a health bonus component that DOES have an update. I don't think there is any reason to combine the two. \$\endgroup\$
    – ptpaterson
    Aug 17, 2011 at 20:12
  • \$\begingroup\$ I'd like to also note that the code I posted only has what is needed to explain the UpdateComponent concept. It excludes all other form of communication between components. \$\endgroup\$
    – ptpaterson
    Aug 17, 2011 at 20:16

3 Answers 3

16
\$\begingroup\$

One of the primary benefits of a component system is the ability to take advantage of caching patterns - good icache and prediction because you run the same code over and over, good dcache because you can allocate the objects in homogeneous pools and because the vtables, if any, stay hot.

The way you have structured your components, this advantage disappears completely, and in fact can become a performance liability compared to an inheritance-based system, as you're making far more virtual calls and more objects with vtables.

What you should be doing is storing pools per type, and iterating each type independently to perform updates.

Does this seem like anything that is normal or used by anyone? I cannot find anything on the subject.

It's not as common in large games because it is not advantageous. It's common in plenty of games, but it's not technically interesting, so no one writes about it.

How could I maintain an order to the components like physics then position change? Is this even necessary?

Code in languages like C++ has a natural way to order execution: type the statements in that order.

for (PhysicsComponent *c : physics_components)
    c->update(dt);
for (PositionComponent *c : position_components)
    c->update(dt);

In reality, that doesn't make sense because no robust physics system is structured that way - you can't update one single physics object. Instead, the code would look more like:

physics_step();
for (PositionComponent *c : position_components)
    c->update(dt);
// Updates the position data from the physics data.
\$\endgroup\$
4
  • \$\begingroup\$ Thanks. I started this whole project with data oriented (not same as data driven) in mind, preventing cache misses and such. But once I started coding I decided just to make something that worked dot com. Turns out it made things harder on me. And for serious, that insomniac article was great! \$\endgroup\$
    – ptpaterson
    Aug 17, 2011 at 23:56
  • \$\begingroup\$ @Joe +1 for a very good answer. Although I'd like to know what an icache and dcache are. Thanks \$\endgroup\$
    – Ray Dey
    Aug 18, 2011 at 12:24
  • \$\begingroup\$ Instruction cache and data cache. Any introductory text on computer architecture should cover them. \$\endgroup\$
    – user744
    Aug 18, 2011 at 19:13
  • \$\begingroup\$ @ptpaterson: Note that there's a small mistake in the Insomniac presentation - the component class must contain its roster index, or you need a separate mapping of pool indices to roster indices. \$\endgroup\$
    – user744
    Aug 18, 2011 at 19:16
3
\$\begingroup\$

What you're talking about is reasonable and fairly common, I think. This might give you some more information.

\$\endgroup\$
1
  • \$\begingroup\$ I think that I came across that article a couple of weeks ago. I've been able to code together a few very simple versions of a component based entity system, each very different as I try different things. Trying to bring together all of the articles and examples into something I want and am able to do. thanks! \$\endgroup\$
    – ptpaterson
    Aug 19, 2011 at 1:21
0
\$\begingroup\$

I like these approaches:

Shortly: Avoid keeping update behavior within components. Components are not behavior. Behavior (including updates) can be implemented in some single-instance subsystems. That approach might also help to batch-process similar behaviors for multiple components (maybe using parallel_for or SIMD instructions on component data).

The IUpdatable idea and Update(gametime dt) method seems a bit too restrictive and introduces additional depencencies. It might be fine if you are not using subsystems approach, but if you do use them, then IUpdatable is a redundant level of hierarchy. After all, the MovingSystem should know that it must update the Location and/or Velocity components directly for all entities which have these components, so there is no need for some intermediate IUpdatable component.

But you could use Updatable component as a mechanism to skip updating some entities despite of them having Location and/or Velocity components. The Updatable component could have a bool flag which can be set to false and that would signal every Updatable-aware subsystem that this particular entity currently should not be updated (although in such context, Freezable seems to be more appropriate name for the component).

\$\endgroup\$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .