create-pixifact 0.15.0 → 0.17.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.
@@ -0,0 +1,312 @@
1
+ # Pixifact Scene Script Patterns
2
+
3
+ 本文件随 Pixifact skill 安装,用于下游项目离线查阅 `.scene` 与同名 `.ts` 脚本的配对写法。新建 Scene、修改脚本契约、添加 `@part()` / `@prop()` / `@event()` / `@slot()`,或修复 scene validate 的脚本契约 diagnostics 时先读本文件。
4
+
5
+ ## 配对不变量
6
+
7
+ 一个 compiler Scene 是同目录、同 basename 的文件对:
8
+
9
+ ```txt
10
+ src/scenes/Hud.scene
11
+ src/scenes/Hud.ts
12
+ ```
13
+
14
+ 这四个名字必须对齐:
15
+
16
+ ```xml
17
+ <Scene name="Hud">
18
+ ```
19
+
20
+ ```ts
21
+ @scene()
22
+ export class Hud extends Group {}
23
+ ```
24
+
25
+ 规则:
26
+
27
+ - `.scene` 文件名 basename、`<Scene name>`、同名 `.ts` 文件、`@scene()` class 名保持一致。
28
+ - Scene 名是本地文件对的名字,不是全局唯一 ID;引用其他 Scene 时用 `.scene` 路径。
29
+ - `.scene` 不写 `script="..."` 或 `class="..."`。
30
+ - `@scene()` 不接收参数。
31
+ - 生成文件只读,不编辑 `.pixifact/generated/**`、`src/generated/**`、`*.scene.generated.ts` 或 `scenes.generated.ts`。
32
+
33
+ ## 分工
34
+
35
+ `.scene` 负责:
36
+
37
+ - 视觉结构、层级、布局、文本、图片和静态显示属性。
38
+ - 官方基础对象(包括用于明确盒子尺寸的 `Group`)和项目子 Scene instance。
39
+ - slot 填充和 event wiring,例如 `@click="startGame"`。
40
+
41
+ `.ts` 负责:
42
+
43
+ - 行为、运行时状态更新、输入处理和动画。
44
+ - 通过 `@part()` 访问 `.scene` 中稳定 `id` 的节点。
45
+ - 通过 `@prop()`、`@event()`、`@slot()` 暴露父 Scene 和 Inspector 可见的公开契约。
46
+
47
+ 不要把静态视觉属性从 `.scene` 搬到 `.ts`。只有需要运行时行为或公开契约时才改脚本。
48
+
49
+ ## 何时改脚本
50
+
51
+ | 任务 | 默认改动 |
52
+ | --- | --- |
53
+ | 移动节点、改颜色、换图片、调布局 | 只改 `.scene`。 |
54
+ | 脚本需要读写某个节点 | 给节点稳定 `id`,脚本加 `@part()`。 |
55
+ | 父 Scene 需要传值给子 Scene | 子脚本加 `@prop()`,父 `.scene` 写属性。 |
56
+ | 子 Scene 需要通知父 Scene | 子脚本加 `@event()` 和 `createEvent()`,父 `.scene` 写 `@eventName="actionName"`。 |
57
+ | 父 Scene 需要向子 Scene 塞图标或内容 | 子 `.scene` 写 `<slot>`,子脚本加 `@slot()`,父 `.scene` 子节点写 `slot="name"`。 |
58
+ | 需要多个相关字段一起传入 | 用 exported structured prop class,父 `.scene` 用 dot-path attributes。 |
59
+
60
+ ## Import 速查
61
+
62
+ 常用导入:
63
+
64
+ ```ts
65
+ import type { Container, Text } from 'pixi.js';
66
+ import { Group, type Rect } from 'pixifact/runtime';
67
+ import { createEvent, defineVariants, event, part, prop, scene, slot } from 'pixifact/scene';
68
+ ```
69
+
70
+ 原则:
71
+
72
+ - `Group`、`Rect`、`Image`、`NineImage`、`TileImage` 等 runtime 类型来自 `pixifact/runtime`。
73
+ - `scene`、`part`、`prop`、`defineVariants`、`event`、`slot`、`createEvent` 来自 `pixifact/scene`。
74
+ - Pixi 节点类型如 `Container`、`Text`、`Graphics` 来自 `pixi.js`。
75
+ - 只用于类型标注时使用 `import type` 或 named type import。
76
+
77
+ ## 最小 Scene
78
+
79
+ `StatusBadge.scene`:
80
+
81
+ ```xml
82
+ <Scene name="StatusBadge" width="220" height="64">
83
+ <Rect id="background" left="0" right="0" top="0" bottom="0" radius="16" fillColor="#1f2937" />
84
+ <Text id="labelText" text="READY" x="24" y="18" fontSize="24" fontWeight="700" fill="#ffffff" />
85
+ </Scene>
86
+ ```
87
+
88
+ `StatusBadge.ts`:
89
+
90
+ ```ts
91
+ import { Group } from 'pixifact/runtime';
92
+ import { scene } from 'pixifact/scene';
93
+
94
+ @scene()
95
+ export class StatusBadge extends Group {}
96
+ ```
97
+
98
+ 如果只是静态 UI,不需要为了节点存在而添加 `@part()`。
99
+
100
+ ## `@prop()` + Binding 更新文本
101
+
102
+ `Hud.scene`:
103
+
104
+ ```xml
105
+ <Scene name="Hud" width="750" height="160">
106
+ <Text id="playerNameText" text="{playerName}" left="24" top="24" fontSize="28" fontWeight="700" fill="#ffffff" />
107
+ <Text id="coinText" text="{coins}" right="24" top="24" fontSize="28" fontWeight="700" fill="#ffd166" />
108
+ </Scene>
109
+ ```
110
+
111
+ `Hud.ts`:
112
+
113
+ ```ts
114
+ import { Group } from 'pixifact/runtime';
115
+ import { prop, scene } from 'pixifact/scene';
116
+
117
+ @scene()
118
+ export class Hud extends Group {
119
+ @prop({ default: 'Player' })
120
+ declare playerName: string;
121
+
122
+ @prop({ default: 0 })
123
+ declare coins: number;
124
+ }
125
+ ```
126
+
127
+ 注意:
128
+
129
+ - `@prop` 只能装饰无 initializer 的 `declare` property,类型从 TypeScript 声明推断。
130
+ - `{playerName}` 和 `{coins}` 是完整值 Binding;数字绑定到 `Text.text` 时会做标准字符串转换。
131
+ - 修改 `hud.coins` 只更新依赖 `{coins}` 的节点,不重建 Scene。
132
+ - 不支持表达式、字符串插值、watch、computed 或双向绑定。
133
+
134
+ ## Button:slot + event
135
+
136
+ `PrimaryButton.scene`:
137
+
138
+ ```xml
139
+ <Scene name="PrimaryButton" width="360" height="80">
140
+ <Rect id="background" left="0" right="0" top="0" bottom="0" radius="18" fillColor="{tone.background}" />
141
+ <HBoxContainer id="content" horizontal="0" vertical="0" gap="12" alignY="center">
142
+ <slot name="icon" />
143
+ <Text id="labelText" text="{label}" fontSize="26" fontWeight="700" fill="{tone.text}" />
144
+ </HBoxContainer>
145
+ </Scene>
146
+ ```
147
+
148
+ `PrimaryButton.ts`:
149
+
150
+ ```ts
151
+ import type { Container } from 'pixi.js';
152
+ import { Group, type Rect } from 'pixifact/runtime';
153
+ import { createEvent, defineVariants, event, part, prop, scene, slot } from 'pixifact/scene';
154
+
155
+ const buttonTones = defineVariants({
156
+ primary: { background: '#2563eb', text: '#ffffff' },
157
+ danger: { background: '#b4233c', text: '#fff0f4' },
158
+ });
159
+
160
+ @scene()
161
+ export class PrimaryButton extends Group {
162
+ @part()
163
+ protected declare background: Rect;
164
+
165
+ @slot({ name: 'icon' })
166
+ readonly icon!: Container;
167
+
168
+ @prop({ default: 'Button' })
169
+ declare label: string;
170
+
171
+ @prop({ default: 'primary', variants: buttonTones })
172
+ declare tone: keyof typeof buttonTones;
173
+
174
+ @event()
175
+ readonly click = createEvent();
176
+
177
+ onMounted() {
178
+ this.background.eventMode = 'static';
179
+ this.background.cursor = 'pointer';
180
+ this.background.on('pointertap', () => {
181
+ this.click.emit();
182
+ });
183
+ }
184
+ }
185
+ ```
186
+
187
+ 父 Scene 填充 slot 并绑定事件:
188
+
189
+ ```xml
190
+ <PrimaryButton id="startButton" scene="./PrimaryButton.scene" label="开始游戏" tone="primary" @click="startGame">
191
+ <Image slot="icon" id="startIcon" texture="assets/icons/play.png" width="32" height="32" fit="contain" />
192
+ </PrimaryButton>
193
+ ```
194
+
195
+ 父 Scene 脚本中的 action 方法:
196
+
197
+ ```ts
198
+ import { Group } from 'pixifact/runtime';
199
+ import { scene } from 'pixifact/scene';
200
+
201
+ @scene()
202
+ export class MainMenu extends Group {
203
+ startGame() {
204
+ // start game flow
205
+ }
206
+ }
207
+ ```
208
+
209
+ `@click="startGame"` 的值是 action name。运行时优先连接 external actions;没有对应 action 时,连接当前 root 脚本实例上的同名方法。父脚本的 `startGame()` 不需要 `@event()`;`@event()` 用在暴露事件的子 Scene 上。
210
+
211
+ ## Structured prop
212
+
213
+ 当多个字段天然属于一个公开属性时,用 exported struct class,不要传 JSON。
214
+
215
+ 父 `.scene`:
216
+
217
+ ```xml
218
+ <RewardCard
219
+ id="reward"
220
+ scene="./RewardCard.scene"
221
+ rectTransform.x="40"
222
+ rectTransform.y="220"
223
+ rectTransform.width="670"
224
+ rectTransform.height="160"
225
+ />
226
+ ```
227
+
228
+ `RewardCard.ts`:
229
+
230
+ ```ts
231
+ import { Group } from 'pixifact/runtime';
232
+ import { prop, scene } from 'pixifact/scene';
233
+
234
+ export class RectTransform {
235
+ x = 0;
236
+ y = 0;
237
+ width = 188;
238
+ height = 48;
239
+ }
240
+
241
+ @scene()
242
+ export class RewardCard extends Group {
243
+ @prop({})
244
+ declare rectTransform: RectTransform;
245
+ }
246
+ ```
247
+
248
+ 规则:
249
+
250
+ - struct class 必须从同文件导出。
251
+ - struct class 必须可无参构造。
252
+ - public fields 必须有 primitive initializer。
253
+ - structured prop 默认值来自 struct class field initializer,不在 `@prop` 里写 `default`。
254
+ - 父 `.scene` 使用 `rectTransform.x="40"` 这种 dot-path attribute。
255
+
256
+ ## 负例
257
+
258
+ 不要写这些:
259
+
260
+ ```ts
261
+ @scene('./PrimaryButton.scene')
262
+ export class PrimaryButton extends Group {}
263
+ ```
264
+
265
+ ```xml
266
+ <Scene name="PrimaryButton" script="./PrimaryButton.ts">
267
+ ```
268
+
269
+ ```ts
270
+ @prop({ type: String, default: 'Button' })
271
+ accessor label = 'Button';
272
+ ```
273
+
274
+ ```ts
275
+ @event()
276
+ click = new EventEmitter<void>();
277
+ ```
278
+
279
+ ```xml
280
+ <RewardCard rectTransform="{&quot;x&quot;:40,&quot;y&quot;:220}" />
281
+ ```
282
+
283
+ ```xml
284
+ <Group id="content" />
285
+ <Control id="panel" />
286
+ ```
287
+
288
+ ```xml
289
+ <PrimaryButton id="button" scene="./PrimaryButton.scene">
290
+ <Text id="label" text="Label" />
291
+ </PrimaryButton>
292
+ ```
293
+
294
+ 上面最后一个例子只有在 `PrimaryButton.ts` 暴露 default slot,且 `PrimaryButton.scene` 中有 `<slot />` 时才有效。子 Scene instance 只通过已声明 slot 接收 children。
295
+
296
+ ## 诊断修复速查
297
+
298
+ | Diagnostic | 修复 |
299
+ | --- | --- |
300
+ | `<Scene name>` must match file basename | 改 `<Scene name>`,或重命名 `.scene` / `.ts` 文件对。 |
301
+ | requires paired script | 创建同目录同 basename 的 `.ts` 文件。 |
302
+ | must match `@scene` class | 让 `<Scene name>` 和 `@scene()` class 名一致。 |
303
+ | No `@scene` decorator found | 在配对脚本里添加一个 `@scene()` class。 |
304
+ | `@part` references missing node id | 给 `.scene` 添加对应 `id`,或修改 `@part({ id })`。 |
305
+ | unknown prop / event / slot | 在子 Scene 脚本暴露对应 `@prop()` / `@event()` / `@slot()`,或修正父 `.scene` 写法。 |
306
+
307
+ 修复后重新运行失败的命令;直接编辑 `.scene` 后至少运行:
308
+
309
+ ```bash
310
+ pixifact scene validate --scene src/scenes/MainMenu.scene
311
+ pixifact compile-scenes
312
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pixifact",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "创建 Pixifact 游戏项目。",
5
5
  "packageManager": "bun@1.3.13",
6
6
  "type": "module",
@@ -29,7 +29,7 @@
29
29
  "registry": "https://registry.npmjs.org/"
30
30
  },
31
31
  "scripts": {
32
- "build": "bunx --no-install tsc -p tsconfig.build.json"
32
+ "build": "bunx --no-install tsc -p tsconfig.build.json && bun ../../scripts/install-skills.mjs --target dist/skills --replace"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "^25.9.1",
@@ -6,14 +6,16 @@
6
6
  "scripts": {
7
7
  "dev": "pixifact dev --mode development --project-root .",
8
8
  "build": "pixifact build --mode production --project-root .",
9
+ "compile": "pixifact compile-scenes --project-root .",
10
+ "editor": "pixifact editor --project-root .",
9
11
  "validate": "pixifact validate --mode production --project-root ."
10
12
  },
11
13
  "dependencies": {
12
14
  "pixi.js": "8.18.1",
13
- "pixifact": "^0.15.0"
15
+ "pixifact": "^0.17.0"
14
16
  },
15
17
  "devDependencies": {
16
- "pixifact-cli": "^0.15.0",
18
+ "pixifact-cli": "^0.17.0",
17
19
  "typescript": "^5.3.3",
18
20
  "vite": "^8.0.10"
19
21
  }