Skip to content

prefab-only-construction-cocos

Prefab-Only Construction (Cocos) — No Code-Built Nodes / UI / Scene-Wiring

Section titled “Prefab-Only Construction (Cocos) — No Code-Built Nodes / UI / Scene-Wiring”

Scope: Cocos Creator runtime & gameplay TypeScript (cc.Component, node, prefab). Does NOT govern editor extensions, genuinely reusable factory utilities, or tests (see Carve-outs). This is the Cocos sibling of prefab-only-construction-unity.md.

In Cocos runtime/gameplay code you MUST NOT construct node hierarchies, UI, or wire scene references in code. Everything visual/structural is authored as a prefab (or in the scene) in the editor and spawned as a prefab at runtime; cross-node references are assigned via @property in the editor, saved in the scene/prefab asset.

Banned in runtime/gameplay code:

  • new Node(...) + addChild(...) to build a runtime visual/entity or a container.
  • addComponent(cc.Sprite) / addComponent(cc.Label) / addComponent(...) chains that assemble a visual hierarchy in code.
  • Building UITransform / Widget / Layout / label / sprite trees procedurally.
  • Code scene-wiring: find(...), getChildByName(...), getComponentInChildren(...) used to locate/assemble what should be an editor-assigned @property reference. Cross-node references are wired in the editor.
  • Auto-creating a “root”/container node in code (e.g. a BoardRoot) — that is a prefab too.

The ONE permitted construction form — spawn a prefab:

// Author CardView.prefab / BoardRoot.prefab in the editor, then:
const node = instantiate(this.cardPrefab); // or ObjectPoolManager spawn
this.boardRoot.addChild(node);
node.getComponent(CardView)!.configure(this.config, size); // set DATA on existing refs
node.getComponent(CardView)!.bind(card);
  • instantiate(prefab) is allowed and encouraged — that IS prefab spawning.
  • Prefer a pool (ObjectPoolManager, see t1k-cocos-playable-object-pool) for anything spawned/recycled repeatedly; code-quality-cocos.md’s pattern-trigger table already makes pooling mandatory for per-spawn/in-loop instantiate()/new Node(). A plain instantiate(prefab) is fine for a fixed one-time set.
  • After spawning, configure via a configure(...)/bind(...) method on the prefab’s @ccclass component that sets data (size, color, text, spriteFrame) on already-serialized @property refs — it must NOT create children.
  • Every runtime visual, entity, HUD element, popup, tile, card, and container/root = its own prefab. Adding a pure-logic (non-visual) component to an already-spawned prefab instance is fine.

When the prefab or scene must be created or modified — new node, new component, wiring a @property, restructuring a hierarchy — drive the Cocos Creator MCP tools, NOT a hand-written scene-building script:

NeedTool
Create / modify / delete a nodemanage_node
Restructure / reparent a hierarchymanage_node_hierarchy
Apply changes into a prefabmanage_prefab (update)
Add / remove / set a component’s propertiesmanage_component
Create / query / save the scenemanage_scene, manage_scene_query
Create a new .ts/asset so @ccclass registersmanage_asset (create, url=db://...)
Confirm you’re targeting the right project firstmanage_project (get_info path)

A one-off “build my scene / wire my refs” script is not authoring — use the MCP. Load t1k-cocos-playable-editor-tools + t1k-cocos-playable-tooling-gotchas and heed the gotchas that make this succeed first try:

  • A new .ts written with the plain Write tool is not auto-imported and its @ccclass does not register — create scripts via manage_asset action=create url=db://assets/....
  • A custom @ccclass __type__ in scene/prefab JSON uses the compressed cid (not the raw uuid, not the class name); built-in cc.* components use the literal class name.
  • A @property node reference takes a node uuid, not a component uuid.
  • Any legitimately runtime-created node defaults to Layers.Enum.DEFAULT and renders invisible under a Canvas — copy the parent’s layer before addChild().
  • manage_prefab update may report “Editor rejected apply-prefab” even on success — do NOT retry; verify by grepping the .prefab for the child uuid.

Carve-outs (allowed — this rule does not apply)

Section titled “Carve-outs (allowed — this rule does not apply)”
  • Editor extension tooling (an editor panel/extension whose job is to author the prefab this rule requires) may build hierarchies freely.
  • Genuinely reusable factory utilities — a shared, tested spawner used from ≥3 sites — may construct nodes; a per-feature one-off may not.
  • Pure-logic (non-visual, non-@ccclass) TypeScript — plain domain/application classes have no nodes to build (see code-conventions-cocos.md § “Feature modules & testability”).
  • Unit tests (Vitest, environment: 'node') may construct whatever they need.
  • 1 documented one-off bootstrap node created once at boot (e.g. a DontDestroyOnLoad-style service host) — document the choice in a code comment so the next reader doesn’t “fix” it.
  • The prefab/scene is the SSOT for visuals & layout: artists/designers edit it in the editor without touching code, and what you see in the editor is what ships.
  • Code-built UI can’t be previewed, drifts from the editor, mixes presentation with logic, and re-authors the same hierarchy on every developer’s read.
  • Editor-@property references are visible, diffable, and don’t silently resolve to null on rename the way find("Name") string lookups do — and they preserve the UUID identity that game-asset-slot-handoff-cocos.md depends on.

If a new Node(...), addComponent(cc.Sprite), createChild/createLabel, or find/getChildByName in runtime code could be replaced by instantiate(prefab) (or a pool spawn) + a configure()/bind() data call, it is a violation — author the prefab via the MCP, wire its children as @property in the editor, spawn it. If the construction is genuinely an editor extension, a reusable factory, a test, or a documented one-off bootstrap, the rule does not apply — say so in a code comment.

A tools: grant does not guarantee the MCP is reachable at runtime

Section titled “A tools: grant does not guarantee the MCP is reachable at runtime”

t1k-cocos-developer and t1k-cocos-debugger carry mcp__cocos-creator__* grants so they can follow this rule directly. Under model-router transparent mode, a routed hop only forwards MCP servers listed in the consumer’s modelRouter.security.allowedMcpServers — a sonnet-tier agent (t1k-cocos-developer) can be delegated to a cheap provider with no cocos-creator entry in that allowlist, in which case the grant is present in frontmatter but absent at runtime. t1k-cocos-debugger is model: opus, which never routes, so this only bites the implementer path. If a routed t1k-cocos-developer reports the mcp__cocos-creator__* tools as unavailable, check the consumer’s allowedMcpServers before assuming the kit regressed.

  • prefab-only-construction-unity.md — the Unity sibling (this rule mirrors it for Cocos).
  • code-quality-cocos.md — mandatory pattern triggers (pooling for per-spawn new Node()).
  • code-conventions-cocos.md — @ccclass/@property, SignalBus named-method rule, logic outside cc.Component.
  • game-asset-slot-handoff-cocos.md — UUID identity: why overwrite-in-place beats add-then-delete.
  • t1k-cocos-playable-object-pool — spawn/recycle prefabs through ObjectPoolManager.
  • t1k-cocos-playable-editor-tools / t1k-cocos-playable-tooling-gotchas — Cocos MCP authoring + gotchas.