🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

ECS Implementation - How should systems communicate with eachother?

Started by
5 comments, last by Dirk Gregorius 4 years, 9 months ago

Hi there,

I'm currently implementing an ECS framework and am struggling with the concept of inter-system communication.

My ideal ECS model would consist of a single game loop with various systems containing a simple "update" function. This update function would then rip through all the relevant entities (components in my case) and perform some action (e.g. update screen, update component data, etc.)

However, I keep running into the scenario where it seems like one system should influence the action of another system. For example: Let's say I have a physics system that updates entity positions and a lifetime system that updates whether or not an entity should continue to be alive. Every game loop, I want to call PhysicsSystem.Update() to update the entity position. I then want to call LifetimeSystem.update() to check if any entities have "died". However, if an entity were to die (as determined by the lifetime system) there would need to be some clean-up action done within the physics system (I use a 3rd party physics engine that you needs to be "cleaned-up" if an entity is removed). So in this case, I need my lifetime system to be able to message my physics system to perform a clean-up action. 

I've heard of adding "event components" to entities to signal actions. For example, I could have a "collision detection system" that checks for collisions and then adds "collision components" to any entity that has been involved with a collision. A "collision handler" system would then rip through all of the collision components.

This seems to work for certain events, but it doesn't work as well for the event I described earlier. In that  scenario, the entity would have already been deleted prior to the the physics system being able to read a "death component" to perform clean-up. To solve this problem, it seems like it would make more sense to implement a "death system" that would perform all clean-up associated with an entity. The only thing I don't like about this is that this "death system" would need to be aware of the other systems. This seems to go against ECS.

I'm curious to hear about techniques relating to inter-system communication and event handling relevant to ECS.

 

 

Advertisement

In ECS as we use it at work, you always have one system operating on a set of components. Components get collected (as entities are just a set of components) and processed in parallel from every system.

A system for example the render system takes the Transform and Mesh components and only entities that have exactly those two components ignoring the other [0 - n] components that are there too and performs its actions on every tuple. Same for the scene manager operating on Transform and SceneGraph components.

In your case, you have two possible options:

  • Let the Physics System also grab the lifetime component (what would be the best option because you could reduce work for the physics engine by early out objects that have been removed)
  • Have a third System that does exactly this. This is valid because Systems work context based, not task based. YOur Physics System is valid because it performs physics actions on your objects but a CleanupByDeath System would also be valid because it works on a different Context and just utilizes the physics library

What I also have seen are ECS approaches that communicate vai events. You are absolutely free to adapt the EC-Model to your needs, no worries to produce something "invalid"

7 hours ago, Shaarigan said:

In ECS as we use it at work, you always have one system operating on a set of components. Components get collected (as entities are just a set of components) and processed in parallel from every system.

A system for example the render system takes the Transform and Mesh components and only entities that have exactly those two components ignoring the other [0 - n] components that are there too and performs its actions on every tuple. Same for the scene manager operating on Transform and SceneGraph components.

In your case, you have two possible options:

  • Let the Physics System also grab the lifetime component (what would be the best option because you could reduce work for the physics engine by early out objects that have been removed)
  • Have a third System that does exactly this. This is valid because Systems work context based, not task based. YOur Physics System is valid because it performs physics actions on your objects but a CleanupByDeath System would also be valid because it works on a different Context and just utilizes the physics library

What I also have seen are ECS approaches that communicate vai events. You are absolutely free to adapt the EC-Model to your needs, no worries to produce something "invalid"

This is good information. Thank you.

In my case, I'm using Box2D to perform position updating, collision detection, etc. So my physics system will call a "Box2D.update()" function and my death system will need to call a "Box2D.kill()" function. I've had the mentality that systems should not need to share resources which was one reason I posted this question. However, I'm starting to think that that is an unnecessary limitation. 

As I already argued in your other thread that I think systems should NOT communicate at all. All communication happens through the components. The OW engine enforces this by implementing systems in cpp files. E.g. there are no system headers you can include. Tim calls this the pit of success in the talk. In my opinion the major goal of ECS is to keeps things decoupled. If you have systems dependent on each other this creates the same spaghetti as entity hierarchies do.

Physics is indeed a challenging problem here. E.g. who would own the physics world (e.g. hkpWorld, b2World, ...)? If you have a physics system which wraps the physics engine you would indeed need to allow access to the physics system so other systems can query the physics state (e.g. contact points, limit state of joints, etc). You also would wrap the ray cast interface of the physics engine and expose it through the physics system. 

I solved this issue the following way. An entity is composed of several physics components. E.g. body, shape(s), and joint(s). The physics world is owned by the scene class (where the scene class is *conceptually* my container which owns all entities for the current level). The scene update has several phases. E.g. a pre and post physic update phase which the system can hook into. There are more but for simplicity let's stick with this. I also realized that there is no such thing as "one" physics system. I have a "basic" physic system which does the standard physic integration (I will describe what it does below). But there are also other systems as well. Most notably a ragdoll system, a destruction system, and a movement system. The scene also wraps the basic ray cast and volume queries functions. It makes sense to me have it there.  For accessing contact state other systems can query the body components or they subscribe to collision events (if you have an event system). Depending on the phase the state is queried you get the contact state from the last or current frame.This is very important in some scenarios!

To wrap this up here is a quick description how a basic physics integration could look like. Every physics engine I am aware of has the concept of a static, kinematic, or dynamic body type. These body types define a contract with the game engine. A static body is assumed to be spawned once in place and it does not move. The physic engine can then apply a bunch of important optimizations for this kind of body. A kinematic body is assumed to be moved by the engine (e.g. by the animation or movement system). This usually happens in the pre-physics phase by computing a velocity such that a desired target transform is reached. The dynamic body is simulated and moved by the physics engine according to the active forces and constraints. A basic physic system does usually at least the three following things:

1) In the pre-physics phase it grabs all the target transforms from the entity and *pushes* it into the physic engine

2) In the post-physics phase it grabs all rigid body transforms from the physic engine and *pulls* them into the entity.

3) If you have an event system this is also the place where collision events are triggered.

 

HTH,

-Dirk

 

2 hours ago, Dirk Gregorius said:

For accessing contact state other systems can query the body components or they subscribe to collision events (if you have an event system). Depending on the phase the state is queried you get the contact state from the last or current frame.This is very important in some scenarios!

This was a great write up. Thank you.

Could you list some systems where you would need to differentiate between grabbing physics information from before and after the "physics update" routine? 

Ragdolls systems somewhat work like this:

Before the physics update you look at a target pose from the animation system and compute motors or other forces to drive the ragdoll to the desired goal. Then in the post update physics many things can happen. In a simple system you just copy the transforms back into the associated bone transform. In a more advanced system you might now evaluate how much the actual pose diverges from the target pose and react to this. This depends a lot on the game and the team you are working with.

Movement/Door/Elevator systems somewhat work like this:

You basically run some movement routine per entity. The naive approach is to have a keyframed body and set the velocity such that it reaches its target. This is a stupid idea as you are now  essentially moving a "black hole" (infinite mass) through your level. The standard example is a door. If the door is a keyframed object and something is blocking it, the door will push the object out of the world in the worst case. Garage doors which are coming down are notorious for this. So you need to deal with all these problems in one way or another. Based on your approach this has to run before or after the physics world is stepped

There is a great book by Jason Gregory "Game Engine Architecture". It has a great chapter about game object systems and talks about the phased update loop. He also gave a couple of great talks over the years. You can find a lot his work here;

https://www.gameenginebook.com/

HTH

This topic is closed to new replies.

Advertisement