Quest Event Bus
QuestEventBus.cs
Overview
QuestEventBus is the internal pub/sub backbone of the Quest Forge event system. It implements the Observer pattern, allowing any system to publish a typed event and any number of subscribers to receive it — without either side holding a direct reference to the other.
It is a pure C# singleton (not a MonoBehaviour). It has no Inspector representation and is never placed in a scene. It is instantiated automatically on first access via QuestEventBus.Instance and lives for the duration of the application session.
In normal usage, developers do not interact with QuestEventBus directly. QuestEventReporter is the public-facing API for publishing events, and QuestManager handles subscription internally. The bus is exposed for advanced use cases such as custom objective types or external systems that need to react to quest events.
Architecture Position
QuestEventReporter (public publishing API)
│
▼
QuestEventBus (internal observer bus) ← this component
│
├──► QuestManager (subscribed in Awake)
├──► Custom Objectives (subscribed when quest starts)
└──► Any custom system (subscribe via QuestEventBus.Instance)Singleton Access
QuestEventBus.InstanceThe instance is created lazily on first access. It does not need to be initialised manually — QuestEventReporter calls Initialize() during its own Awake.
Initialization
Initialize(QuestLogger questLogger, bool enableDebug = false)
Assigns the logger and enables or disables verbose debug output. Called automatically by QuestEventReporter.Awake(). Only needs to be called once per session.
questLogger
The QuestLogger instance to use for output.
enableDebug
When true, logs every subscription, unsubscription, and publish call.
Core API
Subscribe(QuestEventType eventType, Action<QuestEventData> callback)
Registers a callback to be invoked whenever the specified event type is published. Duplicate subscriptions are silently ignored — the same callback will not be added twice for the same event type.
eventType
The QuestEventType to listen for.
callback
The method to call when the event fires. Receives a QuestEventData base reference — cast to the appropriate subtype inside your handler.
Unsubscribe(QuestEventType eventType, Action<QuestEventData> callback)
Removes a previously registered callback. Always call this in OnDisable or OnDestroy to prevent callbacks from firing on destroyed objects.
Publish(QuestEventData eventData)
Broadcasts an event to all registered subscribers for its event type. Execution is synchronous — all callbacks are invoked immediately within the same frame, in registration order.
eventData
A fully constructed QuestEventData subclass. Must not be null.
Safety guarantees during publish:
Null reference cleanup — dead or null callbacks are removed from the listener list before invocation. This handles MonoBehaviours that were destroyed without explicitly unsubscribing.
Iteration safety — the listener list is copied before iteration, so subscribing or unsubscribing from within a callback does not cause collection-modification exceptions.
Exception isolation — if a callback throws an exception, it is caught and logged. Remaining subscribers still receive the event.
Statistics & Debugging
GetEventStatistics() → Dictionary<QuestEventType, int>
Returns a copy of the per-event-type publish count since the session started (or since the last ResetStatistics() call). Used by QuestEventReporter.PrintEventStatistics().
GetTotalEventsProcessed() → int
Returns the total number of events published across all types since session start.
ResetStatistics()
Resets all per-type counts and the total count to zero. Does not affect subscribers.
Maintenance
ClearAllListeners()
Removes all registered callbacks for all event types. Useful during major state transitions such as returning to a main menu, where all scene-level subscribers would be destroyed. Does not affect event counts or the singleton instance itself.
Subscribing from Custom Code (Advanced)
The typical pattern for any MonoBehaviour that needs to react to quest events:
Always use OnEnable / OnDisable rather than Start / OnDestroy when possible. This ensures subscriptions are correctly managed through object activate/deactivate cycles, not just creation/destruction.
Other Notes
QuestEventBusis a plain C# object — it is not tied to any Unity lifecycle. It persists for the entire application session and survives scene loads. Subscribers that are destroyed without unsubscribing will be cleaned up automatically on the next publish, but explicit unsubscription is always preferred.Listener lists are initialised for every
QuestEventTypevalue at construction time, so there is never a key-not-found issue when subscribing to or publishing any valid event type.Adding a new event type requires only adding a value to the
QuestEventTypeenum — the bus handles it automatically.The bus does not support priority ordering. Callbacks are invoked in the order they were subscribed. If execution order matters between two subscribers, consider chaining them or using a shared intermediary.
Last updated