Files
SpineWallpaper/docs/adr/0008-asset-layout-per-skeleton.md
Shuery 3f11426964 feat: TS 构建管线、Spine 抓取器与 wallpapers/ 唯一真相来源
把项目从「手写 dist/」改成「wallpapers/ 是唯一真相来源,dist/ 由 pnpm build 生成」,
并补上配套的类型、门禁与抓取器。一次提交落地整条管线,因为拆开会留下不能构建的中间态。

- src/:运行时与模拟器源码(TS,strict),编译到 build/ 再拷进各分发
- tools/:build / dev / check-{syntax,paths,dist},以及抓取器与回归门禁 tools/checks/
  (.scratch/ 下那批一次性脚本移入 tools/checks/ 并入库为长期门禁)
- wallpapers/:七档壁纸的源数据 + README.md(id/音频/预设的完整规范)
- docs/adr/0005-0008:构建管线与分发拓扑、模拟器契约、自包含 sim、每骨架资源布局
- .gitignore:排除 .scratch/ 的参考资料副本(上游 spine 整仓克隆 ~1.2 GB、
  抓取侦查数据 ~680 MB)与调试转储;这些是本地调查材料,补偿会让仓库无法克隆
- 归一化 .gitignore/CONTEXT.md 行尾(工作区 CRLF、索引 LF 造成的整文件假 diff)

同时修掉三档卡住构建的未完工壁纸:
- kv45 的 meta.json 里 id 还是抓取期场景名 scene_main,经 downloader promote 正名为 kv45
- shajin / zhigengniao_juheye 的 meta.json 误用了骨架描述文件(name/spine/animations/pages)、
  且都缺 preset.template.json;现按规范重建:骨架沉到 spines/<名>/(spine-ts 按 atlas 所在
  目录解析贴图页)、补上元数据与单骨架预设,并清掉 zhigengniao 骨架里指向作者机的绝对路径
- 顺带 promote 已在 sources.yml 里的 kv46(月升之前,与兽共舞)

pnpm check 五道门全绿:10 个分发 / 7 档壁纸 / 141 处引用自包含。
2026-10-02 01:27:02 +08:00

3.6 KiB
Raw Permalink Blame History

资源布局:一具骨架一组,而不是按文件类型分组

取代 ADR 0001 第 7 行「保留 effects//images//audios/ 三层同名子目录」与 ADR 0005 第 43 行 「这一条不变」。那两条的前提是"Spine 骨架 JSON 硬编码了 "images": "../images/",改了就得动骨架文件"; 本决策把贴图页并到 atlas 同目录、把骨架里的 images 归零之后,这个前提本身消失了。

背景

wallpapers/<游戏id>/<壁纸id>/ 原来是按文件类型分的:骨架数据在 effects/<名>.json, atlas 与贴图页在 images/(或 images/<名>/),场景图在 images/scene/。于是一具骨架自己的 东西被拆到两个目录,找一样东西要跳两次;effects/ 这个名字还有歧义(它装的是骨架数据, 不是"特效",而真正的场景图在别处)。

查过官方文档:Spine 对 HTML 项目的目录结构没有任何约定。导出文档 只规定"每个骨架一个数据文件、文件名用骨架名",外加导出时可选 pack 出 atlas。所以这不是"遵循规范"的问题, 而是我们自己的取舍。

真正约束布局的只有一条运行时的解析规则(读 vendored spine-ts 源码得到):

page.setTexture(assetManager.get(pathPrefix + page.name));   // AssetManager.loadTextureAtlas

即 atlas 第一行是页名、页相对 atlas 所在目录解析;skeleton.images 只是可选前缀。 所以「页必须与 atlas 同目录」是硬约束,其余随便放。

来源页面(米哈游活动页)自己也是按类型分的(images/effect/spine/*.json|.atlas、 images/effect/scene/*、audio/*),它能这么干是因为它的引擎有全局图片表、按逻辑名查页; 我们用的是 spine-ts,做不到。

决策

按"一件东西一组"分,而不是按后缀分:

wallpapers/<游戏id>/<壁纸id>/
├── spines/<骨架名>/<名>.json | <名>.atlas | 贴图页…     ← 一具骨架一组
├── scene/                                              ← 场景图与背景图(非骨架的图)
├── audios/                                             ← 音源
├── meta.json  preset.template.json  preview.gif
  • 页跟着 atlas 走,天然满足运行时规则(不再需要 skeleton.images 前缀)。
  • 去掉了有歧义的 effects/:骨架就是骨架。
  • 一档壁纸目前就是一个场景(sceneConfig 就是那一个),所以不再多套一层 scenes/<场景id>/; 真出现"一档多场景"时再加,而不是现在提前分层。
  • 例外:backgroundImage 可以指向某个骨架的贴图页(kv37 就是这样),那种情况就写 spines/<名>/<页>,不强行搬进 scene/——判据是 atlas 自己声明的页名,不做名字启发式。

后果

  • 构建侧要同步的一处硬编码:tools/lib/generate.ts 的 copyWallpaperAssets 只搬 spines / scene / audios 三个目录名;改名必须改它。
  • tools/downloader/promote.py 产出的新壁纸直接是这套布局(否则存量与增量的形状会分叉)。
  • 存量三档壁纸迁移了 86 个文件,预设路径按实际落点重写; verify-visual-equivalence 确认三个比例下逐像素相同(布局不影响渲染)。
  • 迁移脚本留在 .scratch/build-pipeline/(含补救脚本),其中固化一条教训: 跨平台路径映射别拿 str(Path) 当键(Windows 会给反斜杠,而预设里是正斜杠)。