-
-
Notifications
You must be signed in to change notification settings - Fork 95
Reactive Engines
The ECS paradigm introduces a big shift also regarding communication. While we learned to move away from procedural code when we moved to event based programming, with ECS we need to get back to the origins. ECS communication happens only through polling of entities data. However sometimes it may seems wasteful to iterate over dozens or hundreds of entities when only few entities change, hence the introduction of Reactive Engines.
With Svelto ECS 2.8 the scope of DispatchOnSet and DispatchOnChange has been drastically reduced to the point that they are meant to be used only to let implementors (and therefore platform specific events) communicate with one specific engine, which has the responsibility to handle these events.
With this, I have been often asked how to handle events now. The answer is reactive engines. Reactive engines can be achieved in two ways, through the IReactOnAddAndRemove/IReactOnSwap interfaces and/or through the use of the EventStreams.
As BuildEntity and RemoveEntity (and SwapEntity) won't add or remove entities immediately and as you can't assume when these operations happen, the only way to react on entities removed or added is through the callbacks defined by the IReactOnAddAndRemove and IReactOnSwap interfaces. In this way, you can decouple the code that removes or adds the entity from the code that executes the operations due to these events happening.
The following list of possible scenarios will grow over the time with new use cases:
- Once the user understands the importance to build entities without entity view structs (in Unity this represent a gameobjectless scenario), then building and removing entities becomes super cheap. This enable the user to exploit the actual creation and destruction of entities to communicate specific events. For example, in Robocraft, placing a cube is not anymore a change of state of a pre-pooled object, but it's really an entity built on the spot. Other engines can now react on the operation comfortably.
- when groups are used as state for a state machine, the Swap callback can be exploited to activate behaviour according from which group (state) an entity is going to be removed from and to which group (state) an entity has been added to.
The EventStream is instead a new entitiesDB method that allows to communicate, using the publisher/consumer model, entities data change between engines and enginesroots. The pattern usually consists in querying a specific entity as usual, change its data and then publish the fact that this data changed using the EGID of the entity just modified. Other engines will generate their own consumer and separately poll them continuously. The consumer will be not empty only once one or more entities changes are published. EntityStreams work only with pure EntityStructs and once an EntityStruct is published, the EntityStream will hold a copy of that EntityStruct until it's consumed.
entitiesDB.QueryEntity<ButtonEntityStruct>(egid) = new ButtonEntityStruct(egid, value);
entitiesDB.PublishEntityChange<ButtonEntityStruct>(egid);
using (var _buttonConsumer = _consumerFactory.GenerateConsumer<ButtonEntityStruct>(ExclusiveGroups.GuiViewButton,
"MaterialEditorViewSwitchButtons", 1))
while (true)
{
while (_buttonConsumer.TryDequeue(out var entity))
{
if (entity.message == ButtonEvents.SELECT)
{
//...
}
}
yield return null;
}
Note that a consumer can consume regardless the group or according a specific group.
Check the mini examples to see how to use EntityStreams: https://github.com/sebas77/Svelto.MiniExamples
related topic: