Event System
Arche features an event system that can be used to get notifications about world modifications, namely:
- Entity creation and removal
- Component addition, removal and exchange
- Changes of entity relation targets
The event system is particularly useful for automating the management of supplementary data structures that store entities. It can be used to automatically insert entities into these structures on creation or component addition, and to remove them on entity or component removal.
The types of interest here are
ecs.Listener
and
ecs.EntityEvent
.
Subscriptions
A listener must subscribe to certain event types.
These are constants of type
ecs.event.Subscription
:
1_ = event.EntityCreated
2_ = event.EntityRemoved
3_ = event.ComponentAdded
4_ = event.ComponentRemoved
5_ = event.RelationChanged
6_ = event.TargetChanged
Multiple event types can be combined using bit-wise OR (|
):
1subs := event.EntityCreated | event.EntityRemoved
2_ = subs
Some combinations of event types are already defined as
ecs.event.Subscription
.
E.g., to subscribe to all event types, use event.All
.
Besides subscribing to event types, subscriptions can be restricted to certain component types
that must be affected by the event.
Component type subscriptions are realized using the same
ecs.Mask
mechanics that filters use. Create masks with
ecs.All
:
1world := ecs.NewWorld()
2posID := ecs.ComponentID[Position](&world)
3headID := ecs.ComponentID[Heading](&world)
4
5compSubs := ecs.All(posID, headID)
6_ = compSubs
A listener with this component subscription would be notified on changes
that are related to the Position
or to the Heading
component.
Builtin listener
An
ecs.World
can have at most one
ecs.Listener
.
If required, this listener can be used to dispatch events to sub-listeners.
Such a listener is provided by
listener.Dispatch
.
In conjunction with
listener.Callback
,
it is already possible to build a sophisticated event system.
In the following example, we compose a
listener.Dispatch
from two
listener.Callback
.
The first one listens to all entity creation and entity removal events.
The second one listens to events where a Position
or a Heading
is added to an entity.
1world := ecs.NewWorld()
2posID := ecs.ComponentID[Position](&world)
3headID := ecs.ComponentID[Heading](&world)
4
5// Listener for all entity creation and entity removal events.
6entityListener := listener.NewCallback(
7 // A function we want to call on notification.
8 func(w *ecs.World, ee ecs.EntityEvent) { /* Do something here */ },
9 // Subscription to event types.
10 event.EntityCreated|event.EntityRemoved,
11)
12
13posOrHeadAddedListener := listener.NewCallback(
14 // A function we want to call on notification.
15 func(w *ecs.World, ee ecs.EntityEvent) { /* Do something here */ },
16 // Subscription to event types.
17 event.ComponentAdded,
18 // Subscription is restricted to these component types.
19 posID, headID,
20)
21
22// Create the dispatch listener from both sub-listeners.
23dispatch := listener.NewDispatch(
24 &entityListener,
25 &posOrHeadAddedListener,
26)
27// Set it as the world's listener.
28world.SetListener(&dispatch)
Custom listeners
Custom listeners can be created by implementing the interface
ecs.Listener
.
Here is an example of a listener that listens to additions of a Position
component:
1package main
2
3import (
4 "fmt"
5
6 "github.com/mlange-42/arche/ecs"
7 "github.com/mlange-42/arche/ecs/event"
8)
9
10// PositionAddedListener listens to additions of a Position component.
11type PositionAddedListener struct {
12 subs event.Subscription
13 comps ecs.Mask
14}
15
16// NewPositionAddedListener creates a new PositionAddedListener.
17func NewPositionAddedListener(world *ecs.World) PositionAddedListener {
18 posID := ecs.ComponentID[Position](world)
19 return PositionAddedListener{
20 subs: event.ComponentAdded,
21 comps: ecs.All(posID),
22 }
23}
24
25// Notify the listener about a subscribed event.
26func (l *PositionAddedListener) Notify(world *ecs.World, evt ecs.EntityEvent) {
27 fmt.Println("Position component added to entity ", evt.Entity)
28}
29
30// Subscriptions to one or more event types.
31func (l *PositionAddedListener) Subscriptions() event.Subscription {
32 return l.subs
33}
34
35// Components the listener subscribes to. Listening to all components indicated by nil.
36func (l *PositionAddedListener) Components() *ecs.Mask {
37 return &l.comps
38}
EntityEvent
In Listener.Notify
, as well as in the callback for
listener.Callback
, we get an
ecs.EntityEvent
as argument.
It provides all sorts of information about the event, like the affected
ecs.Entity
,
event types covered, components added and removed, and more. See the API docs of
ecs.EntityEvent
for details.