Skip to main content

Events

Introduction​

Events are how entities can communicate with each other through a flexible listener and dispatch system.

Event Listeners​

Listener​
PropertyTypeDescription
targeteidEntity the event was dispatched on.
currentTargeteidEntity the event was listened on.
namestringName of the event.
dataanyEvent custom data

Creating Event Listeners​

addListener​

world.events.addListener(target, name, listener)
Parameters​
tip

It's possible to create global event listeners by using world.events.globalId as the target.

PropertyTypeDescription
targeteidReference to the target entity.
namestringName of the event to listen for.
listenerListenerThe callback function for when an event is triggered

Creating Event Handlers​

When adding event listeners to entities, it’s crucial to set up handlers correctly to ensure they function as intended, especially when components are added to multiple entities. Improper handler creation can lead to stale references and unexpected behavior.

Suppose you’re creating a handler for an NPC entity that listens for a damaged event. The handler should update some schema and data values when the event occurs.

Incorrect Example​

import * as ecs from '@8thwall/ecs'
import { addCleanup, doCleanup } from './cleanup'

ecs.registerComponent({
name: 'npc',
schema: {
isInjured: ecs.boolean,
},
data: {
bpm: ecs.f32,
},
add: (world, component) => {
// Incorrect handler creation
const damagedHandler = (e) => {
component.schema.isInjured = true
component.data.bpm += 30
}
world.events.addListener(component.eid, 'damaged', damagedHandler)

const cleanup = () => {
world.events.removeListener(component.eid, 'damaged', damagedHandler)
}
addCleanup(component, cleanup)
},
remove: (world, component) => {
doCleanup(component)
},
})

In this example:

  • The handler damagedHandler directly references component.schema and component.data.
  • If the component is added to multiple entities, the component reference inside the handler becomes stale.
  • This can cause the handler to operate on incorrect data, leading to bugs.

Correct Example​

To ensure the handler operates on the correct entity data, pass the component’s dataAttribute and schemaAttribute to the handler and use them to fetch cursors inside the handler.

import * as ecs from '@8thwall/ecs'
import { addCleanup, doCleanup } from './cleanup'

// Function to create the damaged handler
const createDamagedHandler = (dataAttribute, schemaAttribute) => (e) => {
const dataCursor = dataAttribute.cursor(e.target)
const schemaCursor = schemaAttribute.cursor(e.target)
schemaCursor.isInjured = true
dataCursor.bpm += 30
}

ecs.registerComponent({
name: 'npc',
schema: {
isInjured: ecs.boolean,
},
data: {
bpm: ecs.f32,
},
add: (world, component) => {
// Correct handler creation
const damagedHandler = createDamagedHandler(component.dataAttribute, component.schemaAttribute)
world.events.addListener(component.eid, 'damaged', damagedHandler)

const cleanup = () => {
world.events.removeListener(component.eid, 'damaged', damagedHandler)
}
addCleanup(component, cleanup)
},
remove: (world, component) => {
doCleanup(component)
},
})

Removing Event Listeners​

removeListener​

world.events.removeListener(target, name, listener)
Parameters​
PropertyTypeDescription
targeteidReference to the target entity.
namestringName of the event to listen for.
listenerListenerThe callback function for when an event is triggered

Event Dispatchers​

Dispatching Custom Events​

Dispatch​

info

When an event happens on an entity, it first runs the handlers on it, then on its parent, then all the way up on other ancestors.

world.events.dispatch(eidOfEnemy, "attack", {damage: 10})

Cleaning Up Listeners​

danger

Always ensure listeners are properly removed to avoid memory leaks.

When a component is deleted, its event listeners are not automatically cleaned up, so you must remove them manually.