How to Subscribe and Unsubscribe to GameEvent in TEngine: A Complete Guide
Use GameEvent.AddEventListener() with an event ID and matching delegate to subscribe, and always call GameEvent.RemoveEventListener() with the exact same signature in OnDestroy to prevent memory leaks; for object-scoped events, use MemoryPool.Acquire<GameEventMgr>() which automatically cleans up when released.
TEngine is a Unity game framework that provides a robust event system for decoupled communication between game components. Understanding how to properly subscribe and unsubscribe to GameEvent in TEngine is essential for preventing memory leaks and ensuring clean architecture. This guide covers both global event subscriptions and local object-scoped events using the actual implementation from the alex-rachel/tengine repository.
Understanding TEngine's Event Architecture
TEngine provides two distinct event subscription models. The global event system uses the static GameEvent class for application-wide communication, storing listeners in a Dictionary<int, List<Delegate>> structure. The local event system uses GameEventMgr instances for object-scoped events that automatically clean up when the manager is released to the memory pool.
Subscribing and Unsubscribing to Global GameEvents
Global events in TEngine are managed through the static API in UnityProject/Assets/TEngine/Runtime/Core/GameEvent/GameEvent.cs.
Basic Subscription Without Parameters
Subscribe to simple events using the integer ID overload:
// In Awake or Start
GameEvent.AddEventListener(EventDefined.OnGameStart, OnGameStart);
// Handler method
private void OnGameStart()
{
Debug.Log("Game started!");
}
// In OnDestroy - MUST match signature exactly
GameEvent.RemoveEventListener(EventDefined.OnGameStart, OnGameStart);
Subscribing to Events with Parameters
Use generic overloads to specify argument types:
// Subscribe with one parameter
GameEvent.AddEventListener<int>(EventDefined.OnScoreChanged, OnScoreChanged);
private void OnScoreChanged(int newScore)
{
Debug.Log($"Score updated: {newScore}");
}
// Unsubscribe with exact same generic signature
GameEvent.RemoveEventListener<int>(EventDefined.OnScoreChanged, OnScoreChanged);
String-Based Event Registration
TEngine supports string-based event names as an alternative to integer constants:
GameEvent.AddEventListener("Player.Die", OnPlayerDie);
GameEvent.RemoveEventListener("Player.Die", OnPlayerDie);
private void OnPlayerDie()
{
Debug.Log("Player has died.");
}
Critical Unsubscription Requirements
The event manager stores delegates in a Dictionary<int, List<Delegate>>. The removal signature must exactly match the registration signature, including generic type parameters. Using Action for registration but Action<int> for removal will fail silently, leaving the listener active and causing memory leaks.
Using Local GameEventMgr for Object-Scoped Events
For events that should not outlive their owning object, use GameEventMgr from UnityProject/Assets/TEngine/Runtime/Core/GameEvent/GameEventMgr.cs.
Acquiring and Configuring GameEventMgr
public class PlayerController : MonoBehaviour
{
private GameEventMgr _localEventMgr;
private void Awake()
{
// Acquire from memory pool
_localEventMgr = MemoryPool.Acquire<GameEventMgr>();
// Subscribe to local events
_localEventMgr.AddEventListener(PlayerEventDefined.OnHpChange, OnHpChange);
}
private void OnHpChange(int hp)
{
Debug.Log($"Player HP: {hp}");
}
}
Automatic Cleanup Best Practices
Release the manager in OnDestroy (or OnDisable for UI components) to automatically unsubscribe all local listeners:
private void OnDestroy()
{
MemoryPool.Release(_localEventMgr);
}
This pattern prevents memory leaks without requiring manual RemoveEventListener calls for each subscription.
Initializing the Event System
Before using any events, initialize the helper class in your game's entry point:
GameEventHelper.Init(); // Generates wrappers for generated event interfaces
This step is required for the source-generated event interfaces to function correctly.
Common Pitfalls and How to Avoid Them
| Pitfall | How to avoid |
|---|---|
Using a different delegate type for removal (e.g., Action vs Action<int>) |
Keep the exact method signature; copy‑paste the registration line when writing the removal line. |
Forgetting to unsubscribe in OnDestroy/OnDisable |
Add the unsubscription logic in the same lifecycle method that created the listener, or use a local GameEventMgr that is released automatically. |
| Mixing string and int IDs for the same logical event | Choose a single convention (either all string‑based or all int‑based) and stick to it throughout the project. |
| Duplicate subscriptions causing multiple callbacks | Check if already subscribed before adding, or ensure initialization logic runs only once. |
Summary
- Global events use the static
GameEventclass withAddEventListenerandRemoveEventListenermethods, storing delegates in aDictionary<int, List<Delegate>>. - Local events use
GameEventMgrinstances acquired viaMemoryPool.Acquire<GameEventMgr>()and released inOnDestroyfor automatic cleanup. - Always match delegate signatures exactly when unsubscribing to prevent memory leaks.
- Call
GameEventHelper.Init()at game startup before using any events.
Frequently Asked Questions
What happens if I forget to call RemoveEventListener in TEngine?
If you forget to unsubscribe from a global GameEvent, the delegate remains in the internal Dictionary<int, List<Delegate>> even after your object is destroyed. This causes a memory leak because the event manager holds a reference to your method, preventing garbage collection. Always unsubscribe in OnDestroy or use GameEventMgr with automatic cleanup.
Can I use both int IDs and string names for the same event in TEngine?
While TEngine supports both int IDs and string names in GameEvent.AddEventListener, mixing conventions for the same logical event is not recommended. The event system treats these as separate keys in the dictionary, so subscribing with an int ID and firing with a string name (or vice versa) will not trigger your handler. Choose one convention per project and stick to it.
How do I pass multiple parameters through TEngine's GameEvent?
Use the generic overloads of AddEventListener to specify up to three type arguments. For example, use GameEvent.AddEventListener<int, string>(EventDefined.OnDataUpdate, Handler) where the handler signature is void Handler(int arg1, string arg2). The generic type parameters must match exactly between subscription, the handler, and unsubscription.
What is the difference between GameEvent and GameEventMgr in TEngine?
GameEvent is a static class providing global event functionality where listeners persist until manually removed, making it suitable for game-wide systems like game state changes. GameEventMgr is an instance-based manager acquired from MemoryPool that maintains its own private dispatcher; when you release the manager via MemoryPool.Release(), all its listeners are automatically unsubscribed, making it ideal for MonoBehaviour components that need lifecycle-bound events.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →