Custom events

Sometimes it’s useful to decouple the different parts of your scene’s code and make them interact with each other via sending events.

Decentraland scenes handle some default events like click events and buttonDown or buttonUp events, but you can create your own to handle things that are specific to your scene.

For example, you could have a pickedCoin event that’s emitted every time the player picks up a coin in your scene. You could then have a score board that listens for these events and updates the score accordingly. Thanks to this, the part of your code that handles the picking of coins doesn’t need to have any reference to the part of the code that updates the scoreboard.

Initiate the event manager

Before you can emit or listen for events, you need to initiate the event manager in your scene.

const events = new EventManager()

Define event types

If you want events in your scene to contain custom data fields, you need to define a specific type for your events. You do this by defining a class that has an @EventConstructor() decorator.

@EventConstructor()
class MyEvent {
  field1: string
  field2: number
  constructor(public field1: string, public field2: number) {
    this.field1 = field1
    this.field2 = field2
  }
}

Emit events

To emit an event, you call the fireEvent() function of the event manager.

events.fireEvent(new MyEvent(field1, field2))

Note that in this example, the event being sent contains an object of a custom event type.

Listen for events

To listen for an event, you can add call the addListener() function of the event manager. This function takes in the following arguments:

  • The type of the event object to listen for.
  • The listener to use. This will almost always be null.
  • The function to execute every time the event is caught.
events.addListener(MyEvent, null, ({ field1, field2 }) => {
  // function
})

Full example

The following example scene emits and listens for events.

// Initiate event manager
const events = new EventManager()

// Define an event type
@EventConstructor()
class UpdateEvent {
  entity: Entity
  dt: number
  constructor(public entity: Entity, public dt: number) {
    this.entity = entity
    this.dt = dt
  }
}

// Define a system
export class RotatorSystem implements ISystem {
  group = engine.getComponentGroup(Transform)

  update(dt: number) {
    for (let entity of this.group.entities) {
      // Emit custom event
      events.fireEvent(new UpdateEvent(entity, dt))
    }
  }
}

engine.addSystem(new RotatorSystem())

// Add a listener
events.addListener(UpdateEvent, null, ({ entity, dt }) => {
  const transform = entity.getComponent(Transform)
  const euler = transform.rotation.eulerAngles
  euler.y += dt * 10
  transform.rotation.eulerAngles = euler
})

// Add an entity to work with
const cube = new Entity()
const transform = new Transform()

cube.addComponentOrReplace(transform)
transform.position.set(5, 0, 5)

const boxShape = new BoxShape()
cube.addComponentOrReplace(boxShape)

engine.addEntity(cube)