Thoughts on this component system for a game engine.

Started by
29 comments, last by Antheus 14 years, 10 months ago
Or instead of doing messy code like that you could make an easy to use component system and just call:

PositionSystem.Translate("EntityName", 1, 2, 3);

or

PositionSystem.Translate(PositionID, 1, 2, 3);


Simple right? And probably in an ideal component system, all you would need to do is add some components and call functions like these from within your code or scripts.
Check out my new blog: http://beefmachinegames.wordpress.com
Advertisement
Quote:Original post by hymerman
What if not all entities have RenderComponents? You've wasted a pointer. What if you have lots and lots of component types? You've wasted lots and lots of pointers. What if you want an entity to have multiple components of the same type? Make it an array? What if you want to add and remove these dynamically? Tough bits.


You should design your systems so that each component doesn't depend on a huge number of other components. If your components has more than a few optional subcomponents, I'd argue that it's time to refactor.

Yes, if you're going to be restructuring your component hierarchy at run-time, you'll need that extra layer of abstraction. However many people think "component system" and immediately go toward a Dungeon Siege or Game Programming Gems solution, when classes support composition quite well.
Anthony Umfer
I'm using a component based entity system as well but a liitle bit different.

Here a entity is only a ID for the programmer and internally its a component container.
Components can when they belong to a entity, register for update and input calls.
Components talk to each other across a event system driven by the entities.

CompEntity entity;
entity->FireEvent("changePosition", senderEntity(can be NULL), DATA);

DATA can be of any type i use something similiar to boost::any but its not boost::any.

Components are attached and deattached via the ComponentManager.

You can access Entities by id. Every Entity has a unique id so no look up tables just simply array calls. Same goes for ComponentQuerries. Components have TypeIDs and names to create them on the fly.
I scanned through your source code Wizecoder, and I have a couple questions/thoughts:

1) How are you planning on integrating a scene graph? This has been something I have struggled with to the point that I'm thinking of forgetting hierarchical culling based on relations (which is typical of a scene graph) and just being happy with the spatial culling of an octree like you apparently going for.

2) Overall I really like your design, and it goes along with how I pictured things. There is one issue that I am wondering if there isn't a better solution to and that is, for example, how your render system calls BMGPositionable::GetPosition(). This introduces a dependency of the render system on the actual data lying in a component that could possibly be used by another system, like physics. When you start multi-threading, you will assumably be assigning a thread to each system. Now you will have a problem since both the render system and physics system will be operating on the same shared data.

The only solution I see to 2) is to have redundant data stored in components. For example, both the render and physics components would need to have position data stored in them. Then, when the actual position component changes, it could broadcast a "PositionChanged" message that will modify the position data in both the physics and render components. The question is, does the separation between systems that this will achieve justify the data redundancy?

#1. I was thinking that once the scene graph starts being implemented then there could just be a new component type called HeirarchyComponent or something like that. Then a HeirarchyComponent could just be added to the Scene Graph and the RenderComponent could be added to the Octree.

#2. I haven't thought to heavily about multi-threading yet but you could probably just protect some of the data by locking it. This will be something i will probably think more about a while from now. And about the message system you mentioned, i am thinking i might do something like that. When one of the functions like PositionSystem->Move("EntityName", 1, 2, 3); is called the Position System would send a message to the necessary systems which will then update there copy of the data(like the Graphics System changing the node locations in the Octree).
Check out my new blog: http://beefmachinegames.wordpress.com
Components on your entity sounds fine, just maintain a hash_map or vector on the entity so you can loop through and update your components. Also, I'd try and make a nice interface to communicate between components. Having to access the vector directly and find the component you want, to make a function call would be tough. A messaging system to be able to send a message and let the component that needs to handle it might be the way to go. Especially if you want one message to be handled by multiple components.
several problems i c here.

1. the system has a incrediblely low cohesion. everything is tightly coupled together.

2. this issue is somewhat introduced coz of the low cohesion. ur system will have a central bottle neck at the Engine class or Manager class. this applies to all manager oriented systems, which ultimately will run into scalability problems.

3. if u r trying to make a high quality engine, i would suggest u redesign and probably start over by studying some of the open source or cheap commercial engines. it seems like there isnt really a design here yet.

4. multithreaded architecture. well since the design is very incomplete, i c it as being almost impossible to scale up to a multithreaded level. without multithreading on current hardware (pc/mac or consoles), the engine will not perform.

its a good start, but theres a LOT to be done before u can code anything. try to study some popular engines before u dive into engine development. make a medium sized game using a popular engine would be a good way to learn.

good luck
Quote:Original post by neakor
several problems i c here.

1. the system has a incrediblely low cohesion. everything is tightly coupled together.

good luck


This doesn't make sense. Cohesion is proportional to coupling. What Wizecoder has going right now is a great start. I would disagree with your assessment that he needs to start over from scratch.

Quote:Original post by Wizecoder
I haven't thought to heavily about multi-threading yet but you could probably just protect some of the data by locking it.
good luck


I agree, a little bit of locking would probably do the trick and is likely the best solution. I do however really like the idea of %100 of the communication via message-passing for networking ease and multithreading. Shared locking just seems to go against my "ideal" view of how a component design should be. Yes I know this may not be realistic, but one can dream can't he? In fact... I think I am going to give it a go tomorrow and see what a strictly message-passing engine would look like.
Quote:Original post by Matt Sloan
Quote:Original post by Wizecoder
I haven't thought to heavily about multi-threading yet but you could probably just protect some of the data by locking it.
good luck
I agree, a little bit of locking would probably do the trick and is likely the best solution. I do however really like the idea of %100 of the communication via message-passing for networking ease and multithreading. Shared locking just seems to go against my "ideal" view of how a component design should be. Yes I know this may not be realistic, but one can dream can't he? In fact... I think I am going to give it a go tomorrow and see what a strictly message-passing engine would look like.
Done naively, that 'little bit of locking' just axed any performance gain multi-threading on this level would have gained.

An implicit message passing system works pretty well - I find explicit messages to be too expensive for inner loop calculations. By implicit I mean that each piece of data is visible to all sub-systems, but many only be written to by exactly one (predefined) sub-system.

If you can enforce that one constraint in your design, you can freely spin off sub-systems into their own threads, with only minimal locking/buffering required to maintain consistency.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by swiftcoder
An implicit message passing system works pretty well - I find explicit messages to be too expensive for inner loop calculations. By implicit I mean that each piece of data is visible to all sub-systems, but many only be written to by exactly one (predefined) sub-system.


This is the other option I was thinking about, and seems to be one of the more popular routes.

Few questions..

1) Tell me if I have your implicit system understood based on this example. The position component contains data that needs to be read by both render and physics components. When physics wants to change position data, it sends out a message that is handled by the position system. The position system in turn locks the data, makes changes, then releases the lock. If the renderer wants to read that position data it would do so via a const Vec3& getPos() function which would be accessed through entity properties so you end up with no coupling between render and position systems.
How does that sound?

2) How inefficient did you find explicit message passing to be exactly? Is there anyway it could be viable even if it was slower than implicit?

This topic is closed to new replies.

Advertisement