Component based architecture and messaging

Started by
18 comments, last by Reitano 10 years, 6 months ago

I just wanted to ask if you've considering not using an event/messaging system? Considering you have systems that will operate over components, and these components contain some data, why not have these "events" simply be a new component, or data in a component?

For whatever event you think an entity or component might need, you should be able to solve it using data in the component, or a specific component type.

For example, if you want to have a "collision event" why not create a collision component, and store it in the entities that are colliding. then, when you run your collision system, it will run over those entity's with the collision component.

At least consider it. I'm not saying you must use it, but I believe it to be a viable solution.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

Advertisement

In my engine, components are fully autonomous classes and do not share data with other components. For example, both the Transformation component and the RigidBody component have a vec3d position member variable. The problem is how to synchronize the components so that the position in the Transformation component is always an up-to-date copy of the RigidBody position. In turn, other components such as the MeshRenderer, Light, which all possess a position, needs a copy of the position of the Transformation component.

Often, message queues or callbacks are suggested. Instead, I use a simpler approach. All my component systems know the required dependencies and explicitly synchronize components once per frame. An example:


struct RigidBodySystem::SyncRigidBodyTransform
{
    const RigidBody* rigidBody;
    Transform*       transform;
};

RigidBodySystem::InitializeComponent(RigidBody* rigidBody, Entity* entity, Scene* scene)
{
    Transform* transform;
    if (scene->Query(entity, &transform))
    {
        // Add synchronization primitive
        this->syncPrimitives.push_back( SyncRigidBodyTransform(rigidBody, transform) )
    }
}

RigidBodySystem::DestroyComponent(RigidBody* rigidBody)
{
    RemoveSyncPrimitive(rigidBody)
}

// Called once per frame
void RigidBodySystem::Update(float dt)
{
    this->physicsEngine->Update(dt)
}

// Called every frame after Update
void RigidBodySystem::SynchronizeComponents()
{
    foreach (sync, this->syncPrimitives)
    {
        sync.transform->SetPosition(sync.rigidBody->GetPosition())
        // Orientation too...
    }
    
}

The synchronization phase is very efficient and cache friendly because the in and out components reside in consecutive memory areas and the loop does not trash the instruction cache.

An objection might be that this approach introduces coupling between classes. That is true but in my case the coupling happens at a high level. In fact, component systems like the RigidBodySystem are simple frameworks that dispatch calls to low-level libraries and these have no dependencies on other component libraries.


Instead, I use a simpler approach. All my component systems know the required dependencies and explicitly synchronize components once per frame.

That's what I do too, and I think it's a reasonable approach. It's fine for that dependency to be encapsulated in the systems.


I have edited the diagram, now I have a render manager, and a render system component. It's better I think, what I don't know is, if the waypoint system should be a system or manager. It' has notihing to do with the entity itself... Hmm....

When you say "it has nothing to do with the entity itself", it makes me wonder if you fully understand the point of an entity-component-system framework.

In my view, all game objects should be entities. Not all entities have to have the same set of components - in fact most entities will just use a few. The waypoint has nothing to do with the player entity, say. But it is its own entity. I would think you would have a waypoint entity that is comprised of a Position and Waypoint component (and a Render component?). Or possibly the Waypoint component could describe a full series of waypoints itself (that would make it less trivial for them to be rendered, but then you wouldn't need some other component/entity that is responsible for connecting waypoints together in a path.

In your diagram, I would also question the lack of an InputSystem. Surely entities will need some sort of Component that describes how they are controlled - e.g. what input they respond to. So just like you have a RenderComponent and RenderSystem, you might want an InputComponent and InputSystem in addition to your InputManager which performs more low level/platform-specific tasks.

Also, you have a box called "Logic" above your engine. What do you envision going in there? I think the bulk of game logic should be in the systems or in individual scripts attached to the entities.

I like the idea to remove the event system. The sync of the position between the components was the problem, and the reason to not remove the event system. I think I understand Reitanos approach.


In my view, all game objects should be entities. Not all entities have to have the same set of components - in fact most entities will just use a few. The waypoint has nothing to do with the player entity, say. But it is its own entity. I would think you would have a waypoint entity that is comprised of a Position and Waypoint component (and a Render component?). Or possibly the Waypoint component could describe a full series of waypoints itself (that would make it less trivial for them to be rendered, but then you wouldn't need some other component/entity that is responsible for connecting waypoints together in a path.

Yes but I want to have one place to store all waypoints and pathes to other waypoints. In my opinion every entity has it's own waypoint component. So I have much duplicate data in store...


In your diagram, I would also question the lack of an InputSystem. Surely entities will need some sort of Component that describes how they are controlled - e.g. what input they respond to. So just like you have a RenderComponent and RenderSystem, you might want an InputComponent and InputSystem in addition to your InputManager which performs more low level/platform-specific tasks.

Yes I don't have drawn the Input system, sorry for this. And how should I do this without event messaging? How could I exit the main loop?


Also, you have a box called "Logic" above your engine. What do you envision going in there? I think the bulk of game logic should be in the systems or in individual scripts attached to the entities.

E.g. I don't know where to put my money system. This I would place in the logic. Or is this another system?


E.g. I don't know where to put my money system. This I would place in the logic. Or is this another system?

What do you envision your money system doing? It's hard to give an opinion without knowing what functionality you require.


Yes I don't have drawn the Input system, sorry for this. And how should I do this without event messaging? How could I exit the main loop?

I'm sorry, I'm not really sure what you're asking here. During your update tick, your InputSystem should be able to query what key/mouse/gamepad events have taken place since the last tick.


Yes but I want to have one place to store all waypoints and pathes to other waypoints. In my opinion every entity has it's own waypoint component. So I have much duplicate data in store...

You really should avoid any duplicate data. Without knowing more about how your waypoints are created/used/modified (are they static points on a map? Is the player interacting with the game to create them? Is the AI using them to move game objects along a path?), it's hard to offer advice. You could certainly make your way-points completely separate from your entity-component-system architecture - if that makes sense. But if you find, say, that you're suddenly writing a ton of code to synchronize the game entities with your waypoint manager, then maybe it makes more sense to have them tightly integrated with your ECS.

Try listing out the various scenarios involving waypoints. For instance: "the user adds a waypoint to his character's path with the mouse", "some way-points are drawn as floating white flags", "a character following a path of waypoints is killed (what happens to the waypoints)". Then figure out a design that makes all this functionality the most straightforward and easiest to implement.


E.g. I don't know where to put my money system. This I would place in the logic. Or is this another system?

What do you envision your money system doing? It's hard to give an opinion without knowing what functionality you require.

In my game I want to set a building on an user defined place, that means he can choose, what to build on a gui, and then place it with the mouse cursor. So the money system has to check, has the user enough money, to buy this building, and if yes, to substract the defined price.


Dominik2000, on 30 Sept 2013 - 3:19 PM, said:

Yes I don't have drawn the Input system, sorry for this. And how should I do this without event messaging? How could I exit the main loop?

I'm sorry, I'm not really sure what you're asking here. During your update tick, your InputSystem should be able to query what key/mouse/gamepad events have taken place since the last tick.

My fault, dumb question.


Dominik2000, on 30 Sept 2013 - 3:19 PM, said:

Yes but I want to have one place to store all waypoints and pathes to other waypoints. In my opinion every entity has it's own waypoint component. So I have much duplicate data in store...

You really should avoid any duplicate data. Without knowing more about how your waypoints are created/used/modified (are they static points on a map? Is the player interacting with the game to create them? Is the AI using them to move game objects along a path?), it's hard to offer advice. You could certainly make your way-points completely separate from your entity-component-system architecture - if that makes sense. But if you find, say, that you're suddenly writing a ton of code to synchronize the game entities with your waypoint manager, then maybe it makes more sense to have them tightly integrated with your ECS.

Try listing out the various scenarios involving waypoints. For instance: "the user adds a waypoint to his character's path with the mouse", "some way-points are drawn as floating white flags", "a character following a path of waypoints is killed (what happens to the waypoints)". Then figure out a design that makes all this functionality the most straightforward and easiest to implement.

Waypoints will be set on placing buildings in the game (buildings can have waypoints, the user can place also streets). I haven't exactly figured out, how to save this data best. I think that I should have one data store where all the waypoints and pathes to other waypoints are stored, and the entity has a component where the waypoints, it has to drive along, are stored. This global waypoint store needs functionality to calc the best way to one defined waypoint. Maybe it's good to have also a waypoint manager where to store and a waypoint system with components to calculate the way.

Is this enough information?


In my game I want to set a building on an user defined place, that means he can choose, what to build on a gui, and then place it with the mouse cursor. So the money system has to check, has the user enough money, to buy this building, and if yes, to substract the defined price.

Checking a value and subtracting it seems like a fairly trivial operation - so if that's all it is, I don't think it merits a system. Taking a stab at it: I would think that there would be player entity that has a "PlayerInfo" component which has a Money property (among other things that are specific to player entities). The code that runs the GUI for choosing buildings would operate in part off of the PlayerInfo component. That code can just check the Money value to see which buildings are available for the player to build, etc..

Note that by "player entity", I don't mean the objects that are owned by players, but an entity that represents the player overall. For instance, if there were 2 players: human vs AI, then there would be two of these entities.


Waypoints will be set on placing buildings in the game (buildings can have waypoints, the user can place also streets). I haven't exactly figured out, how to save this data best. I think that I should have one data store where all the waypoints and pathes to other waypoints are stored, and the entity has a component where the waypoints, it has to drive along, are stored. This global waypoint store needs functionality to calc the best way to one defined waypoint. Maybe it's good to have also a waypoint manager where to store and a waypoint system with components to calculate the way.

Well I can't really suggest anything concrete from that. It sounds like a fairly complicated setup. If you are having trouble coming up with a design, you might want to try prototyping something to see where "gotchas" come up.

My advice is that of decomposing the engine in libraries designed with the principles of middlewares: fully decoupled, configurable, robust, reusable, testable. Then, build an entity/component framework at a higher level with a common API for creating, destroying, updating and synchronizing components of any type.

With that in mind, you would first develop an AI library with support for steering behaviours, waypoints, pathfinding etc. Have a look at commercial or open source AI libraries for inspiration. Among other things, the AI library is responsible for allocating, destroying, loading, saving and updating AI primitives. AI data and code, being fully decoupled, are optimal and do not suffer from the usual pitfalls of overly generic code.

Then, as part of the entity framework, you would have one component system per AI primitive: an AIAgentComponentSystem, an AIWaypointComponentSystem etc. These systems internally communicate with the AI library. They also synchronize AI components with other components like the TransformationComponent; coupling is at the entity level, not the library level.

In your case, an entity moving around the scene following a path could be configured as follows:

<entity name="player">
<component class="Transformation"/>
<component class="AIAgent"/>
<wayPoint>ID</wayPoint>
</component>
<component class="Mesh"/>
<component class="Script"/>
</entity>

The AIAgent component internally owns an AI Agent managed by the AI library. It syncs the agent position with the Transformation component which is then syncronized with the Mesh component for visualization. The Script component contains game logic and allows you to configure the pathfinding at run-time based on game events (i.e: switch pathfinding on/off or pass control to physics).

Then, if desirable, you would have an entity per AI waypoint:

<entity name="waypoint 0">
<component class="AIWayPoint">
<ID>423432423</ID>
<file>waypoint0.dat</file>
</component>
</entity>

Note that waypoints do not necessarily have to have an associated component if you do not need to manipulate them with the component API.

Please let me know if you need further clarifications as I've written this in a rush.

Hmm, this is also a interesting idea.

A little bit more clarification is needed. In my diagram the systems will be removed and the entity manager has only components? The AI libary will offer a pointer to it's managed AI Agent and this will be stored in the component?

It's not completly clear to me.

Dominik

A little bit more clarification is needed. In my diagram the systems will be removed and the entity manager has only components? The AI libary will offer a pointer to it's managed AI Agent and this will be stored in the component?

Hi Dominik, let me show you the organization of my Visual Studio solution in this case:

Projects:
- AI library: re-usable, fully decoupled and configurable, could be plugged in other engines
- Entity/component framework: the framework for the management of scenes, entities and component systems.
- AI module: an engine module which, among other tasks, initializes the AI library and adds AI component systems to the entity framework. The AI library is injected as a dependency into them.

The AI library allocates and returns instances of AI agents. In my component framework, there is no Component class (one of my key requirements) and everything can be a component so the AI agent pointer is managed directly. If you have a Component class, then you'll need an AIAgentComponent proxy that stores the AI agent pointer and dispatches calls to it.

Please let me know if you have other questions.

This topic is closed to new replies.

Advertisement