| name | f8-features-event-workflow |
| description | Use when implementing or troubleshooting Event feature workflows — message dispatching, event listening, EventDispatcher auto-cleanup, and strongly typed zero-GC event parameters (0-7 args, no object[] compatibility path) in F8Framework. |
Event Feature Workflow
⚠️ IMPORTANT: Before using this feature, you MUST formally initialize F8Framework in the launch sequence. Ensure ModuleCenter.Initialize(this); has run first, then create the required module, for example FF8.Message = ModuleCenter.CreateModule<MessageManager>();.
Use this skill when
- The task is about event/message dispatching and listening.
- The user asks about decoupled communication between components.
- The user needs EventDispatcher auto-cleanup on UI or entities.
- Troubleshooting event dead loops or missing callbacks.
Path resolution
- Prefer project source at Assets/F8Framework.
- If F8Framework is installed as a package, use Packages/F8Framework.
- For usage docs, read: Assets/F8Framework/Tests/Event/README.md
Sources of truth
- Runtime module: Assets/F8Framework/Runtime/Event
- Editor module: Assets/F8Framework/Editor/Event
- Test docs: Assets/F8Framework/Tests/Event
Key classes and interfaces
| Class | Role |
|---|
MessageManager | Core module. Access via FF8.Message. Global event bus. |
EventDispatcher | Mixin base class for auto-cleanup event listeners. Used by BaseView. |
API quick reference
Define events
public enum MessageEvent
{
Empty = 10000,
ApplicationFocus = 10001,
NotApplicationFocus = 10002,
ApplicationQuit = 10003,
}
Global event listening (via FF8.Message)
FF8.Message.AddEventListener(MessageEvent.ApplicationFocus, OnEvent, this);
FF8.Message.AddEventListener<int, string>(10002, OnEventNoGC, this);
FF8.Message.AddEventListener<MessageEvent, int, string>(MessageEvent.ApplicationFocus, OnEventNoGC, this);
FF8.Message.AddEventListener<int, string, bool, float, long, byte, char>(10004, OnEventT7, this);
FF8.Message.DispatchEvent(MessageEvent.ApplicationFocus);
FF8.Message.DispatchEvent(10002, 123, "data");
FF8.Message.DispatchEvent(MessageEvent.ApplicationFocus, 123, "data");
FF8.Message.DispatchEvent(10004, 123, "data", true, 1.5f, 999L, (byte)7, 'F');
FF8.Message.DispatchEventAsync(MessageEvent.ApplicationFocus);
FF8.Message.DispatchEventAsync(10002, 123, "data");
FF8.Message.DispatchEventAsync(MessageEvent.ApplicationFocus, 123, "data");
FF8.Message.DispatchEventAsync(10004, 123, "data", true, 1.5f, 999L, (byte)7, 'F');
FF8.Message.RemoveEventListener(MessageEvent.ApplicationFocus, OnEvent, this);
FF8.Message.RemoveEventListener<int, string>(10002, OnEventNoGC, this);
FF8.Message.RemoveEventListener<MessageEvent, int, string>(MessageEvent.ApplicationFocus, OnEventNoGC, this);
FF8.Message.RemoveEventListener<int, string, bool, float, long, byte, char>(10004, OnEventT7, this);
void OnEvent() { }
void OnEventNoGC(int id, string name) { }
void OnEventT7(int id, string name, bool active, float speed, long score, byte level, char rank) { }
EventDispatcher pattern (auto-cleanup)
AddEventListener(MessageEvent.ApplicationFocus, OnEvent);
AddEventListener<int, string>(10002, OnEventNoGC);
AddEventListener<int, string, bool, float, long, byte, char>(10004, OnEventT7);
DispatchEvent(MessageEvent.ApplicationFocus);
DispatchEvent(10002, 123, "data");
DispatchEvent(10004, 123, "data", true, 1.5f, 999L, (byte)7, 'F');
DispatchEventAsync(MessageEvent.ApplicationFocus);
DispatchEventAsync(10002, 123, "data");
DispatchEventAsync(10004, 123, "data", true, 1.5f, 999L, (byte)7, 'F');
RemoveEventListener(MessageEvent.ApplicationFocus, OnEvent);
RemoveEventListener<int, string>(10002, OnEventNoGC);
RemoveEventListener<int, string, bool, float, long, byte, char>(10004, OnEventT7);
Zero-GC recommendation
FF8.Message.AddEventListener<int>(10010, OnHpChanged, this);
FF8.Message.DispatchEvent(10010, 99);
FF8.Message.DispatchEventAsync(10010, 99);
FF8.Message.AddEventListener<int, int>(10011, OnDamage, this);
FF8.Message.DispatchEvent(10011, 12, 3);
FF8.Message.DispatchEventAsync(10011, 12, 3);
FF8.Message.AddEventListener<int, int, int, int, int, int, int>(10012, OnCombo, this);
FF8.Message.DispatchEvent(10012, 1, 2, 3, 4, 5, 6, 7);
FF8.Message.DispatchEventAsync(10012, 1, 2, 3, 4, 5, 6, 7);
void OnHpChanged(int hp) { }
void OnDamage(int damage, int criticalType) { }
void OnCombo(int a, int b, int c, int d, int e, int f, int g) { }
Workflow
- Define event IDs as enum (start from 10000 to avoid framework conflicts).
- Choose pattern: global
FF8.Message or EventDispatcher mixin.
- For UI/entity classes, prefer
EventDispatcher for automatic cleanup.
- Use the strongly typed overload matching the event payload size; the event module now supports 0~7 fixed parameters.
- If you need to spread callback cost across frames, use
DispatchEventAsync(...); the current implementation executes 1 listener per frame.
- Always pass
this as the last parameter to AddEventListener for lifecycle binding.
- The framework has built-in dead-loop prevention.
- Use the Event System Monitor editor window to debug active listeners.
Common error handling
| Error | Cause | Solution |
|---|
| Event not received | Listener added after dispatch | Ensure listener registration before dispatch |
| Dead loop warning | Event A dispatches Event B which dispatches Event A | Break the cycle, use intermediate state |
| Memory leak | Listeners not removed on destroy | Use EventDispatcher or pass this for auto-cleanup |
| Wrong callback signature | Params mismatch | Match the overload exactly: void(), void(T1), void(T1,T2) ... void(T1,T2,T3,T4,T5,T6,T7) |
Cross-module dependencies
- UI: BaseView extends EventDispatcher for auto-cleanup.
- Timer: Application focus events used by Timer module.
Output checklist
- Event ID enum defined with proper range.
- Listener pattern selected (global / EventDispatcher).
- Files changed and why.
- Validation status and remaining risks.