@pix3/cli 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +754 -0
  3. package/dist/index.js +494 -0
  4. package/dist/runtime-types.json +1 -0
  5. package/dist/smoke/prebuilt/build-7MIXIMSZ.mjs +948 -0
  6. package/dist/smoke/prebuilt/chunk-J2SJVMDO.mjs +2 -0
  7. package/dist/smoke/prebuilt/chunk-OJXQXIIQ.mjs +239 -0
  8. package/dist/smoke/prebuilt/chunk-P2CBRXPJ.mjs +4050 -0
  9. package/dist/smoke/prebuilt/runtime.mjs +2 -0
  10. package/dist/smoke/prebuilt/smoke-worker.mjs +3 -0
  11. package/dist/smoke/prebuilt/three.mjs +2 -0
  12. package/dist/smoke/prebuilt/tree-defaults.mjs +2 -0
  13. package/dist/validate/prebuilt/build-AUGKXER3.js +948 -0
  14. package/dist/validate/prebuilt/bundle-main.js +24 -0
  15. package/dist/validate/prebuilt/chunk-6ACT6FNV.js +4050 -0
  16. package/dist/validate/prebuilt/chunk-BS2OZM7U.js +239 -0
  17. package/dist/validate/prebuilt/runtime.js +2 -0
  18. package/dist/validate/prebuilt/three.js +2 -0
  19. package/kit/files/.claude/skills/pix3-nodes/SKILL.md +210 -0
  20. package/kit/files/.claude/skills/pix3-nodes/reference.md +1331 -0
  21. package/kit/files/.claude/skills/pix3-scene-format/SKILL.md +235 -0
  22. package/kit/files/.claude/skills/pix3-scene-format/reference.md +176 -0
  23. package/kit/files/.claude/skills/pix3-scripts/SKILL.md +281 -0
  24. package/kit/files/.claude/skills/pix3-scripts/reference.md +638 -0
  25. package/kit/files/.claude/skills/pix3-verify/SKILL.md +386 -0
  26. package/kit/files/.claude/skills/pix3-verify/reference.md +21 -0
  27. package/kit/files/AGENTS.md +203 -0
  28. package/kit/files/CLAUDE.md +1 -0
  29. package/kit/kit.json +24 -0
  30. package/package.json +54 -0
  31. package/templates/empty-2d/files/README.md +20 -0
  32. package/templates/empty-2d/files/scenes/main.pix3scene +68 -0
  33. package/templates/empty-2d/files/sprites/pix3-logo.png +0 -0
  34. package/templates/empty-2d/template.yaml +7 -0
  35. package/templates/empty-3d/files/README.md +20 -0
  36. package/templates/empty-3d/files/scenes/main.pix3scene +108 -0
  37. package/templates/empty-3d/files/sprites/pix3-logo.png +0 -0
  38. package/templates/empty-3d/template.yaml +7 -0
  39. package/templates/idea-blank/files/README.md +22 -0
  40. package/templates/idea-blank/files/scenes/main.pix3scene +37 -0
  41. package/templates/idea-blank/template.yaml +10 -0
  42. package/templates/minigame-2d/files/README.md +44 -0
  43. package/templates/minigame-2d/files/design/tests/reachability.json +1 -0
  44. package/templates/minigame-2d/files/design/tests/routines/mute-music.json +44 -0
  45. package/templates/minigame-2d/files/scenes/main.pix3scene +84 -0
  46. package/templates/minigame-2d/files/scenes/menu.pix3scene +118 -0
  47. package/templates/minigame-2d/files/scenes/ui/settings-window.pix3scene +138 -0
  48. package/templates/minigame-2d/files/scripts/GameFlow.ts +133 -0
  49. package/templates/minigame-2d/files/scripts/MenuFlow.ts +217 -0
  50. package/templates/minigame-2d/files/scripts/SettingsWindow.ts +198 -0
  51. package/templates/minigame-2d/template.yaml +10 -0
  52. package/templates/playable-2d/files/README.md +30 -0
  53. package/templates/playable-2d/files/design/recipe.md +83 -0
  54. package/templates/playable-2d/files/design/tests/reachability.json +4 -0
  55. package/templates/playable-2d/files/design/tests/routines/intro-to-cta.json +32 -0
  56. package/templates/playable-2d/files/scenes/main.pix3scene +111 -0
  57. package/templates/playable-2d/files/scenes/ui/end-screen.pix3scene +91 -0
  58. package/templates/playable-2d/files/scenes/ui/intro.pix3scene +65 -0
  59. package/templates/playable-2d/files/scripts/CtaButton.ts +55 -0
  60. package/templates/playable-2d/files/scripts/GameFlow.ts +256 -0
  61. package/templates/playable-2d/files/sprites/pix3-logo.png +0 -0
  62. package/templates/playable-2d/template.yaml +9 -0
  63. package/templates/playable-3d/files/README.md +32 -0
  64. package/templates/playable-3d/files/design/recipe.md +127 -0
  65. package/templates/playable-3d/files/design/tests/reachability.json +4 -0
  66. package/templates/playable-3d/files/design/tests/routines/intro-to-cta.json +32 -0
  67. package/templates/playable-3d/files/scenes/main.pix3scene +230 -0
  68. package/templates/playable-3d/files/scenes/ui/end-screen.pix3scene +91 -0
  69. package/templates/playable-3d/files/scenes/ui/intro.pix3scene +65 -0
  70. package/templates/playable-3d/files/scripts/CtaButton.ts +55 -0
  71. package/templates/playable-3d/files/scripts/GameFlow.ts +309 -0
  72. package/templates/playable-3d/files/scripts/GameRules.ts +290 -0
  73. package/templates/playable-3d/files/scripts/ScoreHud.ts +113 -0
  74. package/templates/playable-3d/template.yaml +9 -0
  75. package/templates/recipe-arena-2d/files/README.md +48 -0
  76. package/templates/recipe-arena-2d/files/design/recipe.md +127 -0
  77. package/templates/recipe-arena-2d/files/design/tests/reachability.json +4 -0
  78. package/templates/recipe-arena-2d/files/design/tests/routines/terminal-retry.json +23 -0
  79. package/templates/recipe-arena-2d/files/scenes/main.pix3scene +292 -0
  80. package/templates/recipe-arena-2d/files/scenes/menu.pix3scene +100 -0
  81. package/templates/recipe-arena-2d/files/scenes/prefabs/hazard.pix3scene +42 -0
  82. package/templates/recipe-arena-2d/files/scenes/prefabs/pickup.pix3scene +45 -0
  83. package/templates/recipe-arena-2d/files/scenes/ui/result.pix3scene +85 -0
  84. package/templates/recipe-arena-2d/files/scripts/GameRules.ts +368 -0
  85. package/templates/recipe-arena-2d/files/scripts/MenuFlow.ts +106 -0
  86. package/templates/recipe-arena-2d/files/scripts/PlayerController.ts +151 -0
  87. package/templates/recipe-arena-2d/files/scripts/ScoreHud.ts +94 -0
  88. package/templates/recipe-arena-2d/files/scripts/Spawner.ts +214 -0
  89. package/templates/recipe-arena-2d/files/scripts/TouchRules.ts +174 -0
  90. package/templates/recipe-arena-2d/files/sprites/ph-avatar.png +0 -0
  91. package/templates/recipe-arena-2d/files/sprites/ph-hazard.png +0 -0
  92. package/templates/recipe-arena-2d/files/sprites/ph-pickup.png +0 -0
  93. package/templates/recipe-arena-2d/template.yaml +10 -0
  94. package/templates/recipe-blank-2d/files/README.md +61 -0
  95. package/templates/recipe-blank-2d/files/design/recipe.md +148 -0
  96. package/templates/recipe-blank-2d/files/design/tests/reachability.json +4 -0
  97. package/templates/recipe-blank-2d/files/design/tests/routines/restart.json +27 -0
  98. package/templates/recipe-blank-2d/files/design/tests/routines/terminal-retry.json +23 -0
  99. package/templates/recipe-blank-2d/files/scenes/main.pix3scene +224 -0
  100. package/templates/recipe-blank-2d/files/scenes/ui/result.pix3scene +85 -0
  101. package/templates/recipe-blank-2d/files/scripts/GameRules.ts +350 -0
  102. package/templates/recipe-blank-2d/files/scripts/ScoreHud.ts +110 -0
  103. package/templates/recipe-blank-2d/files/sprites/ph-bg.png +0 -0
  104. package/templates/recipe-blank-2d/files/sprites/ph-capsule.png +0 -0
  105. package/templates/recipe-blank-2d/files/sprites/ph-circle.png +0 -0
  106. package/templates/recipe-blank-2d/files/sprites/ph-orb.png +0 -0
  107. package/templates/recipe-blank-2d/files/sprites/ph-ring.png +0 -0
  108. package/templates/recipe-blank-2d/files/sprites/ph-star.png +0 -0
  109. package/templates/recipe-blank-2d/template.yaml +10 -0
  110. package/templates/recipe-bouncer-2d/files/README.md +81 -0
  111. package/templates/recipe-bouncer-2d/files/design/recipe.md +146 -0
  112. package/templates/recipe-bouncer-2d/files/design/tests/reachability.json +4 -0
  113. package/templates/recipe-bouncer-2d/files/design/tests/routines/terminal-retry.json +23 -0
  114. package/templates/recipe-bouncer-2d/files/scenes/main.pix3scene +615 -0
  115. package/templates/recipe-bouncer-2d/files/scenes/menu.pix3scene +191 -0
  116. package/templates/recipe-bouncer-2d/files/scenes/ui/result.pix3scene +87 -0
  117. package/templates/recipe-bouncer-2d/files/scripts/BallBody.ts +298 -0
  118. package/templates/recipe-bouncer-2d/files/scripts/GameRules.ts +377 -0
  119. package/templates/recipe-bouncer-2d/files/scripts/MenuFlow.ts +109 -0
  120. package/templates/recipe-bouncer-2d/files/scripts/PaddleController.ts +112 -0
  121. package/templates/recipe-bouncer-2d/files/scripts/ScoreHud.ts +94 -0
  122. package/templates/recipe-bouncer-2d/files/scripts/TouchRules.ts +263 -0
  123. package/templates/recipe-bouncer-2d/files/scripts/ball-collision.ts +307 -0
  124. package/templates/recipe-bouncer-2d/files/sprites/ph-ball.png +0 -0
  125. package/templates/recipe-bouncer-2d/files/sprites/ph-bg.png +0 -0
  126. package/templates/recipe-bouncer-2d/files/sprites/ph-bumper.png +0 -0
  127. package/templates/recipe-bouncer-2d/files/sprites/ph-paddle.png +0 -0
  128. package/templates/recipe-bouncer-2d/template.yaml +10 -0
  129. package/templates/recipe-grid-3d/files/README.md +22 -0
  130. package/templates/recipe-grid-3d/files/design/recipe.md +110 -0
  131. package/templates/recipe-grid-3d/files/scenes/main.pix3scene +169 -0
  132. package/templates/recipe-grid-3d/files/scenes/menu.pix3scene +100 -0
  133. package/templates/recipe-grid-3d/files/scenes/ui/result.pix3scene +105 -0
  134. package/templates/recipe-grid-3d/files/scripts/GridBoard.ts +319 -0
  135. package/templates/recipe-grid-3d/files/scripts/GridRules.ts +278 -0
  136. package/templates/recipe-grid-3d/files/scripts/MenuFlow.ts +106 -0
  137. package/templates/recipe-grid-3d/files/scripts/ScoreHud.ts +94 -0
  138. package/templates/recipe-grid-3d/template.yaml +10 -0
  139. package/templates/recipe-tapper-2d/files/README.md +39 -0
  140. package/templates/recipe-tapper-2d/files/design/recipe.md +132 -0
  141. package/templates/recipe-tapper-2d/files/design/tests/reachability.json +4 -0
  142. package/templates/recipe-tapper-2d/files/design/tests/routines/terminal-retry.json +23 -0
  143. package/templates/recipe-tapper-2d/files/scenes/main.pix3scene +269 -0
  144. package/templates/recipe-tapper-2d/files/scenes/menu.pix3scene +100 -0
  145. package/templates/recipe-tapper-2d/files/scenes/prefabs/hazard.pix3scene +42 -0
  146. package/templates/recipe-tapper-2d/files/scenes/prefabs/target.pix3scene +45 -0
  147. package/templates/recipe-tapper-2d/files/scenes/ui/result.pix3scene +85 -0
  148. package/templates/recipe-tapper-2d/files/scripts/GameRules.ts +368 -0
  149. package/templates/recipe-tapper-2d/files/scripts/MenuFlow.ts +106 -0
  150. package/templates/recipe-tapper-2d/files/scripts/ScoreHud.ts +94 -0
  151. package/templates/recipe-tapper-2d/files/scripts/Spawner.ts +214 -0
  152. package/templates/recipe-tapper-2d/files/scripts/TouchRules.ts +192 -0
  153. package/templates/recipe-tapper-2d/files/sprites/ph-hazard.png +0 -0
  154. package/templates/recipe-tapper-2d/files/sprites/ph-target.png +0 -0
  155. package/templates/recipe-tapper-2d/template.yaml +10 -0
@@ -0,0 +1,235 @@
1
+ ---
2
+ name: pix3-scene-format
3
+ description: The .pix3scene YAML format — file header, node shape (id/type/name/properties/children/components), component shape, 2D transform and layout blocks, res:// references, textures, prefab instances and overrides, scenes/ui overlays and the visible/initiallyVisible split, plus how to translate design/recipe.md tool names into file edits. Use BEFORE creating or editing any .pix3scene file (scene, prefab or overlay) in this project.
4
+ ---
5
+
6
+ <!-- Pix3 agent kit 1.6.0 -->
7
+ # `.pix3scene` format
8
+
9
+ Scenes, prefabs and overlays are the same file type: YAML with a `.pix3scene` extension.
10
+ "Prefab" only means "a scene file that other scenes instance". There is no `.pix3prefab`.
11
+ The engine's own statement of the format (principles, skinned UI controls, Spine nodes) is in
12
+ `reference.md` beside this file; where the two differ, this page describes what the loader and
13
+ `pix3 validate` actually do.
14
+
15
+ ## Before you write
16
+
17
+ - Re-read the file you are about to change — the human may have edited it in the editor.
18
+ - Change only the lines you mean to change. A whole-file rewrite from memory drops what the
19
+ human (or the editor) wrote since you last read it.
20
+ - If your tools support it, write to a temporary file and rename it over the target. Not
21
+ required for correctness — the editor waits for the file to stop changing — but cleaner.
22
+ - Then `pix3 check` (see `pix3-verify`).
23
+
24
+ ## File
25
+
26
+ ```yaml
27
+ version: 1.0.0 # the only format version; copy it
28
+ metadata: # optional, free-form
29
+ author: Pix3 Recipe
30
+ description: Tapper gameplay — falling objects
31
+ root: # list of root nodes; at least one
32
+ - id: game-root
33
+ type: Group2D
34
+ ...
35
+ ```
36
+
37
+ `#` lines are comments and may appear anywhere. Top-level keys are exactly `version`,
38
+ `description`, `metadata`, `root`.
39
+
40
+ ## Node
41
+
42
+ ```yaml
43
+ - id: spawner-targets # REQUIRED. Unique across the scene incl. instanced prefabs.
44
+ type: Group2D # REQUIRED for a plain node. Exact class name (see pix3-nodes).
45
+ name: Spawner Targets # display name; free to change
46
+ groups: [spawners] # optional runtime groups
47
+ properties: # node properties — names from pix3-nodes
48
+ width: 900
49
+ height: 60
50
+ transform:
51
+ position: [0, 690]
52
+ scale: [1, 1]
53
+ rotation: 0
54
+ components: # optional; script components
55
+ - id: target-spawner
56
+ type: user:Spawner
57
+ enabled: true
58
+ config:
59
+ prefab: res://scenes/prefabs/target.pix3scene
60
+ intervalSec: 0.85
61
+ children: [] # optional; child nodes, same shape, recursive
62
+ ```
63
+
64
+ Rules:
65
+
66
+ - **`id`**: short, readable, kebab-case, unique (`combo-label`, `spawner-bonus`). Two nodes
67
+ with the same id fail the load. Never rename an id the recipe lists as stable — scripts
68
+ find nodes by id; rename `name` instead.
69
+ - **`type`**: must be a real node type. An unknown or misspelled type does **not** fail the
70
+ load — the node becomes an inert placeholder that draws and does nothing.
71
+ - **`properties`**: unknown keys are kept silently and do nothing; a value of the wrong type
72
+ (a string where a number belongs) is silently replaced by the default. Check names and
73
+ types against `pix3-nodes`.
74
+ - **`children`**: tree order is 2D paint order — a later sibling, or a deeper node, draws on
75
+ top. `zIndex` overrides it.
76
+
77
+ ### 2D transform
78
+
79
+ ```yaml
80
+ transform:
81
+ position: [x, y] # design pixels; the node's CENTRE, relative to its parent
82
+ scale: [1, 1]
83
+ rotation: 0 # DEGREES in YAML; positive = counter-clockwise (Y is up)
84
+ ```
85
+
86
+ 2D space: origin at the centre of the screen/parent, **X right, Y up**. The recipes use a
87
+ 1080 x 1920 portrait design (top edge y = 960, bottom y = -960); other projects read
88
+ `viewportBaseSize` / `projectType` in `pix3project.yaml` — a `3d` project may have loose
89
+ top-level 2D nodes (a HUD) and no 2D root at all, so position new HUD relative to the HUD
90
+ nodes that are already there. Scripts see the rotation in
91
+ radians (`node.rotation.z`). `{ x: 0, y: 690 }` is also accepted for a vector, and so is a flat
92
+ `position:` beside `transform` (read-compat), but write the `transform:` block the editor saves.
93
+
94
+ 3D nodes (`Node3D`, meshes, cameras, lights) use
95
+ `transform: { position: [x, y, z], rotationEuler: [dx, dy, dz], scale: [1, 1, 1] }`,
96
+ rotation in degrees.
97
+
98
+ ### Anchor layout (2D)
99
+
100
+ ```yaml
101
+ layout:
102
+ enabled: true
103
+ horizontalAlign: left # left | center | right | stretch
104
+ verticalAlign: top # top | center | bottom | stretch
105
+ ```
106
+
107
+ The node keeps its authored distance to that edge of its parent when the parent resizes
108
+ (a different screen aspect). `stretch` keeps both margins and resizes the node. Position is
109
+ still authored in `transform.position`. HUD widgets anchor to screen edges; full-screen
110
+ backgrounds and roots use `stretch` on both axes.
111
+
112
+ **A new HUD node must not overlap the HUD that is already there.** Before you place it, read
113
+ the `position`, `width`/`height` and anchoring of its siblings and pick a free spot (a combo
114
+ label placed "top-right" once landed under the game's Shop button). With the live channel,
115
+ confirm it with a `viewport_screenshot` while the game runs; without it, ask the human to look.
116
+
117
+ Flow (stack children in a row/column) is a separate block:
118
+ `flow: { enabled: true, direction: vertical, gap: 16, paddingX: 0, paddingY: 0, align: start, autoSize: false }`.
119
+
120
+ The flat names `layoutEnabled`, `horizontalAlign`, `flowEnabled`, `flowGap` … are the
121
+ inspector's names for the same values: on a plain node they belong in these blocks (flat they
122
+ are ignored — `pix3 validate` says so); on an **instance** node they are the only form (below).
123
+
124
+ ### Values
125
+
126
+ - **Colour**: a quoted hex string, `color: "#141a2e"`. Always quote — an unquoted `#` starts
127
+ a YAML comment and the value becomes empty.
128
+ - **Asset path**: `res://` + path from the project root (the folder with `pix3project.yaml`),
129
+ whatever the layout: `res://sprites/ph-target.png` in a recipe, `res://src/assets/textures/x.png`
130
+ in a project that keeps assets under `src/`. Follow the folders the project already has; a
131
+ new project from `pix3 new` uses one folder per type at the root (`sprites/`, `audio/`,
132
+ `fonts/`, `models/`, `spine/`, `scripts/`, `scenes/`).
133
+ - **Texture slot**: `texture: { type: 'texture', url: 'res://sprites/x.png' }` (same shape for
134
+ `textureNormal`, `textureFill`, …). The saver always writes this form; a bare
135
+ `texturePath: 'res://…'` string is only read for compatibility.
136
+ - **Strings** with `:` `#` `{` or leading spaces: quote them (`label: "SCORE 0"`).
137
+
138
+ ### Shader effects (`Sprite2D`, `AnimatedSprite2D`, `Button2D` only)
139
+
140
+ ```yaml
141
+ effects:
142
+ - type: core:tint
143
+ params: { color: "#7ee787", amount: 1 }
144
+ ```
145
+
146
+ `core:tint` multiplies a near-white placeholder PNG to the colour — this is how recipe
147
+ placeholders get their palette. Also `core:adjust`, `core:grayscale`, `core:flash`,
148
+ `core:uv-scroll`, `core:dissolve`. Other 2D nodes do not read `effects` (a `ColorRect2D` takes
149
+ its colour from `color`). On a `GeometryMesh` the stack lives at `material.effects`.
150
+
151
+ ## Component
152
+
153
+ ```yaml
154
+ - id: combo # readable id; keep it unique in the scene
155
+ type: user:Combo # user:<ExportedClassName> (scripts/*.ts) or core:<Name>
156
+ enabled: true
157
+ config: # keys = names in the component's getPropertySchema()
158
+ windowSec: 1.2
159
+ ```
160
+
161
+ - `config` is **merged over** the defaults the script's constructor sets, so write only the
162
+ keys you want to differ.
163
+ - A `user:X` whose class does not exist (or has no `static getPropertySchema()`) is kept as
164
+ "pending" with a console warning and never runs.
165
+ - Built-ins (`core:*`) and every config key they take, with defaults, are listed in
166
+ `.claude/skills/pix3-scripts/reference.md` ("`core:` components").
167
+
168
+ ## Prefab instance
169
+
170
+ ```yaml
171
+ - id: result-overlay
172
+ name: Result Overlay
173
+ instance: res://scenes/ui/result.pix3scene
174
+ properties:
175
+ visible: false
176
+ transform: { position: [0, 0] }
177
+ ```
178
+
179
+ - `instance:` **replaces** `type:`. The instanced file must contain **exactly one** root node.
180
+ - Instance `properties` are applied to the prefab's root through its **schema names**:
181
+ `visible`, `opacity`, `width`, `layoutEnabled`, `horizontalAlign`, … plus a `transform` block
182
+ (`position`, `rotation`, `scale`). The `layout:` / `flow:` blocks of a plain node are not read
183
+ here — write `layoutEnabled: true` + `horizontalAlign: right` instead.
184
+ - Do **not** add `components` or `children` to an instance node — they are ignored (the
185
+ content comes from the prefab). To change what is inside, edit the prefab file itself, or
186
+ override one inner node's properties:
187
+
188
+ ```yaml
189
+ overrides:
190
+ byLocalId:
191
+ retry-button: # an id below the prefab root; nested: parent-id/child-id
192
+ properties: { label: "AGAIN" } # schema names, as on the instance itself
193
+ ```
194
+
195
+ - Nodes inside an instance keep their authored ids when those are unique, so scripts can find
196
+ them by id (`retry-button` inside `scenes/ui/result.pix3scene` is found as `retry-button`).
197
+ - A prefab carries fixed defaults only (e.g. its `core:Hitbox2D` group). Tunables live on
198
+ plain nodes in the host scene.
199
+ - Where prefabs live: `scenes/prefabs/` (the recipes) or a top-level `prefabs/` — the export
200
+ treats both (and `scenes/ui/`) as prefabs, never as a boot scene.
201
+ - Spawned at runtime with `await this.scene.instantiate('res://scenes/prefabs/x.pix3scene', { parent })`.
202
+
203
+ ## Full-screen UI lives in `scenes/ui/`
204
+
205
+ A result card, pause menu or modal is its own file in `scenes/ui/`, instanced into the host
206
+ scene with `properties: { visible: false }`. Two different flags, both needed:
207
+
208
+ - `visible: false` on the **instance** — hides it in the **editor**, so `main.pix3scene`
209
+ opens on the game, not on a GAME OVER card. Never remove it.
210
+ - `initiallyVisible: false|true` on the **overlay file's root node** — what **play mode**
211
+ applies at start. A script reveals the overlay by setting `node.visible = true`.
212
+
213
+ Never put a full-screen dimmer/panel inline in `main.pix3scene`.
214
+
215
+ ## Recipe tool names → file edits
216
+
217
+ `design/recipe.md` was written for the in-editor agent. Without its tools:
218
+
219
+ | recipe.md says | you do |
220
+ | --- | --- |
221
+ | tunable with `component: "user:X"` | edit `config.<property>` of the component with `type: user:X` on the node with that `id` |
222
+ | tunable without `component` | edit `properties.<property>` on the node with that `id` (in the `transform` / `layout` / `flow` block where it belongs) |
223
+ | `set_property` | edit `properties` in the YAML |
224
+ | `set_component_property` | edit the component's `config` |
225
+ | `create_node` / `add_component` | add the node / component entry to the YAML |
226
+ | `fs_write` (+ `overwrite: true`) | write the file |
227
+ | `play_start`, `game_input`, `game_observe`, `game_run` | the same tools over the live channel when it is connected (`pix3-verify`); otherwise `pix3 check`, then `pix3 smoke <the scene you changed>`, then ask the human to press Play and tell them what to look for |
228
+
229
+ Values out of a tunable's `min`/`max` load as written (`pix3 validate` warns); the script's
230
+ schema `setValue` may clamp them.
231
+
232
+ ## Known gaps
233
+
234
+ - `metadata.description` is free text; nothing reads it but humans.
235
+ - Instance overrides of **components** inside a prefab are not supported from the host scene.
@@ -0,0 +1,176 @@
1
+ <!-- Pix3 agent kit 1.6.0 — generated from docs/pix3-specification.md ("Scene File Format"). Do not edit: pix3 kit --update regenerates it. -->
2
+ # `.pix3scene` — the engine specification
3
+
4
+ Excerpts of the engine's specification, as shipped with this CLI. `SKILL.md` beside this file
5
+ is the practical version and wins where they differ — in particular node ids may be readable
6
+ kebab-case, and there are no scene-format migrations today (`version: 1.0.0` is the only one).
7
+ The "Flat Asset Layout" principle is what `pix3 new` creates; an existing project keeps its own
8
+ layout (`res://` is always relative to the project root). Source paths quoted here (`src/…`, `packages/…`) are files of the Pix3 engine repository, not of this project.
9
+
10
+ ## Key principles
11
+
12
+ - Declarative: The file describes the composition and structure of the scene, not the process of its creation.
13
+ - Asset Referencing: Assets (models, textures) are not embedded in the file but are referenced via relative paths with a res:// prefix (path from the project root).
14
+ - Flat Asset Layout: a game project keeps one folder per asset type **at its root** — `scenes/`, `sprites/` (images/textures), `models/`, `audio/`, `fonts/`, `spine/` (skeleton + atlas + pages together), `scripts/`, `locales/` — with free subdivision inside (`sprites/ui/…`). There is no `assets/` wrapper folder; the project templates ship this layout and `defaultAssetFolder()` (`src/core/asset-categories.ts`) is the canonical mapping used when the editor creates an asset from a bare file name.
15
+ - SVG textures: an `.svg` works as a `Sprite2D` / `Button2D` texture only when its root `<svg>` carries `xmlns="http://www.w3.org/2000/svg"` and px `width`/`height` (a viewBox alone has no intrinsic size), it is self-contained, and the Blob it is decoded from is typed `image/svg+xml` — every read path therefore types image/audio blobs by extension when the stored type is generic (`CloudProjectCacheService.readBlob`, `PlayableHtmlBuildService.resolveMimeType`). `pix3 validate` checks referenced SVGs (`E_SVG_INVALID`, `E_SVG_NO_SIZE`, `W_SVG_VIEWBOX_ONLY`, `W_SVG_EXTERNAL_REF`).
16
+ - Composition: Complex scenes are assembled from simpler ones by instantiating other scene files.
17
+ - Unambiguous Structure: An explicit children key is used to denote the list of child nodes, which separates the hierarchy from the properties of the node itself.
18
+ - Unique Identification: Every node must have an id field. The value is a short, cryptographically secure unique identifier (similar to Nano ID) to provide a balance between file readability and the absolute reliability of references.
19
+ - Versioned Schema: Each file includes a `version` field; migrations are maintained in the SceneManager and run automatically on load.
20
+ - Conflict Resolution: Instance overrides always win over parent definitions. Duplicate IDs trigger validation errors during import.
21
+ - Forgiving Vocabulary: a `type:` the loader does not know does **not** fail the load — the node is built as a bare `NodeBase` placeholder that does nothing, so a scene written by a newer version (or with a typo) stays openable and editable. Because such a node is otherwise indistinguishable from a working one, it is reported on three surfaces: the renderability lint (`inert-nodes` in `scene_tree` / `game_observe` / play start), the loader diagnostic (`describeUnknownNodeType`, which suggests the nearest known name), and the Scene Tree row, which carries a warn badge and states the reason in its tooltip. `isInertNode` (`packages/pix3-runtime/src/core/renderability-lint.ts`) is the one predicate behind all three.
22
+
23
+ ## Skinned 2D UI controls
24
+
25
+ `Button2D`, `Checkbox2D`, `Slider2D` and `Bar2D` are colour-driven by default and
26
+ skinned by pointing their texture slots at `res://` sprites. A slot that is set
27
+ replaces the corresponding flat colour (the material tint goes white); a slot that
28
+ is unset keeps the colour, so an existing scene is unchanged and **every one of
29
+ these keys is omitted from a saved scene at its default**.
30
+
31
+ Slots per node: `Button2D` — `textureNormal` / `textureHover` / `texturePressed` /
32
+ `textureDisabled` (a missing state falls back to normal); `Checkbox2D` —
33
+ `textureBox`, `textureBoxChecked` (optional; falls back to `textureBox`),
34
+ `textureMark` (drawn over the box only while checked); `Slider2D` —
35
+ `textureTrack`, `textureFill`, `textureThumb`; `Bar2D` — `textureTrough`,
36
+ `textureFill`.
37
+
38
+ The four `sliceBorderLeft/Right/Top/Bottom` scalars are the same nine-slice insets
39
+ `TiledSprite2D` uses — source-texture pixels, Godot's `patch_margin_*` — so one
40
+ 64x64 skin fits a control of any size instead of being smeared. They apply to
41
+ `Button2D`'s state skins, `Slider2D`'s track and fill, and `Bar2D`'s trough and
42
+ fill; the slider thumb is drawn at its authored size and is never sliced. A fill
43
+ that shrinks with `value` is **re-cut** at the new width, so its end caps keep
44
+ their pixel size. All-zero (the default) is the plain stretch. A sliced skin opts
45
+ out of the 2D quad batcher, because the batcher extracts four *unit* corners, and
46
+ is excluded from the pre-launch texture atlas for the same reason.
47
+
48
+ Sprites for every one of these slots — and the matching insets — can be generated
49
+ and applied without leaving the editor; the files and their manifest are specified
50
+ in **§6.23 UI Kit Assets**.
51
+
52
+ ```yaml
53
+ - id: 'q7Bd-1mZ_kTn0-Vx4pA'
54
+ type: 'Button2D'
55
+ name: 'PlayButton'
56
+ properties:
57
+ transform:
58
+ position: { x: 0, y: -80 }
59
+ width: 240
60
+ height: 72
61
+ label: 'Play'
62
+ buttonAction: 'Submit'
63
+ textureNormal: { type: 'texture', url: 'res://sprites/ui/btn_normal.png' }
64
+ textureHover: { type: 'texture', url: 'res://sprites/ui/btn_hover.png' }
65
+ texturePressed: { type: 'texture', url: 'res://sprites/ui/btn_pressed.png' }
66
+ # 64x64 skin with 16 px corners -> fits 240x72 without smearing
67
+ sliceBorderLeft: 16
68
+ sliceBorderRight: 16
69
+ sliceBorderTop: 16
70
+ sliceBorderBottom: 16
71
+
72
+ - id: 'w3Kf-8sQ_2rLm-Yc6tB'
73
+ type: 'Bar2D'
74
+ name: 'HealthBar'
75
+ properties:
76
+ width: 200
77
+ height: 24
78
+ value: 100
79
+ maxValue: 100
80
+ textureTrough: { type: 'texture', url: 'res://sprites/ui/bar_trough.png' }
81
+ textureFill: { type: 'texture', url: 'res://sprites/ui/bar_fill.png' }
82
+ sliceBorderLeft: 12
83
+ sliceBorderRight: 12
84
+
85
+ - id: 'e5Nh-4tW_9pJk-Zd1uC'
86
+ type: 'Checkbox2D'
87
+ name: 'MusicToggle'
88
+ properties:
89
+ size: 32
90
+ checked: true
91
+ label: 'Music'
92
+ textureBox: { type: 'texture', url: 'res://sprites/ui/check_off.png' }
93
+ textureBoxChecked: { type: 'texture', url: 'res://sprites/ui/check_on.png' }
94
+ textureMark: { type: 'texture', url: 'res://sprites/ui/check_tick.png' }
95
+ ```
96
+
97
+ ## Spine skeleton nodes
98
+
99
+ A `SpineSkeleton2D` references its Spine export by two `res://` paths — the
100
+ skeleton (`.json` or `.skel`) and the atlas (`.atlas`). The atlas' page images are
101
+ NOT listed: they are named inside the atlas text and resolved relative to it (the
102
+ export asset collector parses `.atlas` files for the same reason). `texture` is an
103
+ optional single-page override.
104
+
105
+ ```yaml
106
+ - id: 'ZP2h-9kQ_1sVb0Ac-7mnE'
107
+ type: 'SpineSkeleton2D'
108
+ name: 'Hero'
109
+ properties:
110
+ transform:
111
+ position: { x: 0, y: -120 }
112
+ scale: { x: 1, y: 1 }
113
+ skeletonPath: 'res://spine/hero.json'
114
+ atlasPath: 'res://spine/hero.atlas'
115
+ animation: 'idle'
116
+ loop: true
117
+ isPlaying: true
118
+ skin: ''
119
+ timeScale: 1
120
+ defaultMix: 0.15
121
+ color: '#ffffff'
122
+ # Omitted unless enabled: twoColorTint, freeOnFinish, previewInEditor
123
+ ```
124
+
125
+ Rendering requires the optional `@esotericsoftware/spine-threejs` (`~4.3`) module,
126
+ registered by the host through `setSpineModuleLoader`. A scene with a Spine node
127
+ still loads without it — the node keeps its authored properties and warns.
128
+
129
+ ## Groups
130
+
131
+ Groups provide runtime categorization for nodes (for example, `enemies`, `ui`, `interactables`).
132
+
133
+ #### 6.19.1 Node API
134
+
135
+ - `addToGroup(group)`
136
+ - `removeFromGroup(group)`
137
+ - `isInGroup(group)`
138
+
139
+ #### 6.19.2 Scene API
140
+
141
+ `SceneManager` provides group-based queries and invocation:
142
+
143
+ - `getNodesInGroup(group)` - returns matching nodes in the active scene.
144
+ - `callGroup(group, method, ...args)` - calls matching component methods across grouped nodes.
145
+
146
+ `callGroup` performs runtime method checks and warns if no callable method is found.
147
+
148
+ #### 6.19.3 Serialization
149
+
150
+ Groups are serialized in `.pix3scene` nodes via `groups: []`.
151
+
152
+ ```yaml
153
+ root:
154
+ - id: player_001
155
+ type: Node3D
156
+ name: Player
157
+ groups: [actors, player]
158
+ ```
159
+
160
+ ## Prefabs — instance creation
161
+
162
+ Creating a prefab instance uses the `instance:` YAML key:
163
+
164
+ ```yaml
165
+ root:
166
+ - id: scene_root
167
+ type: Node3D
168
+ children:
169
+ - id: player_instance_1
170
+ instance: res://prefabs/player.pix3scene
171
+ name: Player1
172
+ properties:
173
+ position: { x: 0, y: 0, z: 0 }
174
+ ```
175
+
176
+ The `properties` block allows overriding base prefab values. Overrides are tracked separately from the base values.