Components' fields change all the time, and sometimes we want to react to a certain change on a component without having complex logic or writing our own event machinery.
The goals are:
enable snapshots to capture component values
simplify listening to property value changes for components which don't change a lot
augment observation so that 3rd party code just works
One example could be listening for changes to an object (as a component) which is managed by a thrid-party library, like a physics engine or AI.
A more understandable example could be the implementation of achievements. Whenever a player enables a lever, the state can be observed and inside a system it can be tracked without having to add new logic. Let's say the game wants to track that a player activated a lever three times:
class Lever {
constructor(public state: LeverState) {}
}
class LeverInteraction {
constructor(public target: Lever) {}
}
class LeverInteractionEvent {
constructor(
public readonly entity: IEntity,
public readonly lever: Lever,
public readonly field: string,
public readonly oldValue: unknown,
public readonly newValue: unknown,
) {}
}
class LeverState {
active = false
}
// ...
prepWorld.buildEntity().withComponents(
new Mesh('lever.glb'),
Interactive,
Obervable(Lever, LeverInteractionEvent),
// ...
).build();
// ...
const LeverSystem = createSystem({
query: queryComponents({leverInteractions: Read(LeverInteraction)}),
})
.withRunFunction(({query}) =>
// Change lever state after activation
query.execute(({leverInteractions}) => leverInteractions.target.state.active = true)
)
.build();
const ActivateLeverThreeTimesSystem = createSystem({
achievementState: Storage({ activatedLevers: 0, unlocked: false }),
leverInteractionEvent: ReadEvent(LeverInteractionEvent),
})
.withRunFunction(({achievementState, leverInteractionEvent}) =>
leverInteractionEvent.execute(event => {
// Increment number of lever activations this step
if (event.newValue as boolean == true) {
achievementState.activatedLevers++;
}
});
// Check if the threshold was reached
if (!achievementState.unlocked && achievementState.activatedLevers >= 3) {
achievementState.unlocked = true;
// unlock achievement!
}
)
.build();
This code is not final, but should demonstrate how such an observer could be used from a user perspective. The implementation can be done using setters in the Object definition. We will not use Proxys, since their performance is abysmal. Also, observers should be used sparingly, since they augment a function call behind a simple value assignment.
Components' fields change all the time, and sometimes we want to react to a certain change on a component without having complex logic or writing our own event machinery.
The goals are:
One example could be listening for changes to an object (as a component) which is managed by a thrid-party library, like a physics engine or AI.
A more understandable example could be the implementation of achievements. Whenever a player enables a lever, the state can be observed and inside a system it can be tracked without having to add new logic. Let's say the game wants to track that a player activated a lever three times:
This code is not final, but should demonstrate how such an observer could be used from a user perspective. The implementation can be done using setters in the Object definition. We will not use
Proxy
s, since their performance is abysmal. Also, observers should be used sparingly, since they augment a function call behind a simple value assignment.