t1k:cocos:playable:lifecycle
| Field | Value |
|---|---|
| Module | playable |
| Version | 2.14.4 |
| Effort | high |
| Tools | — |
Keywords: component, GameLifecycleManager, lifecycle
How to invoke
Section titled “How to invoke”/t1k:cocos:playable:lifecycleGameLifecycleManager — Non-Component Lifecycle System
Section titled “GameLifecycleManager — Non-Component Lifecycle System”Drives Tick/FixedTick/LateTick on plain TS classes that don’t extend Cocos Component. Add GameLifecycleManager as a component on a persistent scene node. See also: t1k-cocos-playable-signalbus, t1k-cocos-playable-async-utilities.
Import path:
db://assets/packages/@playablelabs/game-foundation/gameLifecycle—GameLifecycleManager,RegisterLifecycle,LifecycleRegistry, and theIInitializable/ITickable/IFixedTickable/ILateTickable/IDisposableinterfaces
Interfaces
Section titled “Interfaces”| Interface | Method signature | Called when |
|---|---|---|
IInitializable | Initialize(): void | Immediately on Register() |
ITickable | Tick(dt: number): void | Every frame (via Cocos update) |
IFixedTickable | FixedTick(fixedDt: number): void | Fixed timestep accumulator (default 0.02s / 50 FPS) |
ILateTickable | LateTick(dt: number): void | After all Ticks (via Cocos lateUpdate) |
IDisposable | Dispose(): void | On Unregister() or scene destroy |
A class can implement any combination. Register() detects which interfaces are present via duck-typing (isType checks for method name existence).
@RegisterLifecycle() Decorator
Section titled “@RegisterLifecycle() Decorator”Auto-instantiates and registers the class when GameLifecycleManager.onLoad() runs. No-arg constructor required.
import { RegisterLifecycle } from "db://assets/packages/@playablelabs/game-foundation/gameLifecycle";import { IInitializable, ITickable, IDisposable } from "db://assets/packages/@playablelabs/game-foundation/gameLifecycle";
@RegisterLifecycle()export class MySystem implements IInitializable, ITickable, IDisposable { Initialize(): void { // setup — called once at scene start }
Tick(dt: number): void { // runs every frame }
Dispose(): void { // cleanup on scene destroy }}Manual Registration
Section titled “Manual Registration”Use when you need constructor args or runtime control:
const system = new MySystem(someArg);GameLifecycleManager.Instance.Register(system);
// Later, to stop ticking and call Dispose:GameLifecycleManager.Instance.Unregister(system);Fixed Timestep
Section titled “Fixed Timestep”The manager accumulates delta time and calls FixedTick at a fixed rate:
// Default: 0.02s (50 Hz). Change at runtime:GameLifecycleManager.Instance.SetFixedTimeStep(1 / 30); // 30 HzGameLifecycleManager.Instance.GetFixedTimeStep();FixedTick receives the configured fixedDeltaTime, not the accumulated surplus.
isType Pattern (Internal)
Section titled “isType Pattern (Internal)”The manager uses duck-typing — not instanceof — to check interfaces:
// Internal implementation (for reference):private isType<T>(obj: any, methodName: string): obj is T { return methodName in obj;}This means TypeScript interfaces are checked at runtime by method presence. Ensure method names match exactly (Tick, FixedTick, LateTick, Initialize, Dispose).
Debug Info
Section titled “Debug Info”console.log(GameLifecycleManager.Instance.GetDebugInfo());// Prints counts for each collection + fixedTimeStepWhen to Use Lifecycle Manager vs Cocos Component update()
Section titled “When to Use Lifecycle Manager vs Cocos Component update()”| Scenario | Use |
|---|---|
| Pure logic / service class (no Node) | @RegisterLifecycle() + interfaces |
| Node-attached behaviour | Cocos Component with update() |
| Physics-like deterministic step | IFixedTickable |
| Camera follow / post-process | ILateTickable |
| One-time setup with no ticking | IInitializable only |
| Cross-system service needing frame updates | ITickable via lifecycle manager |
Common Mistakes
Section titled “Common Mistakes”- Implementing
tick()(lowercase) instead ofTick()— manager checks exact name; the method is silently ignored. - Using
@RegisterLifecycle()on a class with constructor arguments — manager callsnew ClassConstructor()with no args; use manualRegister()instead. - Multiple
GameLifecycleManagercomponents in the scene — the second one self-destructs. Keep exactly one on a persistent node. - Forgetting
IDisposableon systems holding signal subscriptions or timers — causes leaks on scene reload.
Gotchas
Section titled “Gotchas”- Cocos lifecycle order: onLoad → onEnable → start → update — this is the phase order WITHIN one component, not a guarantee between components. Wiring listeners in
startinstead ofonEnableskips the first re-enable cycle. - Cross-component ordering is NOT guaranteed within a phase. Cocos guarantees every
onLoad()in the scene finishes before anystart()runs, and everystart()finishes before the first scheduler tick — but it does not guarantee order between sibling components in the same phase:onLoad()of component A may run before or afteronLoad()of component B, depending on scene tree order.- Passing a value between two components → do it in
onLoad()(the earlier-guaranteed phase). If you don’t know which side runs first, use a two-way handshake: both sides are singletons, and each tries to find the other in its ownonLoad(); sinceonLoad()is single-threaded and non-interleaved, exactly one direction always succeeds. - A side-effect depending on state built by another component’s
start()→ don’t assumestart()order. Latch the desired value and flush once viascheduleOnce(fn, 0)— the first scheduler tick is guaranteed to run after everystart(). - Anti-pattern: moving a data read from
onLoad()tostart()“to make sure the other side is ready” — this swaps anonLoad()-ordering trap for astart()-ordering trap, and if the other component throws when required data is missing instart(), it turns a silent bug into a crash.
- Passing a value between two components → do it in
onDisablefires on scene close beforeonDestroy— release resources inonDestroy, notonDisable, to survive scene navigation.updateis called even on disabled components in some Cocos versions — guard withthis.enabledor migrate tolateUpdate.