Skip to content

t1k:cocos:playable:layout

FieldValue
Moduleplayable
Version0.15.0
Efforthigh
Tools

Keywords: ads, aspect ratio, cocos, layout, playable, responsive, safe area, UI

/t1k:cocos:playable:layout

Cocos Component singleton that detects device aspect ratio on load and on every canvas resize, categorizes it into NARROW/STANDARD/WIDE, fires ScreenResizedSignal via SignalBus, and provides helpers for adaptive node positioning and scaling. Attach to a persistent node in the scene (e.g. Canvas root). See also: t1k-cocos-playable-signalbus.

Import paths:

  • db://assets/PLAGameFoundation/layout/ResponsiveLayoutService
  • ScreenResizedSignal fires once on onLoad immediately after detection — subscribe before onLoad completes if you need the initial value, or read service.category directly in start().
  • Plays well with the parameter system: subscribe to ScreenResizedSignal in ParameterController to re-apply layout-sensitive parameter values when the device rotates or the browser is resized.
  • getSafeArea() returns zeros on web — safe to call unconditionally.
  • Only one ResponsiveLayoutService instance is allowed; a second onLoad call destroys the newer component.
  • Reading ResponsiveLayoutService.instance before the component’s onLoad has run — returns null. Always guard with a null check or access in start() / after a frame delay.
  • Calling adjustForAspectRatio without subscribing to ScreenResizedSignal — positions only set once and will not update if browser is resized.
  • Using scaleToFit on a node whose UITransform.contentSize is zero — method early-returns silently. Set contentSize explicitly.
  • ES2017 target: do not use optional chaining (?.) or nullish coalescing (??) in game code.
  • Cocos Widget aligns relative to parent at layout time — animating the parent breaks the widget unless alignFlags are reapplied per frame (or Widget.target is locked).
  • Safe-area inset on iOS in-app webview is not always reported — provide a manual fallback inset of 24px top, 12px bottom.
  • Self-resizing layout Components need a re-entrancy guard on SIZE_CHANGED — a Component that both listens to its OWN node’s Node.EventType.SIZE_CHANGED and sets that same node’s size inside the handler is inherently self-triggering. It only appears stable because Cocos does not re-emit SIZE_CHANGED when a size is set to its current value (the handler converges to a fixed point). The moment you chain ANY additional size mutation into that handler — e.g. an AspectRatioSizeFitter.apply() that runs UITransform.setContentSize, or resizing referenced fitters — each pass changes a size again, SIZE_CHANGED re-fires, and because Cocos emits it synchronously the handler re-enters itself on the stack (not the event queue), producing RangeError: Maximum call stack size exceeded. Fix — guard the handler with a boolean flag in try/finally:
    private isResizing = false;
    private resizeToFullScreen(): void {
    if (this.isResizing) return;
    this.isResizing = true;
    try {
    // set sizes + trigger child fitters
    } finally {
    this.isResizing = false;
    }
    }
    Every re-entrant emission during the pass hits the guard and returns; after the pass the flag clears with nothing queued. Also: an aspect-ratio / contentSize fitter and a full-screen cover resizer must NOT share a node — they compete for that node’s contentSize (last writer in the pass wins). Keep fitters on CHILD nodes so they read the sized parent and size only themselves. (Observed 2026-07-02: Cocos 3.8.7 editor crash — a UIScreenResolution component chaining AspectRatioSizeFitter.apply() inside its own SIZE_CHANGED handler.)