Guides Behaviours Use Signals for Owner-Scoped Callbacks

Behaviours 2 min read Updated Apr 2026

Use Signals for Owner-Scoped Callbacks

Use a `Signal` when one object wants to expose a callback surface on itself and listeners already know which exact owner they care about.

That is the key idea:

a Signal belongs to an owner instance.

Subscribers still need a reference to that owner in order to subscribe.

When Signal Is a Good Fit

Reach for Signal when:

  • one object exposes a callback surface to several listeners
  • listeners already know the owner they want to observe
  • the event belongs to that one owner instance
  • subscription objects, once(...), and explicit disposal are useful

This is a good fit for local, owner-scoped relationships.

Example: A Door Exposes onOpened

Here the Door owns the signal:

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Signal;

final class Door extends Behaviour
{
    public Signal $onOpened;

    public function awake(): void
    {
        $this->onOpened = $this->createSignal();
    }

    public function open(): void
    {
        $this->onOpened->dispatch($this);
    }
}

Now another object can subscribe to that one specific door:

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\SignalSubscription;

final class DoorIndicator extends Behaviour
{
    public ?Door $door = null;
    private ?SignalSubscription $subscription = null;

    public function onEnable(): void
    {
        $this->subscription = $this->door?->onOpened->add(function (Door $door): void {
            $this->flashIndicator();
        });
    }

    public function onDisable(): void
    {
        $this->subscription?->dispose();
        $this->subscription = null;
    }

    private function flashIndicator(): void
    {
    }
}

This works well because DoorIndicator already knows which Door it cares about.

Why createSignal() Matters

Create signals in awake() with createSignal():

public function awake(): void
{
    $this->onOpened = $this->createSignal();
}

That keeps the signal tied to the behaviour lifecycle.

What Signals Do Well

Signals are useful when you want:

  • one owner to expose a local callback surface
  • several listeners attached to the same owner
  • explicit subscription objects
  • once(...) behavior

Signals are not the default answer for general gameplay events.

If your listener should react without holding a dispatcher reference, use EventBus instead.

What Comes Next