Component Based Entity Design communication between components and systems

Started by
17 comments, last by Gasimzada 10 years, 4 months ago

I have designed Component Based Entity (CBE) System and I am stuck in communicating in between components and systems (by that I mean, component-component, component-system, system-system). I communicate between components and systems through events. Each component and system registers functions per event name. There are also global events that handle rendering or capturing.

Components and systems know nothing about each other.

Here is a what I have so far:

There are 2 systems: TileLevelSystem and InputSystem. The TileLevelSystem holds a stack of entities in each tile; and the InputSystem captures the input and sends the mapped event to the systems and PlayerComponents. When a PlayerComponent added to an Entity, the entity gets automatically added to the InputSystem. When TilePositionComponent gets added to an Entity, the entity gets added to a tile in the TileLevelSystem. There is also a PassableComponent component that checks if a character can walk through an entity.

  1. If one types 'w', the input fires event "moveUp"; the fired event is sent to a the entity with the PlayerComponent
  2. The entity passes the "moveUp" event to all the components.
  3. TilePositionComponent receives it, and sends an event "updatePosition(currentX, currentY, currentX, currentY-1)" to TileLevelSystem.
  4. TileLevelSystem sends event "isPassable" to an entity in the new position (if there is one); if the event returns true, the player moves to the new position and the "updatePosition" event returns true. Then the TilePositionComponent's currentX and currentY gets updated.
  5. Then everything gets rendered.

I am currently working on picking items that are pickable. I have created two components: InventoryComponent and PickableComponent. InventoryComponent accepts event "pickItem", and PickableComponent accepts event "isPickable". There are two approaches that I have in mind.

First one is sending an event "pickItemFromCurrentTile", which gets accepted by TilePositionComponent. After TilePositionComponent does all its communications with the TileLevelSystem, the component sends a "pickItem" event to the InventoryComponent with Entity as an argument, which adds the entity to the inventory.

The second one is creating a return class for Event which holds a state; then sending "pickItemCurrentTile", which returns the entity and I add it to the pickItem.

I am writing this system to understand how CBE System flow really works. If you think my whole design is wrong, please tell me. I want to make this system very clear and flexible before I do anything else.

Thanks,

Gasim

Advertisement
Components and systems know nothing about each other.

I can understand components not knowing about systems, but I don't see why you would have systems not know about components. It is the whole point of systems to handle the processing of components relevant to those systems. Some people might argue that making things event driven in this way is better, but I'm not a big fan of event systems used in this way.

In my opinion systems will 'update' the components they are responsible for updating directly, though if you prefer your update event can accomplish the same thing. An InputSystem will update all the known InputComponents. That might include PlayerInput components(possible multiple for local multiplayer), and AIInput if you implement your AI to function through the same control interface. Each instance of PlayerInput will know about what keys/controllers are relevant to that particular player and interpret it accordingly.

There isn't really a single correct way to implement all you describe. There are a bunch of different approaches that may have pros and cons. I'll try to explain an alternative component implementation that I feel makes more sense.

Your motion appears to be implemented in full tile increments, but even in that situation I would design the component system with an implementation that is more appropriate to being used if you were using incremental motion, with a real collision system. The tile increment motion can be hidden away inside the implementation details.

For example.

PhysicsSystem - knows about and updates PhysicsComponents, resolves all physics based on velocities/forces applied to the PhysicsComponents

PhysicsSystemTile - specific implementation for your full tile locomotion, moves each object the most appropriate direction to their velocity a full tile size

PhysicsComponent - simply holds the velocity/force accumulations to be applied by the physics system and resolved with others.

PlayerInput - interprets control input and sets velocity

The benefit is of course that not only are you future proofing for the flexibility of doing a proper say box2d physics version of the game, but you are reducing the component cross chatter.

If one types 'w', the input fires event "moveUp"; the fired event is sent to a the entity with the PlayerComponent

This event is unnecessary. The specifics of which keys to interpret as which actions should be properties of the specific Input component instance. Again this reduces component chatter and leaves the game open to multiple player instances each deriving input from separate sources, ie local multiplayer.

The entity passes the "moveUp" event to all the components.

Again this is unnecessary chatter. Event broadcasting IMO is a dirty temptation of event systems. If the component knows which buttons map to which actions, it can speak to the relevant components directly.

TilePositionComponent receives it, and sends an event "updatePosition(currentX, currentY, currentX, currentY-1)" to TileLevelSystem.
TileLevelSystem sends event "isPassable" to an entity in the new position (if there is one); if the event returns true, the player moves to the new position and the "updatePosition" event returns true. Then the TilePositionComponent's currentX and currentY gets updated.
Then everything gets rendered.

These details are highly specific to your specific motion model, and would need to be wholly replaced with the introduction of a physics system. It may even be problematic to implement simple animation/lerping the entities to the center of each tile.

If instead we adopted the PhysicsSystem approach, The PlayerInput would interpret the input keys into a velocity, The PhysicsSystemTile would take that velocity and for example use the most significant direction to animate/lerp the PhysicsComponent it processes to the next valid tile in that direction. For managing crossable and uncrossable tiles, do that the same as you would if you were using a physics system. Collision masks. The PhysicsSystemTile is now the only place that has to know or care about the fact that your game operates at the tile level in terms of locomotion, and if you want to change that system in the future it becomes very simple. The most important short term win though is the reduction in component cross talk and the dependencies that introduces. The relationship between Input, Physics, and motion I think is one of the easiest aspects of a component system to keep pretty separated. Certain input ultimately gets mapped to velocity. Other input gets mapped to actions, probably on the PlayerComponent or possibly a more general CharacterComponent if you want the AI to function through the same interface. Many games, and all bots for FPS games operate like this. The AI ultimately feeds 'button presses' that maps to specific action into the game so the AI can operate through much the same code path as players. The analog in a component system might be that the AIComponent ultimately sets 'action' flags on the CharacterComponent, the same as a PlayerComponent.


character->SetAction( true, FL_ATTACK );

For item picking, I would probably again utilize some knowledge about how it would likely be implemented in a game with a physics sytem. Pickupable items would probably have PhysicsComponents that utilize a non collidable pickup trigger mask. This would make it the responsibility of the PhysicsSystem when it is updating entities based on their motion, to maintain a list of items the character entity is 'touching'. In a fleshed out game, the knowledge that a player is touching a pickable item might be reflected in rendering button prompts on the GUI. The PlayerComponent would probably have some specific knowledge about what to do/present whenever his physics component is touching pickable items.

Maybe something like this


PlayerComponent::Update()
{
    PhysicsComponent * physics = GetEntity()->GetPhysics();
    Entity * pickable = GetFirstPickable( physics->GetTouchedEntities() ); // local function that gets the first pickable in the list(may be multiple), distinguished by collision mask or presence of certain dependent component
    if ( pickable )
    {
        EnablePickupGui( pickable ); // shows a gui for the current pickable
        if ( GetInput( IN_PICKUP ) )
        {
            DisablePickupGui();
            PickupItem( pickable );
        }
    }
    else
      DisablePickupGui();
}

In my opinion systems will 'update' the components they are responsible for updating directly, though if you prefer your update event can accomplish the same thing. An InputSystem will update all the known InputComponents. That might include PlayerInput components(possible multiple for local multiplayer), and AIInput if you implement your AI to function through the same control interface. Each instance of PlayerInput will know about what keys/controllers are relevant to that particular player and interpret it accordingly.

I have one thing that I want to be sure 100%. In the system you provided, ideally, systems store components for their specific types. So, GraphicsSystem stores GraphicsComponents; PhysicsSystem will store PhysicsComponents; etc. Do I understand it correctly?

If the component knows which buttons map to which actions, it can speak to the relevant components directly.

What if we don't have the component? Is there a way to make this system modular rather than tightly integrated? Or there is always going to be integration between systems no matter what?

I like the physics system approach a a lot, even though I don't like the idea of every component being connected to each other. I need to find a way to adapt this system to my beliefs tongue.png I will post it here the moment I find it.

Thanks,

In my opinion systems will 'update' the components they are responsible for updating directly, though if you prefer your update event can accomplish the same thing. An InputSystem will update all the known InputComponents. That might include PlayerInput components(possible multiple for local multiplayer), and AIInput if you implement your AI to function through the same control interface. Each instance of PlayerInput will know about what keys/controllers are relevant to that particular player and interpret it accordingly.

I have one thing that I want to be sure 100%. In the system you provided, ideally, systems store components for their specific types. So, GraphicsSystem stores GraphicsComponents; PhysicsSystem will store PhysicsComponents; etc. Do I understand it correctly?

Yes basically. Although a system might also be responsible for managing/updating multiple component types. TriggerComponent might also be managed by the PhysicsSystem since it might involve the underlying physics system for detection of the triggering. Point being it doesn't need to be a 1 system to 1 component mapping. Some components may not even have systems, they may be book keeping or functionality components used by other components.


If the component knows which buttons map to which actions, it can speak to the relevant components directly.

What if we don't have the component? Is there a way to make this system modular rather than tightly integrated? Or there is always going to be integration between systems no matter what?

I like the physics system approach a a lot, even though I don't like the idea of every component being connected to each other. I need to find a way to adapt this system to my beliefs tongue.png I will post it here the moment I find it.

If you don't have the component to talk directly to? I would detect that during object initialization. The nature of a component system always ends up as a bunch of components with various dependencies on each other. Avoid it when you can but taken too far it can adversely affect the system. A PlayerComponent and AIComponent probably depend on a CharacterComponent, or at least some component that implements an interface of that expected type. To me this is an error condition better handled as a data validation step rather than a runtime error condition as much as possible.

In my opinion there is always going to be a coupling between components. I'm not personally very convinced that a message passing system provides much benefit. In the end you are still doing similar things. Calling a function(sending an event by name or Id), passing arguments(as event parameters), and possibly relying on a result by the handler(the return value). The key difference IMO is that with an event system that functionality is obfuscated by the generic interface required of a general purpose event system. It is opened to being handled by unexpected components in unexpected ways, maybe being restricted in what types of data it can send, unless you dynamically new event objects and pass the parameters that way and rely on casting. In the end the data contained in these events is not much less of a coupling than a direct call, other than the need to have components know of the other component, or at least know of the interface to another component family. As a function call, changing that 'contract' between the 2 components(the parameters of the data passed, and the return value) is simpler to do, as it is just a function parameter changes, and the compiler will help you avoid making mistakes at callsites, mistakes that may be easier to slip through with a general purpose event system, depending on implementation.

All that said, to event or not to event is partially a subjective judgement, so there may be other reasons to prefer that method, or a mix of events and direct communication. Most of my initial reply as far as structuring the components is still relevant whether you use direct communication or event based.


I have one thing that I want to be sure 100%. In the system you provided, ideally, systems store components for their specific types. So, GraphicsSystem stores GraphicsComponents; PhysicsSystem will store PhysicsComponents; etc. Do I understand it correctly?

As DrEvil mentioned, there is not necessarily a 1-to-1 mapping. A system might have a reference to the store for a particular component type, but not necessarily "own" it, as many systems might need access to the same components (e.g. the position component being a good example of this). In my implementation, a top level EntityManager class "owns" all the component stores, and systems get a reference to the ones they need.


In my opinion there is always going to be a coupling between components. I'm not personally very convinced that a message passing system provides much benefit. In the end you are still doing similar things. Calling a function(sending an event by name or Id), passing arguments(as event parameters), and possibly relying on a result by the handler(the return value). The key difference IMO is that with an event system that functionality is obfuscated by the generic interface required of a general purpose event system.

I definitely agree with this. If the dependency is there and necessary, best to make it explicit.

After thinking, researching, and crafting this design, I decided to use both referencing and event management. I use events when dependency is not necessary. For example, InputComponent shouldn't care if there is a component that can accept it. On the other hand, MovementComponent wouldn't work without PositionComponent. Also I use events for operations that are not "inner"; basically anything that is decorative or user related.

There are two more communications I would like to discuss: between a component and a system; and between systems. I gave systems full access to the components they are holding. What about the relationship from Component to System? Suppose MovementComponent calculated the new position and called a function mPositionComponent->movePosition(newX, newY). How should PositionComponent send the information to the TileSystem, so the Component is moved to a new tile. Currently, I am handling this problem by sending events but I am not happy about it. Should I just give direct call to the event?


As DrEvil mentioned, there is not necessarily a 1-to-1 mapping. A system might have a reference to the store for a particular component type, but not necessarily "own" it, as many systems might need access to the same components (e.g. the position component being a good example of this).

In the cases you mentioned, would you hold the entities of the used components in each system or the components? Can you please clarify why do you have the EntityManager? Why aren't Entities themselves the owners of these components?

As I described in the PhysicsComponent example, Input could set velocity on the PhysicsComponent, PhysicsSystem interprets velocity into motion and reflects the motion back onto the position, whether position is in its own component or position is part of the PhysicsComponent(which I tend to prefer). The position is where the results get stored, and where a bunch of the components of an entity will look for the position. There is no need for this value to be pushed out to other components. If you do a position component, it will likely be a dependency of a bunch of different components. Don't think about all these components as needing an updated copy of that position data, they should just grab the position through the component itself. This is largely why I prefer not to have a component simply for position/transform. It's too low of a level of a property to make a component out of IMO, and in almost all cases, it is controlled in some way by the physics system, even if moved procedurally, so I would make it part of the physics interface. It sounds like you are approaching this from a perspective of each component having a copy of the positional information.


As DrEvil mentioned, there is not necessarily a 1-to-1 mapping. A system might have a reference to the store for a particular component type, but not necessarily "own" it, as many systems might need access to the same components (e.g. the position component being a good example of this).

In the cases you mentioned, would you hold the entities of the used components in each system or the components? Can you please clarify why do you have the EntityManager? Why aren't Entities themselves the owners of these components?

I've worked with systems that allocate the components on the heap. The entities have pointers to their components, and the systems have pointers to the specific components they are responsible for updating. I've also worked with systems that allocate large contiguous arrays of components. Again the entities can access their specific components directly, but the system is set up to more efficiently update the components with contiguous access. Entities basically do own the components, as they are allocated and freed in the scope of the entity lifetime most likely. They are also usually in the system processing lists while allocated too, though there may be some implementation details such as making an entity inactive might unregister it from systems so that it doesn't get updated.

I won't speak for the other poster, but it sounds likehis EntityManager is the system you allocate your entities with that creates all the components that make up an entity and register the created components with the appropriate systems. It would also be responsible for deleting and cleaning up the entities. It assembles the component puzzle pieces into a single entity unit and hooks up the relevant processing.

I kind of have the same functionality; the difference is the PhysicsComponent is split into MovementComponent and PositionComponent, where Movement and Position work together. In the future, I would most likely merge them but I am not really concerned about it. Other components use position by just a function call. The only place I have a copy of the position is the indexes of the tiles in the TileSystem. However, I still don't understand one thing:

As I described in the PhysicsComponent example, Input could set velocity on the PhysicsComponent, PhysicsSystem interprets velocity into motion and reflects the motion back onto the position

How does PhysicsSystem interprets velocity into the motion; how does it know which velocity is changed. Or does it go through all the PhysicsComponents in a separate Update function? If yes, what I am understanding is that, you calculate all positions by something like this:


for(PhysicsComponent * component : mPhysicsComponents) {
   component->x += component->velocityX * dt;
   component->y += component->velocityY * dt;
}

But then how would you change the tile that the component is stored since you don't know the initial position of component anymore? I am guessing that's when the PhysicsSystemTile comes to play. If this system held tiles, how would it know which tile needs update? Am I missing something?


Suppose MovementComponent calculated the new position and called a function mPositionComponent->movePosition(newX, newY). How should PositionComponent send the information to the TileSystem, so the Component is moved to a new tile.

Think about it like this instead: Some code (presumably a MovementSystem or something? it doesn't really matter) modifies the position in the PositionComponent. Then, later, there is a system that iterates through the PositionComponents, calculates which tile they are in, and then updates the PhysicsTileComponent accordingly. Think about it like a series of steps where the systems manage data flow (and transform data) from one component to another on an entity.


How does PhysicsSystem interprets velocity into the motion; how does it know which velocity is changed. Or does it go through all the PhysicsComponents in a separate Update function?

Your systems need some way to say "for this FooComponent, get me the BarComponent on the same entity".

This article I wrote might be of some use:

http://www.gamedev.net/page/resources/_/technical/game-programming/case-study-bomberman-mechanics-in-an-entity-component-system-r3159

I think it does something somewhat similar to what you're looking for. I have a physics system that is responsible for moving objects at a fine resolution and yet the game is also tile-based.

I that code I have a CollisionSystem that uses a 3rd party physics library. It creates a body in that 3rd party physics library for each Physics component. In the CollisionSystem's Update method, it updates each body with the current position of the relevant component (I call this Placement) if it's dirty. Then the physics world is "stepped". Then, the positions of the bodies are sync'd back into the relevant Placement components. Note: I don't currently expose velocity in any component. Instead velocity is set by sending a message to an entity, which is then picked up by the CollisionSystem which updates the body object it allocated for that entity. I don't particularly recommend this, and would instead have velocity as a field in your physics component, like you described above.

To handle the "tiled-ness" of the game, I have a GridSystem. It runs after the CollisionSystem and maintains a coarse grid based upon each entity's position. It can respond to queries like "Give me the entities at (4,6) that have a HealthComponent", say. It currently does this through an abstract messaging system, but it would probably be better if the other systems that needed instead just asked the GridSystem explicitly (as per my previous post). I don't have a TilePositionComponent because I didn't need one. But if you did, I would imagine it would be the responsibility of the GridSystem to update the TilePositionComponent based on the position component. Again, a system basically just responsible for transforming data from one component to another.


I won't speak for the other poster, but it sounds likehis EntityManager is the system you allocate your entities with that creates all the components that make up an entity and register the created components with the appropriate systems. It would also be responsible for deleting and cleaning up the entities. It assembles the component puzzle pieces into a single entity unit and hooks up the relevant processing.

Yes, that's pretty much it. The EntityManager stores components in per-type arrays, and stores the info responsible for mapping an entity id to the particular instances of the components that make up the entity. It's "a" way to implement it, certainly not "the" way.

This topic is closed to new replies.

Advertisement