t1k:cocos:playable:layout
| Field | Value |
|---|---|
| Module | playable |
| Version | 0.15.0 |
| Effort | high |
| Tools | — |
Keywords: ads, aspect ratio, cocos, layout, playable, responsive, safe area, UI
How to invoke
Section titled “How to invoke”/t1k:cocos:playable:layoutResponsiveLayoutService
Section titled “ResponsiveLayoutService”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
Details
Section titled “Details”- API reference: categories, signal, properties, methods
- Usage examples: signal subscription, adjustForAspectRatio, scaleToFit, getSafeArea
Integration Points
Section titled “Integration Points”ScreenResizedSignalfires once ononLoadimmediately after detection — subscribe beforeonLoadcompletes if you need the initial value, or readservice.categorydirectly instart().- Plays well with the parameter system: subscribe to
ScreenResizedSignalinParameterControllerto 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
ResponsiveLayoutServiceinstance is allowed; a secondonLoadcall destroys the newer component.
Common Mistakes
Section titled “Common Mistakes”- Reading
ResponsiveLayoutService.instancebefore the component’sonLoadhas run — returns null. Always guard with a null check or access instart()/ after a frame delay. - Calling
adjustForAspectRatiowithout subscribing toScreenResizedSignal— positions only set once and will not update if browser is resized. - Using
scaleToFiton a node whoseUITransform.contentSizeis zero — method early-returns silently. SetcontentSizeexplicitly. - ES2017 target: do not use optional chaining (
?.) or nullish coalescing (??) in game code.
Gotchas
Section titled “Gotchas”- Cocos
Widgetaligns relative to parent at layout time — animating the parent breaks the widget unlessalignFlagsare reapplied per frame (orWidget.targetis 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’sNode.EventType.SIZE_CHANGEDand sets that same node’s size inside the handler is inherently self-triggering. It only appears stable because Cocos does not re-emitSIZE_CHANGEDwhen 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. anAspectRatioSizeFitter.apply()that runsUITransform.setContentSize, or resizing referenced fitters — each pass changes a size again,SIZE_CHANGEDre-fires, and because Cocos emits it synchronously the handler re-enters itself on the stack (not the event queue), producingRangeError: Maximum call stack size exceeded. Fix — guard the handler with a boolean flag in try/finally: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 /private isResizing = false;private resizeToFullScreen(): void {if (this.isResizing) return;this.isResizing = true;try {// set sizes + trigger child fitters} finally {this.isResizing = false;}}contentSizefitter and a full-screen cover resizer must NOT share a node — they compete for that node’scontentSize(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 — aUIScreenResolutioncomponent chainingAspectRatioSizeFitter.apply()inside its ownSIZE_CHANGEDhandler.)