Quick Reference (Cheat Sheet)¶
Use this as a rapid guide to define/emit/listen and manage lifecycles.
Do’s
- Use attributes +
DxAutoConstructorfor clarity (or interfaces on structs for perf). - Bind struct messages to a variable before emitting.
- Use GameObject/Component emit helpers (no manual
InstanceId). - Register once; enable/disable with component state.
- Prefer named handler methods over inline lambdas for reuse and clarity.
- When using DI, inject
IMessageRegistrationBuilderinstead of newingMessageHandlers manually.
Don'ts¶
- Don’t emit from temporaries; use a local variable (e.g.,
var msg = new M(...); msg.Emit();). - Don’t mix Component vs GameObject targeting if you expect matches (see targeting notes below).
- Don’t register in Update; use
Awakefor staging +OnEnable/OnDisablefor lifecycle. - Don’t forget base calls when inheriting from
MessageAwareComponent— callbase.RegisterMessageHandlers()andbase.OnEnable()/base.OnDisable(). - Don’t hide Unity methods with
new(e.g.,new void OnEnable()); preferoverrideand callbase.*.
Define messages¶
C#
using DxMessaging.Core.Attributes;
[DxUntargetedMessage]
[DxAutoConstructor]
public readonly partial struct SceneLoaded { public readonly int buildIndex; }
[DxTargetedMessage]
[DxAutoConstructor]
public readonly partial struct Heal { public readonly int amount; }
[DxBroadcastMessage]
[DxAutoConstructor]
public readonly partial struct TookDamage { public readonly int amount; }
Emit (Unity helpers)¶
C#
using DxMessaging.Core.Extensions;
var scene = new SceneLoaded(1); scene.Emit();
var heal = new Heal(10); heal.EmitGameObjectTargeted(gameObject);
var hit = new TookDamage(5); hit.EmitComponentBroadcast(this);
// String shorthands
"Saved".Emit(); // GlobalStringMessage
"Hello".EmitAt(gameObject); // StringMessage to GO (or .Emit(instanceId))
"Hit".EmitFrom(gameObject); // SourcedStringMessage from GO
Register (Unity, via token)¶
C#
using DxMessaging.Core; // InstanceId
// Untargeted
_ = token.RegisterUntargeted<SceneLoaded>(OnSceneLoaded);
void OnSceneLoaded(ref SceneLoaded m) { /* ... */ }
// Targeted: to this component or gameObject
_ = token.RegisterComponentTargeted<Heal>(this, OnHeal);
_ = token.RegisterGameObjectTargeted<Heal>(gameObject, OnHeal);
void OnHeal(ref Heal m) { /* ... */ }
// Broadcast: from this component or gameObject
_ = token.RegisterComponentBroadcast<TookDamage>(this, OnDamageFromMe);
_ = token.RegisterGameObjectBroadcast<TookDamage>(gameObject, OnDamageFromMe);
void OnDamageFromMe(ref TookDamage m) { /* ... */ }
// Listen to all targets/sources
_ = token.RegisterTargetedWithoutTargeting<Heal>(OnAnyHeal);
void OnAnyHeal(ref InstanceId target, ref Heal m) { /* ... */ }
_ = token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);
void OnAnyDamage(ref InstanceId src, ref TookDamage m) { /* ... */ }
Register (DI / services)¶
C#
using DxMessaging.Core.MessageBus;
public sealed class DamageSystem : IStartable, IDisposable
{
private readonly MessageRegistrationLease lease;
public DamageSystem(IMessageRegistrationBuilder registrationBuilder)
{
lease = registrationBuilder.Build(new MessageRegistrationBuildOptions
{
Configure = token =>
{
_ = token.RegisterUntargeted<TookDamage>(OnDamage);
}
});
}
public void Start() => lease.Activate();
public void Dispose() => lease.Dispose();
private static void OnDamage(ref TookDamage message) { /* respond */ }
}
Tip: Define ZENJECT_PRESENT, VCONTAINER_PRESENT, or REFLEX_PRESENT to enable the optional shims under Runtime/Unity/Integrations that bind the builder automatically for those containers.
Interceptors and post‑processors¶
C#
using DxMessaging.Core; // MessageHandler
using DxMessaging.Core.MessageBus; // IMessageBus
var bus = MessageHandler.MessageBus;
_ = bus.RegisterBroadcastInterceptor<TookDamage>((ref InstanceId src, ref TookDamage m) =>
{
if (m.amount <= 0) return false; // cancel
m = new TookDamage(Math.Min(m.amount, 999));
return true;
});
_ = token.RegisterUntargetedPostProcessor<SceneLoaded>((ref SceneLoaded m) => LogScene(m.buildIndex));
Lifecycle¶
C#
void Awake() { /* stage registrations */ }
void OnEnable() { token.Enable(); }
void OnDisable() { token.Disable(); }
Inheritance tip (MessageAwareComponent)¶
- If you override
RegisterMessageHandlers, start withbase.RegisterMessageHandlers(). - If you override Unity lifecycle methods, call
base.OnEnable()/base.OnDisable()(andbase.Awake()/base.OnDestroy()if overridden).
Targeting notes (Component vs GameObject)¶
- A targeted message matches if the emitted
InstanceIdequals the registeredInstanceId. - Registering for a Component target listens for messages targeted at that specific Component.
- Registering for a GameObject target listens for messages targeted at that GameObject.
- Emitting to a GameObject will not reach Component‑targeted listeners (and vice‑versa). Use the matching helper.
- Shorthands exist for strings too; be explicit about using a GameObject vs Component with
EmitAt/EmitFrom.
See also¶
Execution Order¶
Untargeted¶
Targeted¶
Text Only
Interceptors → Global Accept-All → Handlers<T> @ target
→ Handlers<T> (All Targets) → Post-Processors<T> @ target
→ Post-Processors<T> (All Targets)
Broadcast¶
Text Only
Interceptors → Global Accept-All → Handlers<T> @ source
→ Handlers<T> (All Sources) → Post-Processors<T> @ source
→ Post-Processors<T> (All Sources)
📝 Note: Priority Rules
- Lower priority values run earlier
- Same priority preserves registration order
- Within a priority, fast (by-ref) handlers run before action handlers
API Quick Reference¶
Token: Untargeted¶
C#
// Register handler
token.RegisterUntargeted<T>(Action<T> handler, int priority = 0)
token.RegisterUntargeted<T>(FastHandler<T> handler, int priority = 0)
// Post-processor
token.RegisterUntargetedPostProcessor<T>(FastHandler<T> handler, int priority = 0)
Token: Targeted (Specific)¶
C#
// Register for specific target
token.RegisterGameObjectTargeted<T>(GameObject go, handler, int priority = 0)
token.RegisterComponentTargeted<T>(Component c, handler, int priority = 0)
token.RegisterTargeted<T>(InstanceId id, handler, int priority = 0)
// Post-processor
token.RegisterTargetedPostProcessor<T>(InstanceId id, FastHandler<T> handler, int priority = 0)
Token: Targeted (All Targets)¶
C#
// Listen to messages for any target
token.RegisterTargetedWithoutTargeting<T>(FastHandlerWithContext<T> handler, int priority = 0)
// Post-processor
token.RegisterTargetedWithoutTargetingPostProcessor<T>(FastHandlerWithContext<T> handler, int priority = 0)
Token: Broadcast (Specific)¶
C#
// Register for specific source
token.RegisterGameObjectBroadcast<T>(GameObject go, handler, int priority = 0)
token.RegisterComponentBroadcast<T>(Component c, handler, int priority = 0)
token.RegisterBroadcast<T>(InstanceId id, handler, int priority = 0)
// Post-processor
token.RegisterBroadcastPostProcessor<T>(InstanceId id, FastHandler<T> handler, int priority = 0)
Token: Broadcast (All Sources)¶
C#
// Listen to broadcasts from any source
token.RegisterBroadcastWithoutSource<T>(FastHandlerWithContext<T> handler, int priority = 0)
// Post-processor
token.RegisterBroadcastWithoutSourcePostProcessor<T>(FastHandlerWithContext<T> handler, int priority = 0)
Token: Global Observer¶
C#
// Action-based
token.RegisterGlobalAcceptAll(
Action<IUntargetedMessage> untargeted,
Action<InstanceId, ITargetedMessage> targeted,
Action<InstanceId, IBroadcastMessage> broadcast)
// Fast handler-based
token.RegisterGlobalAcceptAll(
FastHandler<IUntargetedMessage> untargeted,
FastHandlerWithContext<ITargetedMessage> targeted,
FastHandlerWithContext<IBroadcastMessage> broadcast)
Bus: Interceptors¶
C#
// Type-specific interceptors (return false to cancel)
bus.RegisterUntargetedInterceptor<T>(UntargetedInterceptor<T> interceptor, int priority = 0)
bus.RegisterTargetedInterceptor<T>(TargetedInterceptor<T> interceptor, int priority = 0)
bus.RegisterBroadcastInterceptor<T>(BroadcastInterceptor<T> interceptor, int priority = 0)
// Bus-level global observer
bus.RegisterGlobalAcceptAll(MessageHandler handler)