aispritejs 0.5.6 → 0.5.8
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.
- package/README.md +59 -231
- package/README_ZHTW.md +59 -228
- package/dist/atlas/index.cjs +24 -470
- package/dist/atlas/index.cjs.map +1 -1
- package/dist/atlas/index.js +13 -459
- package/dist/atlas/index.js.map +1 -1
- package/dist/chunk-6E4ILDBY.js +506 -0
- package/dist/chunk-6E4ILDBY.js.map +1 -0
- package/dist/chunk-QF5N7LOI.cjs +513 -0
- package/dist/chunk-QF5N7LOI.cjs.map +1 -0
- package/dist/index.cjs +21 -462
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -461
- package/dist/index.js.map +1 -1
- package/dist/pixi/index.cjs +3 -461
- package/dist/pixi/index.cjs.map +1 -1
- package/dist/pixi/index.js +2 -460
- package/dist/pixi/index.js.map +1 -1
- package/llms-full.txt +116 -435
- package/llms.txt +8 -38
- package/package.json +5 -3
- package/schemas/aispritejs-graph.schema.json +5 -4
package/README_ZHTW.md
CHANGED
|
@@ -1,266 +1,97 @@
|
|
|
1
1
|
# aispritejs
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](https://github.com/islumina/aispritejs/actions/workflows/ci.yml)
|
|
5
|
-
[](LICENSE)
|
|
6
|
-
[](https://www.anthropic.com/claude-code)
|
|
7
|
-
[](README.md)
|
|
3
|
+
Input-driven、renderer-agnostic 的 2D sprite animation runtime。JSON graph 會把 Number/Boolean/Trigger inputs 對應到 visual states 與 frames;adapter 再把選到的 frame 綁到 renderer。
|
|
8
4
|
|
|
9
|
-
>
|
|
5
|
+
> **狀態:0.5.8 - 穩定 family-aligned API。** Core、PixiJS adapter、atlas parser、JSON Schema subpath 都已發布。
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
## 安裝
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- **輸入驅動,而非名稱驅動。** 你設定參數(`speed=4`、`isGrounded=false`、`fireTrigger("jump")`),而不是動畫名稱。視覺轉移存在於資料中,與遊戲程式解耦。
|
|
18
|
-
- **核心與渲染器無關。** 狀態機從 delta-time 加上輸入算出當前影格;它從不 import PixiJS 也不碰 DOM。轉接器負責把結果對映成 texture。
|
|
19
|
-
- **視覺 ≠ 邏輯。** 這嚴格來說是一個*視覺*動畫器,**不是**遊戲邏輯 FSM,也**不**相依 `aifsmjs`。用任何邏輯層(純程式、FSM、ECS)來驅動它 —— 它們以慣例組合,從不以相依耦合。
|
|
20
|
-
- **輕巧又快。** O(1) 的輸入查找、對離開當前狀態的轉移做 O(N) 檢查、無每幀配置。
|
|
21
|
-
|
|
22
|
-
## 何時你「不需要」aispritejs
|
|
23
|
-
|
|
24
|
-
`aispritejs` 的價值在於影格選擇是**由執行期輸入驅動**、且橫跨**數個視覺狀態**時才會顯現。在這個門檻之下,直接用 PixiJS 就好 —— 用狀態機並無任何好處:
|
|
25
|
-
|
|
26
|
-
- **單一 sprite/一張靜態圖片** → 純 PixiJS [`Sprite`](https://pixijs.download/release/docs/scene.Sprite.html)。沒有動畫、不需要圖集。
|
|
27
|
-
- **單一循環片段、無任何分支**(轉動的金幣、閃爍的火把)→ PixiJS [`AnimatedSprite`](https://pixijs.download/release/docs/scene.AnimatedSprite.html)(`AnimatedSprite.fromFrames(...)`、`.play()`)。永遠以同一種方式播放的單一片段不需要輸入。
|
|
28
|
-
- **沒有 texture atlas**(你並未把影格打包成 spritesheet)→ 直接載入圖片;`aispritejs` 從 PixiJS-v8 atlas(`animations` / `frames`)讀取它的影格。
|
|
29
|
-
- **沒有多狀態切換** —— 若你的程式早已確切知道要播哪個片段、只需呼叫 `.play()` / `.gotoAndStop()`,你並不需要轉移圖。
|
|
30
|
-
|
|
31
|
-
當你有**由輸入驅動、以 texture atlas 為後盾的多狀態視覺切換**時,才該動用 `aispritejs` —— 例如 `idle ⇄ walk → jump`,或由 trigger 觸發的一次性受擊 FX —— 此時畫面上顯示哪一格是 `speed` / `isGrounded` / `attack` 的函數,而非寫死的 `play()` 呼叫。
|
|
32
|
-
|
|
33
|
-
## 心智模型
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
inputs ─▶ [轉移圖] ─▶ 當前狀態 ─▶ (Δt) ─▶ 當前影格 ─▶ 轉接器 ─▶ texture
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add aispritejs
|
|
11
|
+
pnpm add pixi.js # only when using aispritejs/pixi
|
|
37
12
|
```
|
|
38
13
|
|
|
39
|
-
- **Inputs(輸入)** —— `Number`(連續,如 `speed`)、`Boolean`(開關,如 `isGrounded`)、`Trigger`(一次性;被某個轉移消耗後自動重置,如 `jump` / `attack`)。
|
|
40
|
-
- **States(狀態)** —— 一個動畫鍵(指向 atlas 的 `animations`)加上 loop / on-end 行為與選用的速度倍率。
|
|
41
|
-
- **Transitions(轉移)** —— 當輸入條件成立時(`Equals` / `NotEquals` / `GreaterThan` / `LessThan` / `Trigger`),從某狀態(或 **Any State**)轉到另一狀態。優先度最高且成立者勝出。
|
|
42
|
-
- **`update(dt)`** —— 推進播放計時器;評估轉移(切換狀態、觸發 `onStateChange`、消耗 trigger);由動畫的逐影格時長加 loop 算出當前影格;非循環片段播完時觸發 `onComplete`。
|
|
43
|
-
|
|
44
|
-
## 快速開始 —— 核心(零相依)
|
|
45
|
-
|
|
46
14
|
```ts
|
|
47
15
|
import { createSpriteAnimator } from "aispritejs";
|
|
48
|
-
|
|
49
|
-
const anim = createSpriteAnimator(graph); // graph = { inputs, states, transitions, animations }
|
|
50
|
-
|
|
51
|
-
anim.setInput("speed", 4);
|
|
52
|
-
anim.setInput("isGrounded", true);
|
|
53
|
-
anim.fireTrigger("jump");
|
|
54
|
-
|
|
55
|
-
anim.onStateChange((to, from) => {/* ... */});
|
|
56
|
-
anim.onComplete((state) => {/* ... */});
|
|
57
|
-
|
|
58
|
-
// 在你的 render loop 裡:
|
|
59
|
-
anim.update(deltaMs);
|
|
60
|
-
const frameKey = anim.activeFrameKey; // 交給你的渲染器
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## 快速開始 —— PixiJS v8 轉接器
|
|
64
|
-
|
|
65
|
-
`aispritejs/pixi` 子路徑把核心綁到一個 `PIXI.Sprite`。`pixi.js` 是**選用的** `peerDependency`,且僅以 **type-only** 方式 import —— 編譯後的轉接器不含任何 `pixi.js` runtime require,核心也永不碰它。
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
import { createPixiSpriteAnimator } from "aispritejs/pixi"; // pixi.js 是選用 peer
|
|
69
|
-
|
|
70
|
-
// `textures` 是 PIXI.Spritesheet(或 frame-key → Texture 的 map),需涵蓋 graph
|
|
71
|
-
// 參照的每一格 —— 缺鍵會丟出 MissingTextureError。
|
|
72
|
-
const view = createPixiSpriteAnimator(sprite, graph, spritesheet);
|
|
73
|
-
|
|
74
|
-
// 每幀:
|
|
75
|
-
view.update(deltaMs); // 把綁定 sprite 的 texture 換成當前影格,
|
|
76
|
-
// 並套用該格的 atlas anchor(texture.defaultAnchor)
|
|
77
|
-
|
|
78
|
-
view.setInput("speed", 4);
|
|
79
|
-
view.fireTrigger("jump");
|
|
80
16
|
```
|
|
81
17
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
### 完整範例 —— 一段 6 格爆炸(play-once FX)
|
|
85
|
-
|
|
86
|
-
最常見的入門場景:一次性的受擊/撞擊 FX(水花、槍口閃光),來自一張 **6 格爆炸 sprite sheet**,透過 `/pixi` 轉接器驅動。一個 `Trigger` 觸發一段**非循環**片段,播放一次後經由 `onEnd` 自動回到靜止影格。完整可執行版本在 [`examples/02-explosion-pixi/index.ts`](examples/02-explosion-pixi/index.ts)(`pnpm example:explosion`)。
|
|
18
|
+
## 快速開始 - Core
|
|
87
19
|
|
|
88
20
|
```ts
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
// 命名 → 它的影格鍵;`frames` 帶有逐影格時長。
|
|
94
|
-
const graph = {
|
|
95
|
-
animations: {
|
|
96
|
-
explosion: ["explosion_0", "explosion_1", "explosion_2", "explosion_3", "explosion_4", "explosion_5"],
|
|
97
|
-
idle: ["explosion_0"], // 一段 1 格的靜止片段,在每次爆發之間維持
|
|
98
|
-
},
|
|
99
|
-
frames: {
|
|
100
|
-
explosion_0: { duration: 40 }, explosion_1: { duration: 40 }, explosion_2: { duration: 40 },
|
|
101
|
-
explosion_3: { duration: 40 }, explosion_4: { duration: 40 }, explosion_5: { duration: 40 },
|
|
21
|
+
const anim = createSpriteAnimator({
|
|
22
|
+
inputs: {
|
|
23
|
+
speed: { type: "number", default: 0 },
|
|
24
|
+
jump: { type: "trigger" },
|
|
102
25
|
},
|
|
103
|
-
|
|
26
|
+
initial: "idle",
|
|
104
27
|
states: {
|
|
105
|
-
idle: { animation: "idle"
|
|
106
|
-
|
|
28
|
+
idle: { animation: "idle" },
|
|
29
|
+
run: { animation: "run", speed: 1 },
|
|
30
|
+
jump: { animation: "jump", loop: false, onEnd: "idle" },
|
|
107
31
|
},
|
|
108
32
|
transitions: [
|
|
109
|
-
{ from: "
|
|
33
|
+
{ from: "idle", to: "run", when: [{ input: "speed", op: "GreaterThan", value: 0 }] },
|
|
34
|
+
{ from: "*", to: "jump", when: [{ input: "jump", op: "Trigger" }] },
|
|
110
35
|
],
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const sheet = await Assets.load("explosion.json"); // 一個 PIXI.Spritesheet
|
|
116
|
-
const sprite = new Sprite();
|
|
117
|
-
const fx = createPixiSpriteAnimator(sprite, graph, sheet);
|
|
118
|
-
|
|
119
|
-
// 在撞擊時觸發一次性 trigger:
|
|
120
|
-
fx.fireTrigger("detonate");
|
|
121
|
-
|
|
122
|
-
// 從你的 PixiJS render loop(例如 `app.ticker`)驅動這段爆發,依經過的
|
|
123
|
-
// 毫秒數推進:`app.ticker.add((ticker) => fx.update(ticker.deltaMS))`。
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
`update(dt)` 把 `explosion_0…explosion_5` 播放一次(尊重每一格的 `duration`);在播完片段的那一個 tick,它會觸發 `onComplete`,並因為 `boom` 宣告了 `onEnd`,在**同一個** tick 內自動轉移回 `idle` —— 所以不會停在最後一格,呼叫之後 `activeState` 立即讀到 `idle`。再次 `fireTrigger("detonate")` 即可重播。同一份 graph 在沒有渲染器時也能跑 —— 見 [`examples/02-explosion-pixi/index.ts`](examples/02-explosion-pixi/index.ts),它以純 `Texture` / `Sprite` 實例、在無頭環境下操練真正的轉接器。
|
|
127
|
-
|
|
128
|
-
## 資料格式(atlas)
|
|
129
|
-
|
|
130
|
-
`aispritejs` 讀取 **PixiJS v8 原生**的 spritesheet atlas(`meta` / `frames` / `animations`),並額外加上一個 `aispritejs` 的**輸入驅動**控制區塊:
|
|
131
|
-
|
|
132
|
-
```jsonc
|
|
133
|
-
{
|
|
134
|
-
"meta": { "image": "sheet.png", "size": { "w": 1024, "h": 1024 }, "scale": "1" },
|
|
135
|
-
"frames": { /* PixiJS 原生:frame{x,y,w,h}、anchor、duration、trimmed... */ },
|
|
136
|
-
"animations": { "idle": ["idle_0", "idle_1"], "walk": ["walk_0", "..."], "jump": ["jump_0", "..."] },
|
|
137
|
-
|
|
138
|
-
"inputs": {
|
|
139
|
-
"speed": { "type": "number", "default": 0 },
|
|
140
|
-
"isGrounded": { "type": "boolean", "default": true },
|
|
141
|
-
"jump": { "type": "trigger" }
|
|
142
|
-
},
|
|
143
|
-
"states": {
|
|
144
|
-
"idle": { "animation": "idle", "loop": true },
|
|
145
|
-
"walk": { "animation": "walk", "loop": true },
|
|
146
|
-
"jump": { "animation": "jump", "loop": false }
|
|
36
|
+
animations: {
|
|
37
|
+
idle: ["idle_0"],
|
|
38
|
+
run: ["run_0", "run_1"],
|
|
39
|
+
jump: ["jump_0", "jump_1"],
|
|
147
40
|
},
|
|
148
|
-
|
|
149
|
-
{ "from": "*", "to": "jump", "when": [{ "input": "jump", "op": "Trigger" }], "priority": 10 },
|
|
150
|
-
{ "from": "idle", "to": "walk", "when": [{ "input": "speed", "op": "GreaterThan", "value": 0 }] },
|
|
151
|
-
{ "from": "walk", "to": "idle", "when": [{ "input": "speed", "op": "Equals", "value": 0 }] }
|
|
152
|
-
]
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
這個**輸入驅動**模型刻意與事件驅動 FSM 區隔。`aispritejs` 只吃通用的 `frames` / `animations`;`inputs` / `states` / `transitions` 屬於它自己。若某個 atlas 帶有來自其他工具的事件驅動 `states` 區塊,`aispritejs` 會忽略它。
|
|
157
|
-
|
|
158
|
-
## 載入 atlas —— `aispritejs/atlas`
|
|
41
|
+
});
|
|
159
42
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// 增強型 atlas(上述形狀,inputs/states/transitions 內嵌):
|
|
166
|
-
const anim = loadAtlas(atlasJson);
|
|
167
|
-
|
|
168
|
-
// 真實 atlas 的 `states` 是 foreign(事件驅動)或不存在 —— 另外傳入輸入驅動
|
|
169
|
-
// 控制區塊;foreign 區塊會被忽略:
|
|
170
|
-
const graph = parseAtlas(atlasJson, { inputs, states, transitions, initial });
|
|
43
|
+
anim.setInput("speed", 1);
|
|
44
|
+
anim.fireTrigger("jump");
|
|
45
|
+
anim.update(16.7);
|
|
46
|
+
console.log(anim.activeState, anim.activeFrameKey);
|
|
171
47
|
```
|
|
172
48
|
|
|
173
|
-
|
|
174
|
-
- foreign 事件驅動 `states`(`{ initial, definitions }` 的 FSM 形狀)會被**偵測並忽略** —— 改傳 `aispritejs` 控制區塊。結構問題丟出 `InvalidAtlasError`;語意問題由核心丟出 `InvalidGraphError`。
|
|
175
|
-
- 標準結構以 JSON Schema 發佈於 [`schemas/aispritejs-graph.schema.json`](schemas/aispritejs-graph.schema.json)(亦匯出為 `aispritejs/schema`),供編輯器與 CI 驗證;parser 以程式碼鏡像它,因此無 runtime schema-validator 相依。
|
|
176
|
-
|
|
177
|
-
## 核心 API
|
|
178
|
-
|
|
179
|
-
公開介面是單一工廠函式,加上型別與具名錯誤。**不匯出任何 class 建構子** —— `createSpriteAnimator` 回傳一個 `SpriteAnimator`。
|
|
49
|
+
## PixiJS Adapter
|
|
180
50
|
|
|
181
51
|
```ts
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
anim.setInput(name, value); // Number | Boolean;丟出 Unknown/InputTypeError
|
|
185
|
-
anim.fireTrigger(name); // 將某個 Trigger 標記為 pending
|
|
186
|
-
anim.update(deltaMs); // 推進;評估轉移;推進影格
|
|
187
|
-
anim.reset(); // 回到初始狀態與預設輸入(保留 buffer)
|
|
188
|
-
anim.dispose(); // 冪等;之後呼叫 mutator 會丟錯
|
|
189
|
-
|
|
190
|
-
const off = anim.onStateChange((to, from) => {}, { signal?, once? }); // → 取消訂閱
|
|
191
|
-
const off2 = anim.onComplete((state) => {}, { signal?, once? });
|
|
52
|
+
import { createPixiSpriteAnimator } from "aispritejs/pixi";
|
|
192
53
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
anim.disposed; // boolean
|
|
54
|
+
const view = createPixiSpriteAnimator(sprite, graph, spritesheet);
|
|
55
|
+
view.update(deltaMs);
|
|
56
|
+
view.dispose(); // dispose core animator;不 destroy Pixi sprite
|
|
197
57
|
```
|
|
198
58
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
## 語意(精確規則)
|
|
202
|
-
|
|
203
|
-
這些規則是決定性的,並在 1.0 推出後對 1.x 凍結:
|
|
204
|
-
|
|
205
|
-
- **`update(dt)` 順序** —— 以 `dt × speed` 推進計時器(非有限或非正值的 `dt` 夾到 `0`);評估轉移;重算當前影格;為播完的非循環片段觸發 `onComplete`,接著執行任何 `onEnd` 自動轉移。因此同一幀內,明確的輸入轉移優先於片段結束行為。
|
|
206
|
-
- **轉移解析** —— 在離開當前狀態的轉移(加上 **Any-State** `from: "*"`)中,候選依 `priority`(遞減)再依宣告順序(遞增)排序;取**第一個有效**者。所有 `when` 條件須全部成立(邏輯 AND)。
|
|
207
|
-
- **自我轉移規則** —— `to` 等於當前狀態的轉移,*只有在它消耗一個 Trigger 時才有效*。純 Number/Boolean 的自我迴圈會被跳過,因此不會每幀把片段重置回第 0 格。帶 trigger 的自我轉移會**重啟**片段(例如連續攻擊),但**不**觸發 `onStateChange`(狀態名稱沒變)。
|
|
208
|
-
- **Triggers** —— `fireTrigger(name)` 把某 trigger 標記為 pending;它跨幀保持 pending,直到某個檢查它的轉移被採用而**消耗**它。一次 fire → 至多一次轉移。
|
|
209
|
-
- **影格時序** —— 當前影格是累積時長首次超過已經過時間的那一格。循環片段在總時長處回繞;非循環片段停在最後一格並只觸發一次 `onComplete`。逐影格 `duration` 取自 atlas `frames`;沒有的影格採用 `defaultFrameDuration`(預設 `100` ms)。`speed` 是時間倍率(`2` = 兩倍速)。
|
|
210
|
-
- **決定性** —— 相同的輸入加 `dt` 序列必產生相同的影格序列。無轉移的路徑不做任何配置。
|
|
211
|
-
|
|
212
|
-
## 錯誤
|
|
213
|
-
|
|
214
|
-
具名錯誤,從不裸 throw:
|
|
215
|
-
|
|
216
|
-
- `InvalidGraphError` —— `createSpriteAnimator` 在 graph 驗證失敗時丟出(缺少動畫、未知的轉移目標、運算子/型別不符、非正的時長/速度、`onEnd` 配 `loop:true`…)。Fail-fast:不合法的 graph 絕不產出半成品動畫器。
|
|
217
|
-
- `UnknownInputError` —— 對 `inputs` 未宣告的輸入呼叫 `setInput` / `fireTrigger`(帶有 `.input`)。
|
|
218
|
-
- `InputTypeError` —— 值型別錯誤、對 Trigger 呼叫 `setInput`、或對非 Trigger 呼叫 `fireTrigger`(帶有 `.input`)。
|
|
219
|
-
- `SpriteAnimatorDisposedError` —— `dispose()` 之後呼叫任何 mutator(`setInput` / `fireTrigger` / `update` / `reset`)。
|
|
220
|
-
|
|
221
|
-
## 解耦(P0)
|
|
59
|
+
`pixi.js` 是 optional peer dependency,adapter 只用 type-only import。root package 不 import Pixi、DOM 或 canvas API。
|
|
222
60
|
|
|
223
|
-
|
|
224
|
-
- **核心與渲染器無關** —— 根進入點永不 import `pixi.js`。只有 `aispritejs/pixi` 會,而 `pixi.js` 是**選用的** `peerDependency`。
|
|
225
|
-
- **僅是視覺動畫器** —— 用設定輸入的方式搭配遊戲邏輯層;它不假設你的邏輯如何組織。
|
|
61
|
+
## Atlas and Schema
|
|
226
62
|
|
|
227
|
-
|
|
63
|
+
- `parseAtlas(atlas, control?)` 將 PixiJS-v8 atlas 加 control block 轉成 `SpriteGraph`。
|
|
64
|
+
- `loadAtlas(atlas, control?)` parse 後直接建立 `SpriteAnimator`。
|
|
65
|
+
- `aispritejs/schema` 匯出 `schemas/aispritejs-graph.schema.json`,可用於 editor/CI validation。
|
|
66
|
+
- Parser 做 structural validation(`InvalidAtlasError`);compiler 做 semantic validation(`InvalidGraphError`)。
|
|
228
67
|
|
|
229
|
-
|
|
230
|
-
|---|---|---|---|---|
|
|
231
|
-
| 控制模型 | 輸入驅動(Number/Boolean/Trigger) | 輸入驅動 | 事件驅動(邏輯) | 手動 |
|
|
232
|
-
| 範圍 | 視覺動畫 | 視覺動畫 | 遊戲邏輯 | 僅播放 |
|
|
233
|
-
| Runtime | 輕巧 TS,無 wasm | wasm runtime | 輕巧 TS | — |
|
|
234
|
-
| 渲染器 | 無關 + 轉接器 | 自帶 | n/a | PixiJS |
|
|
235
|
-
|
|
236
|
-
`aispritejs` 與 `aifsmjs` 互補 —— 邏輯 FSM 設定輸入、視覺動畫器挑影格 —— 且永不耦合。
|
|
237
|
-
|
|
238
|
-
## 給 AI agent 的閱讀指南
|
|
239
|
-
|
|
240
|
-
- **一次抓完整 context** —— [`llms-full.txt`](llms-full.txt) 串接了本 README、changelog、contributing 指南與範例索引。
|
|
241
|
-
- **原始碼版面** —— 核心位於 [`src/sprite/`](src/sprite/):`types.ts`(所有公開型別集中一檔)、`machine.ts`(`createSpriteAnimator` 引擎)、`compile.ts`(graph 驗證與正規化)、`inputs.ts`(輸入儲存)、`emitter.ts`(自有具名 signal)、`errors.ts`。根 [`src/index.ts`](src/index.ts) 再匯出公開介面,且**不** import 任何渲染器。
|
|
242
|
-
- **穩定度分級** —— 見 [STABILITY.md](STABILITY.md)。
|
|
243
|
-
|
|
244
|
-
## 測試
|
|
245
|
-
|
|
246
|
-
`vitest` 行為測試涵蓋輸入、轉移、trigger 消耗、影格時序、`onComplete` / `onEnd`、訂閱(`signal` / `once`)、`dispose` / `reset` 與 graph 驗證。`fast-check` 屬性測試驗證**轉移決定性**(相同輸入加 `dt` 序列 ⇒ 相同影格軌跡)與 **trigger 消耗**(一次 fire ⇒ 一次進入)。覆蓋率以家族下限執行(≥95% statements / ≥90% branches / 100% functions 與 lines)。
|
|
247
|
-
|
|
248
|
-
```bash
|
|
249
|
-
pnpm test # 跑一次
|
|
250
|
-
pnpm coverage # 帶門檻
|
|
251
|
-
pnpm example:platformer
|
|
252
|
-
```
|
|
68
|
+
## 核心 API
|
|
253
69
|
|
|
254
|
-
|
|
70
|
+
- `createSpriteAnimator(graph)` 回傳 `SpriteAnimator`。
|
|
71
|
+
- `setInput(name, value)` 接受 Number/Boolean inputs。
|
|
72
|
+
- `fireTrigger(name)` 觸發 Trigger input,transition 後會消耗。
|
|
73
|
+
- `update(deltaMs)` 推進時間、transition、frame index 與 `onEnd`。
|
|
74
|
+
- `reset()` 回到 initial state。
|
|
75
|
+
- `dispose()` 可重複呼叫;dispose 後 mutators 會丟 `SpriteAnimatorDisposedError`。
|
|
76
|
+
- `onStateChange(handler, options?)` 與 `onComplete(handler, options?)` 支援 `once` 與 `signal`。
|
|
255
77
|
|
|
256
|
-
|
|
78
|
+
## 注意事項
|
|
257
79
|
|
|
258
|
-
|
|
80
|
+
- 非 loop state 若有 `onEnd`,會在 clip 完成的同一個 `update()` tick 轉場。
|
|
81
|
+
- Pixi adapter 接受 `AnimatedSprite`,因為它 extends `Sprite`;bind 時會 stop playback,避免跟 adapter 搶 texture。
|
|
82
|
+
- 所有 reachable frame keys 都必須存在於 texture map/spritesheet;缺少時丟 `MissingTextureError`。
|
|
83
|
+
- `duration`、`defaultFrameDuration`、state `speed` 都必須是 finite 且大於 0。
|
|
84
|
+
- 目前 schema backlog:替 `animations` 加 `minProperties`,並考慮用 finite numeric maximums 對齊 runtime guards。
|
|
259
85
|
|
|
260
|
-
##
|
|
86
|
+
## AI Context
|
|
261
87
|
|
|
262
|
-
|
|
88
|
+
- 短索引:[`llms.txt`](llms.txt)
|
|
89
|
+
- 完整生成內容:[`llms-full.txt`](llms-full.txt)
|
|
90
|
+
- 穩定度契約:[`STABILITY.md`](STABILITY.md)
|
|
91
|
+
- 目前 review backlog:[`REVIEW.md`](REVIEW.md)
|
|
92
|
+
- 範例索引:[`examples/README.md`](examples/README.md)
|
|
93
|
+
- 版本紀錄:[`CHANGELOG.md`](CHANGELOG.md)
|
|
263
94
|
|
|
264
95
|
## License
|
|
265
96
|
|
|
266
|
-
MIT
|
|
97
|
+
MIT
|