Component-based Entities using Properties

Started by
23 comments, last by owl 14 years, 5 months ago
I forgot to mention this in my last post, but since you also can listen for properties changing, and this might be something you'd want to do, you could also listen in the Graphics component for when the position property changes, and only do the graphical updating when this occures. Now the physics component could change the position, and neither component could care less about the other. I would imagine though that you'd want to run your graphics component once per frame and not only when position changes.
Advertisement
Trefall, your system is very much like what I've been cobbling together slowly for Solariad. I mixed this year's GDC Canada slides about the game Prototype (which supports very generic component interfaces, only onUpdate() and onMessage(), much like your own system) with the property design pattern of Wyvern, an online rpg that was released eight years ago. The result was just like yours - a component-based approach with a centralized per-entity blackboard for storing properties. Your system is already far along, but you might want to check out Stevey's thoughts about how to make the property pattern more efficient. Two more takeaways from that discussion are support for transient properties as well as property inheritance.

I think what it boils down to is leaving the magical world of type-safety, efficiency, and explicit dependencies for one that is extremely data-driven, modder-friendly, and loosely coupled. This is probably the gaming equivalent of Web 2.0 - creating games that are easily moddable and extendable by the userbase. Even small games can benefit from this approach, as it allows for a community to form around your game and extends the shelf life. With a traditional game, once I'm done playing it, on the shelf it goes, collecting dust.

You should definitely write this up into an article - there is far too little out there on the nitty gritty details of implementing a component system, although I did see two very bare-bones code bases before (one in C# and one in C++). Most discussions and articles focus on a very high-level perspective and omit the talk about how any such system is wired up and used properly.

I'm not sure I understood the logic behind adding behaviors as separate classes to the components. You mention coroutines, so I guess the point is to parallelize them?
Quote:zyrolasting
We programmers love to experiment, but please note your engine isn't doing anything C++ can't do. We have all the means to declare and define what we want to begin with.

The component system allows flexibility and code reuse beyond any straightforward C++. Primarily it enables dynamic change of entities' properties/traits, which in turn simplifies the whole entity system. I.e. when you want to suspend user input and drive an entity programmatically for a while, in C++ you have to write this functionality into the object beforehand. With dynamic adding of components it's a matter of swapping a component for a while.
Without a component based approach you'll end up with elephant-sized classes and duplication of code everywhere, code that is not standarized in any way.
Components give clean separation of aspects, better testablility, and easier maintenance. So I very strongly oppose a statement "your engine isn't doing anything C++ can't do". It does.

@Trefall
I very much like the idea of entity-owned properties. But I don't agree that component dependencies are a bad idea. Some of them will be designed to depend on others. The idea of hard coding the most important components into the entity, like in the GraphicsComponent/PhysicsComponent example, would prevent i.e. swapping of the input between user/cpu I mentioned earlier. It can be done your way, but again - you have to design your classes for it from the start, predicting what you might need in *every* entity, and put it into the generic empty entity - this is not good.
I thought about component dependencies before, and it seems to me that it would be reasonable to allow components to form DAGs (directed acyclic graphs) of dependency, sorted topologically. One could also use something like MEF (Managed Extensibility Framework). Anyway, I've implemented a component based entity system for my game before, and I was badly in need of dependencies. In my another project, DAGs worked well for it.
Trefall, regarding the properties, I assume you're using a tree map or a hash map in managing it. Is the performance hit by using those negligible (especially for properties like position, orientation angles which get updated/accessed every frame)?

What are the specs of the machine where you tried this framework?

I have my own component based entity system that allows dependencies among them (I just make sure there are no circular dependencies). Although, it performs well in release build, the debug build is real slow (it matters to me). It is partly caused by accessing components from the component map:

// usageentity->getComponent<WorldObjectComponent>()->getPosition();// getComponent() definitiontemplate <typename ComponentType>ComponentType* getComponent() {  // I use Type instances as keys to my component map  Type typeInfo = Type::getType( ComponentType );  return dynamic_cast<ComponentType*>( this->componentMap.get( typeInfo ) );}
C++ RTTI is quite slow, at least if you're using it that often. Maybe some other identifier would be more effective (eg. an enum). And since your function has a ComponentType template, why not just use static_cast instead? It's not like you're checking for a NULL return anyway...

You might also consider just using a std::vector of components instead of any sort of map. In fact, I would explicitly recommend it in this case. If you have 10 components on average, then the map lookup will make 3 or 4 comparisons on average whereas the vector will make 5 on average, but the vector contents will be linear in memory and far more cache-friendly than the map which has to jump from one map node to another.
Quote:Original post by Kylotan

You might also consider just using a std::vector of components instead of any sort of map. In fact, I would explicitly recommend it in this case. If you have 10 components on average, then the map lookup will make 3 or 4 comparisons on average whereas the vector will make 5 on average, but the vector contents will be linear in memory and far more cache-friendly than the map which has to jump from one map node to another.


I have read of implementation where it would upon initiation use a vector by default and after certain treshold (20) switch to using map.

Quote:Original post by Kylotan
C++ RTTI is quite slow, at least if you're using it that often. Maybe some other identifier would be more effective (eg. an enum).


I don't use enums because it transforms my Entity class to a templated one (I don't wanna use int to generalize enums):
template <typename ComponentKeyType> // use the enum type a certain game uses hereclass Entity {  void addComponent( const ComponentKeyType& key, IComponent* const component );  // casting will be done by callee  IComponent* getComponent( const ComponentKeyType& key );};


My framework is interface based (I don't use function pointers or functors) so an change from a non-templated class to templated one will also require changes to interfaces using that class (there are many of them in my case). I'd say introducing a template class is viral.

Moreover, I won't be able to enforce a certain component type to be related to only one key as good as the current system.
someEntity->addComponent( GameComponentType::Movement, movementComponent );// I can make this mistakeanotherEntity->addComponent( GameComponentType::Movement, physicsComponent );


Unless you have better suggestions for these issues. I'd like to hear them.

But I'd really like to know the performance impact of accessing/updating properties that are attached to entities (outside of components). I like the idea and I'd like to try it in my next game.
Quote:Original post by mazelle
I don't use enums because it transforms my Entity class to a templated one (I don't wanna use int to generalize enums)

There's no reason templates have to come into your entity class. All I'm talking about is literally a component type ID.

// getComponent() definition, overloaded once per concrete component typetemplate <typename ComponentType>ComponentType* getComponent() {    // Use the class's static id value which you've defined as something unique    IComponent* c = this->componentMap.get(ComponentType::id);    return static_cast<ComponentType*>(c);}


There any number of ways you can hard-code unique values into each component type and then write simple wrappers to access them. They're not as elegant as having C++ generate its own automatic unique values via RTTI but they're almost all faster at run-time. Just choose what is most important to you.

Quote:But I'd really like to know the performance impact of accessing/updating properties that are attached to entities (outside of components). I like the idea and I'd like to try it in my next game.

I doubt it makes any significant difference. What's more important is likely to be the structures you're using to hold the components or properties.
Sorry that I haven't replied here for a while. I've been really busy, and I still am. But I just wanted to add a few comments.

ComponentContainer holds a vector of components.
PropertyContainer holds a map of properties.

So yes, we have a map in there, but the trick here is that we reference to the properties in the components, only seldomly will you look them up in the map, so on a general basis, you're speaking directly to the property reference.

One could surely use an integer instead of a string, but with our goal to script most components, using a hardcoded enum is not an option for us, thus the string was chosen.

I've piled up quite a few modifications and cleanups to the code in my game project now, hopefully I'll find time soon to add in those things to the open source version soon.

I'll be back with more thorough answers and discussion at a later point! Thank you all for keeping thisone alive and sharing your opinions!
Just commited a fairly large update to the component system. It breaks backwards compatibility, but holds a lot of cleanup and also adds a type serializer, written by Kenneth Gangstø aka Sphair, which I'm doing this component system with (we use it in our game project). It gets rid of the PropertyManager and EntityManager, so updating over properties now goes through your game object (was done over the PropertyManager before). EntityManager wasn't doing much, so no reason to keep it in there.

This topic is closed to new replies.

Advertisement