EventBus

Namespace:

namespace Lenga\Engine\Core;

final class EventBus

EventBus is the process-wide decoupled event system.

For gameplay events, start here when listeners should react without holding a reference to the dispatcher. Use Signal only when a specific owner instance intentionally exposes an owner-scoped callback surface.

Inside Behaviours, prefer the Behaviour helpers emitEvent(...), onEvent(...), and onceEvent(...) unless you specifically need the raw static API.

Constants

UI_SELECTION_CHANGED

public const UI_SELECTION_CHANGED = 'ui.selection_changed';

Emitted by the engine when UI selection changes through the built-in navigation system.

The payload is an array with:

  • name: the event name string
  • viaPointer: whether pointer input caused the selection
  • canvas: native lookup data for the canvas
  • previous: native lookup data for the previously selected UI element, or null
  • current: native lookup data for the selected UI element, or null

Methods

on

public static function on(string $eventName, callable $listener): SignalSubscription

Subscribes a listener and returns a SignalSubscription.

The listener receives the payload first and the event name second:

EventBus::on('game.round_started', function (array $payload, string $eventName): void {
    // ...
});

If you are inside a Behaviour, prefer onEvent(...) so lifecycle cleanup is automatic.

addListener

public static function addListener(string $eventName, callable $listener): SignalSubscription

Alias for on(...).

subscribe

public static function subscribe(string $eventName, callable $listener): SignalSubscription

Alias for on(...).

once

public static function once(string $eventName, callable $listener): SignalSubscription

Subscribes a listener that automatically removes itself after the first event.

onceListener

public static function onceListener(string $eventName, callable $listener): SignalSubscription

Alias for once(...).

off

public static function off(string $eventName, SignalSubscription|callable $listener): void

Removes a subscription or callable listener from the event.

removeListener

public static function removeListener(string $eventName, SignalSubscription|callable $listener): void

Alias for off(...).

emit

public static function emit(string $eventName, mixed $payload = null): void

Dispatches a global event with one payload value.

dispatch

public static function dispatch(string $eventName, mixed $payload = null): void

Alias for emit(...).

clear

public static function clear(?string $eventName = null): void

Clears listeners for one event. If no event name is provided, clears all global event listeners.

Use this carefully. Most gameplay code should dispose the specific SignalSubscription it owns instead.

hasListeners

public static function hasListeners(string $eventName): bool

Returns whether the event currently has listeners.

listenerCount

public static function listenerCount(string $eventName): int

Returns the number of listeners for the event.

Example

use Lenga\Engine\Core\Behaviour;

final class Health extends Behaviour
{
    public int $current = 100;
    public string $kind = 'enemy';

    public function applyDamage(int $amount): void
    {
        $this->current -= $amount;

        if ($this->current <= 0) {
            $this->emitEvent('game.health.died', [
                'source' => $this,
                'kind' => $this->kind,
            ]);
        }
    }
}
use Lenga\Engine\Core\Behaviour;

final class GameManager extends Behaviour
{
    public function onEnable(): void
    {
        $this->onEvent('game.health.died', function (?array $payload): void {
            if (($payload['kind'] ?? null) === 'player') {
                $this->beginGameOverFlow();
            }
        });
    }

    private function beginGameOverFlow(): void
    {
    }
}

This pattern lets unrelated systems react to the same event without first finding the dispatcher.