@coralai/sps-cli 0.65.72 → 0.65.74

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 (39) hide show
  1. package/dist/commands/artCommand.d.ts +4 -0
  2. package/dist/commands/artCommand.d.ts.map +1 -0
  3. package/dist/commands/artCommand.js +277 -0
  4. package/dist/commands/artCommand.js.map +1 -0
  5. package/dist/commands/skillCommand.js +13 -1
  6. package/dist/commands/skillCommand.js.map +1 -1
  7. package/dist/core/skills/distribution.d.ts +13 -0
  8. package/dist/core/skills/distribution.d.ts.map +1 -1
  9. package/dist/core/skills/distribution.js +50 -12
  10. package/dist/core/skills/distribution.js.map +1 -1
  11. package/dist/core/skills/ownership.d.ts +46 -0
  12. package/dist/core/skills/ownership.d.ts.map +1 -0
  13. package/dist/core/skills/ownership.js +63 -0
  14. package/dist/core/skills/ownership.js.map +1 -0
  15. package/dist/main.js +18 -1
  16. package/dist/main.js.map +1 -1
  17. package/dist/providers/art/atlas.d.ts +34 -0
  18. package/dist/providers/art/atlas.d.ts.map +1 -0
  19. package/dist/providers/art/atlas.js +71 -0
  20. package/dist/providers/art/atlas.js.map +1 -0
  21. package/dist/providers/art/frames.d.ts +43 -0
  22. package/dist/providers/art/frames.d.ts.map +1 -0
  23. package/dist/providers/art/frames.js +94 -0
  24. package/dist/providers/art/frames.js.map +1 -0
  25. package/dist/providers/art/pixels.d.ts +33 -0
  26. package/dist/providers/art/pixels.d.ts.map +1 -0
  27. package/dist/providers/art/pixels.js +431 -0
  28. package/dist/providers/art/pixels.js.map +1 -0
  29. package/dist/providers/art/sheet.d.ts +70 -0
  30. package/dist/providers/art/sheet.d.ts.map +1 -0
  31. package/dist/providers/art/sheet.js +312 -0
  32. package/dist/providers/art/sheet.js.map +1 -0
  33. package/dist/services/SkillService.d.ts +6 -0
  34. package/dist/services/SkillService.d.ts.map +1 -1
  35. package/dist/services/SkillService.js +3 -0
  36. package/dist/services/SkillService.js.map +1 -1
  37. package/package.json +3 -1
  38. package/skills/art-chain/SKILL.md +242 -0
  39. package/skills/registry.yaml +2 -0
@@ -0,0 +1,242 @@
1
+ ---
2
+ name: art-chain
3
+ description: 游戏美术资源链 SOP——从 AI 直出图到可用的雪碧图/atlas:去背景(色键→残留判据→rembg 兜底)、切帧、打包、归一化、尺寸检查。绝大部分是确定性运算(sps art 子命令),你只在几个岔路口做判断。
4
+ category: workflow
5
+ audience: worker
6
+ tags: [game, media, asset-pipeline]
7
+ whenToUse: 处理游戏美术资源——抠背景、把序列图切成帧、打雪碧图+atlas、按屏幕尺寸判断资源大小是否合适。
8
+ insteadUse: 只是生成一张图→generate_asset 工具直接调;判画面美术质量是否达标→visual-rubric。
9
+ ---
10
+
11
+ # 游戏美术资源链
12
+
13
+ **这条链上绝大多数步骤是确定性运算,不需要你算。** 你的职责是:看事实、选路、判可接受。
14
+ 运算一律用 `sps art <子命令>`(stdout 出 JSON),**不要自己写像素处理脚本**。
15
+
16
+ > 🔴 **本文档里带"为什么"的段落,每一条都对应一次真实事故。**
17
+ > 它们看起来像可以合并的冗余 —— 不是。删掉任何一条,产物都**照常导出、照常加载、不报错**,
18
+ > 只是在游戏里错。**读到"丢了会怎样"时请当真。**
19
+
20
+ ---
21
+
22
+ ## 〇 · 你在哪几个点做判断
23
+
24
+ ```
25
+ 纯工具(不用你判):色键 · rembg · cleanAlpha · 切帧 · 打包 · 归一化 · resize · atlas 写盘
26
+ 🔴 要你判断的四处:
27
+ ① 切帧模式选 grid 还是 auto
28
+ ② frames ≠ expectedFrames 时,是否换模式重来
29
+ ③ 资源尺寸相对屏幕是否过大
30
+ ④ 抠图结果是否可接受(看不出来就别改判据,见 §2)
31
+ ```
32
+
33
+ **其余任何"我觉得可以优化一下"的念头,先读那一段的"为什么"。**
34
+
35
+ ---
36
+
37
+ ## 一 · 全链形状
38
+
39
+ ```
40
+ AI 直出原图(洋红底)
41
+
42
+ ├─ 留底 ────────────────► 真原图(抠图/缩放前)。出了问题只有它能复现,不可省
43
+
44
+ ├─ 已经透明? ──是─► 跳过整个去背景(模型原生 RGBA)
45
+ │ 否
46
+ ├─ ① 色键(确定性)
47
+ │ └─ 品红残留 > 0.005 ?
48
+ │ 是 ──► ② rembg(语义兜底)──► cleanAlpha
49
+ │ 否 ──► 用 ① 的结果
50
+
51
+ └─ 压缩(resize + 编码一次)
52
+ ```
53
+
54
+ ### 🔴 走 rembg 时,喂进去的是**原图**,不是色键结果
55
+
56
+ ```
57
+ ✅ sps art rembg --in 原图.png
58
+ ❌ sps art rembg --in 色键结果.png ← 写成这样就错了
59
+ ```
60
+
61
+ **为什么**:色键已经把背景打成透明/半透明。再喂给 rembg = 让语义模型去看**一张被破坏过的图**,
62
+ 它的前景/背景判断会更差,不是更好。**两条路是互斥的备选,不是串联的两道工序。**
63
+
64
+ **如果你写成后者,症状是**:图能出、能加载,主体边缘被啃掉一圈,或者干脆判反(主体被当背景)。
65
+ 没有任何报错。而且没人会想到去查"我们是不是把中间产物喂给了兜底方案"。
66
+
67
+ ---
68
+
69
+ ## 二 · 去背景
70
+
71
+ ### ① 色键:双重门
72
+
73
+ 背景是**已知先验**(平台生图提示词强制纯洋红底),不是从图里猜的。
74
+
75
+ ```
76
+ 第一道(色相):s = min(R-G, B-G) 洋红 = 红蓝同时远高于绿
77
+ 第二道(距离):到**本图实测背景色**的距离
78
+ ```
79
+
80
+ 🔴 **两道门缺一不可,它们分工不同**:
81
+ - `s` 门负责「**只考虑洋红系**」—— 不吃白底/浅底图
82
+ - 距离门负责「洋红系里**只抠背景那一种**」—— 不吃紫色主体
83
+
84
+ **为什么光有色相不够**:紫宝石 `(182,67,249)` 的 `s=115`,照样过第一道门。
85
+ 它到实测背景 `(249,3,239)` 的距离 ≈ 93 → 第二道门救回来。
86
+ > **真实事故**:一个项目的宝石被整个抠掉,就是只有色相门的那个版本。
87
+
88
+ 🔴 **背景色是实测的,而且优先从边框采**:不假设 `#FF00FF`。模型画出来的常是偏粉的洋红(如 `(205,27,122)`)。
89
+ 硬编码 FF00FF → 距离门把真背景判成"离得远" → **一点都抠不掉**。
90
+ 采样优先级:**边框上的洋红像素均值(≥20 个才采纳)** → 没有再退回全图均值。
91
+ **为什么优先边框**:生成契约下边框必然是背景;全图均值会被紫斗篷/紫宝石拉偏,**两道门一起失准**。
92
+
93
+ 🔴 **分两个阶段(外部背景 / 内部镂空)**:
94
+ 阶段②要回答的是——一块被主体包住的洋红,是**镂空洞**(手环内圈)还是**主体装饰**(宝石内芯)?
95
+ **判据只能是「到背景基准色的距离」,不能是色相** —— 偏紫装饰 `(195,40,180)` 的 `s=140` 也很高,**按 s 根本分不开**。
96
+ **丢了会怎样**:手环里留一块洋红(游戏里是个洋红色的洞),或者宝石内芯被抠穿。两种都能正常导出加载。
97
+
98
+ 🔴 **后处理三步,顺序不能换**:`去晕环 → RGB 外溢 → alpha 平滑`
99
+ 先洗净边界色,再外溢**干净的**色,平滑时新增的半透明像素才已具备正确 RGB。
100
+ 换了顺序 = 把**脏色**摊得更大。
101
+ **"RGB 外溢"最容易被当成多余删掉** —— 因为 PNG 本身完全正常,任何看图软件都看不出问题。
102
+ 它防的是:引擎的双线性过滤**会采样到透明邻居的 RGB(alpha=0 也照采)**。
103
+ **丢了会怎样**:**只有在引擎里、只有在缩放/旋转时**才出现粉边。这是本条最贵的地方。
104
+
105
+ ### ② 判据:品红残留 > 0.005 → 走 rembg
106
+
107
+ 🔴 **这个阈值在决定「这张图值不值得交给模型」,不是「抠得干不干净」。**
108
+ **不要为了"提高抠图质量"去调它。**
109
+
110
+ **为什么不用"四角是否透明"**:前景条带类资产(草丛贴满下缘)**下角天然不透明** →
111
+ 被误判成失败 → 交给 rembg → rembg 把"一排草丛"当背景整体吞掉,只留语义主体。
112
+ > **真实事故**:某跑酷项目的前景层只剩一根蕨叶。
113
+
114
+ **读图失败时按"有残留"处理**(保守走 rembg)—— 宁可多走一次兜底,也不要把读不出来的图当成抠干净了。
115
+
116
+ ### ③ rembg:语义兜底
117
+
118
+ 它是**跑着的 HTTP 服务**,不需要你装任何东西、不需要碰权重。
119
+
120
+ 🔴 **model 必须 `isnet-general-use`** —— `isnet-anime` 对葡萄这类物体会**整张删掉**(不是抠差,是全没)。
121
+
122
+ 🔴 **rembg 之后必须 cleanAlpha** —— rembg 出的是**连续 alpha**,边缘一圈半透明像素**带着原背景色**,
123
+ 贴到别的背景上就是一圈脏边。
124
+ **丢了会怎样**:图能出、能加载、缩略图正常;**要放大到像素级、或换个背景色贴一次**才看得出来。
125
+
126
+ > 📌 判它"底座在不在"错过两次,两次都是查了 CLI 装没装 / 权重文件权限,而它实际是 HTTP 端点。
127
+ > **问"谁在提供这个能力",不要问"这个包装了没有"。**
128
+
129
+ ---
130
+
131
+ ## 三 · 切帧
132
+
133
+ ### 🔴 判断点①:选 grid 还是 auto
134
+
135
+ ```
136
+ 规整横排序列帧 → mode=grid
137
+ AI 直出的不规则图集/单图 → mode=auto(默认)
138
+ ```
139
+
140
+ **为什么 `grid` 不能删**:连通域会把**一个动作的分离部件**误切成多帧 ——
141
+ 波动拳的能量球和角色是两个连通域 → 一帧变两帧 → **帧数对不上**。
142
+
143
+ **为什么 `auto` 是默认**:AI 生成的网格**几乎从不是像素级均匀**。按 `图宽/cols` 等分必然切歪。
144
+ **丢了会怎样**:每帧都能导出、动画能播 —— 只是每帧偏几个像素,角色**在原地抖**。
145
+ 很容易被当成"AI 画得不稳"。
146
+
147
+ ### 🔴 判断点②:frames ≠ expectedFrames 就重来
148
+
149
+ `sps art slice-sheet` 返回:
150
+ ```json
151
+ { "frames": 实际切出, "expectedFrames": cols × rows }
152
+ ```
153
+ **两者不符 = 切分可疑** → 换 `mode=grid` 重来。**这是给你用的自检口,不是日志 —— 你要主动去比这两个数。**
154
+
155
+ ### auto 的三道清理(别删任何一道)
156
+
157
+ ```
158
+ ① 丢噪点 面积过小的块丢掉 → 根治 1×1 误检帧
159
+ ② 并回母体 小块落在更大块内且自身很小 → 高光/眼睛反光/断开的描边点
160
+ ③ 行序排列 按中心 y 分行带,行内按 x → 帧序稳定
161
+ ```
162
+ **② 的两个条件缺一不可**:只判包含会把并排的独立精灵误并;只判大小会把小配件并进无关大块。
163
+ 两个并排的独立精灵 bbox 不会互相包住 —— **这是它安全的理由**。
164
+
165
+ 🔴 **③ 帧序丢了最隐蔽**:flood-fill 的产出是**扫描线顺序**,不是人看的顺序。
166
+ 不排序 → **动画帧序乱,但每张图完全正常**。
167
+ 行带阈值取**中位高的 0.7 倍**而非固定值 —— 让阈值跟着这张图的尺度走,否则帧高不一致时同一行会被拆成两行。
168
+
169
+ **检测半成功比检测失败更危险**:行带数 ≠ rows 时工具会**退回等分**并告诉你。保留这个行为,别去"修好它"。
170
+
171
+ ---
172
+
173
+ ## 四 · 打包 atlas
174
+
175
+ ### 🔴 帧顺序必须保持(空格用透明占位,不能跳过)
176
+
177
+ 打包重排的是**位置**,不是**顺序**。第 i 帧换了 rect,但它仍然是第 i 帧。
178
+ **为什么占位而不是跳过**:跳过会让后面所有帧索引前移,而游戏按 `<id>_0 / <id>_1 / …` 引用 ——
179
+ **一个空格就能让整套动画错位一帧。**
180
+ **丢了会怎样**:图集正常、atlas 正常、帧数正常,**动画播出来是错的**。
181
+
182
+ ### 🔴 帧间留白不能省
183
+
184
+ 防**纹理采样越界串色**(同 alpha-bleeding 的道理:双线性过滤会采到邻帧像素)。
185
+ **丢了会怎样**:图集更小更"高效",游戏里每帧边缘闪一下隔壁帧的颜色。
186
+
187
+ ### 🔴 限制帧高要用 `--frame-max-height`,**绝不能事后 resize 整张图集**
188
+
189
+ ```
190
+ ✅ sps art pack-atlas --frame-max-height 128
191
+ ❌ 打完图集再 sps art resize 整张 ← 灾难
192
+ ```
193
+ **为什么**:事后 resize 会把「单帧上限」**误当整图上限** —— 8 帧的图集被缩到单帧大小,**每帧变成原来的 1/8**。
194
+ **丢了会怎样**:图集能出、atlas 坐标自洽、游戏能加载 —— **角色变成一个小点**。
195
+
196
+ ### 🔴 atlas 里 `trimmed:false` 且偏移为 0
197
+
198
+ 切帧时**已经**把每帧裁到紧致 bbox 了。若声明 `trimmed:true` 并给偏移,引擎会**再补一次偏移** → 帧就偏了。
199
+
200
+ > **一句原则**:**对引擎的声明要描述「引擎将看到的东西」,不是「我们做过什么」。**
201
+ > 我们裁过 ≠ 要告诉引擎"这是裁过的"。
202
+
203
+ **丢了会怎样**:atlas 合法、加载成功、每帧位置都偏一点。
204
+
205
+ ---
206
+
207
+ ## 五 · 归一化:两个不同的东西,别合并
208
+
209
+ | 场景 | 做法 | 为什么 |
210
+ |---|---|---|
211
+ | **图集内** | 等大 + **居中** | 让下游 `frameWidth = 图宽/cols` 的等分变成对的 |
212
+ | **角色动画** | 抠到内容框 + 🔴 **脚底对齐**(底部居中) | 角色动画的基准是**脚底** |
213
+
214
+ 🔴 **为什么角色动画不能用居中**:蹲下/跳跃帧的中心高度不同 → 播放时角色**上下浮动**。
215
+ **丢了会怎样**:每帧单看都正常,动画播起来**角色在飘**。很容易被当成"动画做得不好",而不是"对齐基准选错了"。
216
+
217
+ ---
218
+
219
+ ## 六 · 尺寸检查(判断点③)
220
+
221
+ `sps art inspect` 返回**确定性事实**:尺寸、通道、是否已透明、**以及相对屏幕的占比**。
222
+
223
+ **为什么要占比而不是只给尺寸**:1024px 对 1920 宽的游戏是正常的,对 480 宽的游戏是灾难。
224
+ **给绝对值你判不了。**
225
+
226
+ 缩放用 `sps art resize`:**只缩不放**(放大会糊)、**等比**(裁剪/拉伸会毁主体)、**已在范围内就不动**(避免重复编码累积体积)。
227
+
228
+ 🔴 **`resize` 不适用于图集** —— 图集必须用 `pack-atlas --frame-max-height`(见 §四)。这是**禁止**,不是建议。
229
+
230
+ ⚠️ **拿不到游戏尺寸时 `inspect` 会报错,不会给默认值。** 这是故意的:
231
+ **它的产物是「判断依据」,判断依据不能有默认值** —— 一个假的参照系会让所有占比都错,
232
+ 而你拿到的是一个看起来完全合理的数字。报错时去确认项目配置,别绕过。
233
+
234
+ ---
235
+
236
+ ## 七 · 判断点④:抠图结果可不可接受
237
+
238
+ **看得出问题再改,看不出就别动判据。** 本文档里几乎每个常数都是某次事故调出来的:
239
+ 残留阈值、行带系数、留白宽度、alpha 上下限、外溢环数……
240
+
241
+ **"这个常数看起来可以调一下"这个念头出现时,先去读它那一段的"为什么"。**
242
+ 如果那段说了"丢了会怎样(不报错)",那就是说:**你改完之后不会立刻看到问题,用户会。**
@@ -51,6 +51,8 @@ categories:
51
51
  tags:
52
52
  # 产品 / 领域面
53
53
  game: 游戏开发
54
+ media: 图像 / 音视频 / 资源处理
55
+ asset-pipeline: 美术资源流水线(抠图 / 切帧 / 图集 / atlas)
54
56
  web: 网页 / 浏览器
55
57
  backend: 服务端 / 后端
56
58
  api: API 设计 / 集成