Advertisement

Entity/Component System - Understanding Event Priority

Started by May 26, 2015 10:18 PM
8 comments, last by wintertime 9 years, 8 months ago

Hello!

So I have been working with implementing various types of Entity/Component Systems, and I really like the model which follows:

Entities are only IDs, Components are bags of data/properties, Systems act on one or more components that they're interested in.

This is all fine, but here's something I don't quite get ---

In my model, when something happens to a component, an event is raised, and other systems that care about that event respond. How does one determine the "firing" order of these events so that it's not totally random the order in which systems respond to events?

For example, in a CollisionSystem, if two entities have a CollisionComponent and collide, the system would fire off an event into the wild saying so. How can you efficiently guarantee the the responses to this event are fired in the right order?

It may depend on how the events are consumed, but couldn't you just fire the events into a buffer and then consume them in order?

-------R.I.P.-------

Selective Quote

~Too Late - Too Soon~

Advertisement

If you need to control the order things are processed in, you could (instead of firing an event and having the receiver process it immediately) set information in the CollisionComponent describing the collision. Then you would have a system (which by nature are updated in a specific order) that processes the result of the collision.

When an event fires, you could place it in a queue and during the game loop, iterate through it and handle them accordingly.
If you're using a true ECS pattern (sounds like you are), read these:

http://bitsquid.blogspot.com/2009/12/events.html
http://bitsquid.blogspot.com/2011/01/managing-coupling.html
http://bitsquid.blogspot.com/2011/02/managing-decoupling-part-2-polling.html
http://bitsquid.blogspot.com/2011/02/some-systems-need-to-manipulate-objects.html

Then read every other Bitsquid blog article.

Then head over and read the Molecular Musings blog (https://molecularmusings.wordpress.com/).

Sean Middleditch – Game Systems Engineer – Join my team!


For example, in a CollisionSystem, if two entities have a CollisionComponent and collide, the system would fire off an event into the wild saying so. How can you efficiently guarantee the the responses to this event are fired in the right order?

The real questions should perhaps be: What other sub-systems are addressed by the event and what meaning have the responses w.r.t. the collision system, and … what responsibility has the here mentioned collision system in its own?

Well, the movement sub-system wants collision detection (and correction) to avoid moving avatars bumping into one another. The damage sub-system, on the other hand, wants collision detection (without correction) to determine damage happening. The visual sense sub-system wants collision detection to determine whether a foe is in the viewing cone of an AI agent (to set the alert mode). And so on, and so on. Hence ...

a) the subsets of entities that are suitable for the one or the other kind of collision detection vary with the sub-system that is interested in such collisions;

b) already the different meanings of actual collisions introduce an order in which collisions should be detected (e.g. first movement, then damage);

c) the collision volumes are not necessarily the same for the different kinds of collision detection;

d) how should a generic collision detection be responsible for movement, AI, damage, and so on;

e) what leads (IMHO) to the conclusion that collision detection in itself is a service used by other (higher order) sub-systems,

f) and that such sub-system should not be processed in an arbitrary order anyway.

The ideas "having a sub-system per component" and "each sub-system has an update()" should not be treated as fundamental; each tool should be used where appropriate and should be replaced by something else where it is not appropriate. Even in case of collision detection it may be used (more or less strict) as soon as the generic collision volume component is dropped in favor of more specific components. For example, an entity may have a VisualSense component which means the AI has to use it. The component is parametrized with a viewing cone definition, because the underlying mechanic is based on collision detection. When that component is enabled, the entity has a role of a detector in the visual sense sub-system. The entity may also provide a VisualStimulus component. This one is driven e.g. by the movement sub-system, e.g. the faster the movement the stronger the stimulus. A mandatory parameter of the VisualStimulus is a volume for collision detection (which may but need not be the same as the volume for bumping avoidance). So the entity plays also the role of a detectable within the visual sense sub-system in case that the component is enabled.

You can still define collision volumes by using CollisionVolume components, if you wish to, but it has no meaning in itself. First its use as parameter to another component sets it into a meaningful context (a bit like data and behavior components). Well, this is the way I've chosen. It is of course not the only one.

Advertisement

Yet another thread talking about collision systems with clearly little knowledge of how CD works or it is supposed to work.

My suggestion: take a look at proper collision libraries. You will have surprises. Now, back to the point:

For example, in a CollisionSystem, if two entities have a CollisionComponent and collide, the system would fire off an event into the wild saying so. How can you efficiently guarantee the the responses to this event are fired in the right order?

You just need a linear scan of the returned collision pairs. I don't see any good reason to force an order of update anyway.

Previously "Krohm"

Wow, such a wealth of great information! biggrin.png Thanks much for your responses. The collision example seems to have given this thread an additional wrinkle in it, but I was mainly after if any pecking order ought to exist whereby systems respond to the same event sitting in a queue of other events that were raised by other systems.

Lots of things to consider:

  1. Is (or do you need) your system to be deterministic?
  2. Is your system multithreaded, and how does that impact its design with regard to point 1?
  3. Do you have events that require immediate response from subsequent events before continuing (something like double-dispatch of events).
  4. Does your environment provide coroutines/green threads/futures or any suitable suspend-resume feature.
  5. Do you process events entity-wise (all events for each entity, then to the next entity) or event-wise (all events of a type, then to the next event type).

I think the most scalable/flexible solution would probably be some kind of "fence" system -- which is basically a means of suspending one execution-path until some preconditions have been met, for example, you could have a fence on processing sound events until physics events are complete (because, e.g., they might spawn a sound). You can do this kind of course-grained dependency ordering just by controlling the general order in which events are processed, but it falls apart when you have different entities with different needs. Something like a fence on a particular entity could solve that problem, but of course you risk creating cycles or deadlock even more, so debugging can be more difficult.

throw table_exception("(? ???)? ? ???");

I think the simplest solution is to not use events, as they encourage creating spaghetti code.

Just have each system get input from each component of the associated component type, then write the calculated data to another component type for each affected entity. Then other systems can easily read this data when its their turn to get updated.

This topic is closed to new replies.

Advertisement