Skip to main content
Applies to: Molca Core

Overview

This recipe shows you how to use the EventDispatcher service to implement decoupled communication between components. You’ll learn how to inject the EventDispatcher, register event handlers, dispatch events from publisher components, and properly clean up subscriptions. This pattern is ideal for cross-system notifications where components shouldn’t have direct references to each other.

Prerequisites

Step-by-step

Step 1: Inject EventDispatcher into components

Use the [Inject] attribute to get the EventDispatcher service in both publisher and subscriber components.
Why this works: The [Inject] attribute tells RuntimeManager to automatically populate the _eventDispatcher field after initialization. This eliminates the need for singleton patterns or manual service lookups.

Step 2: Register event handlers in OnEnable

Subscribe to events in OnEnable using RegisterEvent. Use parameterless registration for simple notifications or generic registration for typed payloads.
Why this works: Registering in OnEnable ensures handlers are active whenever the component is enabled. The RegisterEvent<T> generic method allows type-safe payload delivery without casting.

Step 3: Unregister handlers in OnDisable

Always unregister event handlers in OnDisable to prevent memory leaks and errors when the component is destroyed.
Why this works: Failing to unregister causes destroyed objects’ callbacks to remain registered, leading to MissingReferenceException errors. The OnEnable/OnDisable pairing ensures proper cleanup across enable/disable cycles.

Step 4: Dispatch events from publisher components

Use DispatchEvent to publish events. Other components registered for that event will receive the notification immediately.
Why this works: DispatchEvent invokes all registered callbacks for that event name immediately. The generic DispatchEvent<T> version passes the payload to all handlers registered with matching type parameter.

Step 5: Use EventConstants for type-safe event names

Instead of magic strings, use EventConstants or define your own constants class for better maintainability and refactoring support.
Why this works: Constants provide compile-time checking, IDE autocomplete, and safe refactoring. Typos in event names become compiler errors instead of silent runtime failures.

Complete example

Here’s a complete example showing a training scenario with multiple components communicating via events:
This example demonstrates:
  • Multiple subscribers listening to the same event
  • Type-safe event names using constants
  • Proper registration/unregistration lifecycle
  • Decoupled communication (components don’t reference each other)
  • Publisher doesn’t know who’s listening

Troubleshooting

  • Event not received: Confirm RegisterEvent runs before the first DispatchEvent. If subscription is in OnEnable but dispatch fires during RuntimeManager initialization, the handler may not be registered yet. Move subscription to Start with await RuntimeManager.WaitForInitialization().
  • Duplicate callbacks firing: Calling RegisterEvent twice with the same callback adds it twice. Always pair with UnregisterEvent in OnDisable to prevent stacking across enable/disable cycles.
  • MissingReferenceException after scene unload: Destroyed objects’ callbacks remain registered. Always call UnregisterEvent in OnDisable or OnDestroy.
  • Wrong type parameter: The type key must match exactly between publisher and subscriber. DispatchEvent<int> will not reach RegisterEvent<float> handlers even with the same event name.
  • Events fire but handler doesn’t execute: Check that the subscriber component is enabled and active. Handlers registered in OnEnable won’t fire if the GameObject is disabled.