跨任务视角发现的问题:Critical1 materialize.mjs 的 junction 排除比较在 Windows 上因长路径前缀恒假、从未生效,且测试夹具的 junction 指向真源自身, 掩盖了此问题;Critical2 composeSkin 例外通道在 uuid 缺失/框架侧无 meta 时 静默失败,违反第二准则;Important3 可覆盖范围误把 theme/ 下的框架 TS 代码 也纳入;Important5 cli-entry 测试漏注册 check-skin/build-game;Important6 composeSkin 与 check-skin 各自拼路径,未共用 paths.mjs 权威推导;Important7 --platform 缺值时静默落到默认平台。逐条修复并补测试,Critical1 用诱饵框架 目录验证过能真正杀掉该 bug(变异推演见 fix report)。 spec §5 step5 的 dist/<name>/ 拷贝按要求标注为本期未实现,不实现。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
347 lines
26 KiB
Markdown
347 lines
26 KiB
Markdown
# framework/ui 资源归属 · 皮肤机制 · 打包裁剪 设计
|
||
|
||
> 状态:已与用户确认(brainstorming 通过),待转 writing-plans。
|
||
> 适用工程:`cocoscreator_projects/YouleNexus`(框架真源)+ `cocoscreator_projects/games/*`(子游戏)。
|
||
> 关联:架构 spec `2026-06-28-cocos-framework-design.md` §2(monorepo + junction)/§5(换肤覆盖机制)/§7(new-game)/§8(版本升级);配置 spec `2026-06-28-config-channel-design.md`(远程参数归属)。
|
||
> **本 spec 取代架构 spec §5 的落地形式**(见 §0.3),是 `framework/ui` 层动工的前置。
|
||
> 后续另立 spec:组件替换(`SeatView` 契约、约定节点名、注册机制、按人数布局配置)。
|
||
|
||
---
|
||
|
||
## 0. 目标与背景
|
||
|
||
### 0.1 三条需求
|
||
|
||
1. **R1 独立打包**:每个子游戏独立构建发布,包内**不含任何其它子游戏**的资源与代码。
|
||
2. **R2 各自皮肤**:每个子游戏的平台界面皮肤可以不同。
|
||
3. **R3 框架可传播**:框架改动(代码、界面、资源)方便传播到所有子游戏,消灭旧架构"人工替换到每个子游戏"的痛点。
|
||
|
||
### 0.2 决策依据(旧项目实测数据)
|
||
|
||
本 spec 的关键取舍不是推测,来自对旧 H5 项目的全量比对。**记录在此,便于日后回看为什么这么定。**
|
||
|
||
**平台 UI 代码在子游戏间几乎完全相同**:`js/00_Surface/11_GameUI.js` 模板 9862 行,`doudizhu` / `niuniu` / `majiang_jx` 与模板各只差 **18 行**,且这 18 行是功能开关(手机绑定是否走验证码、地址判定条件),**不是布局差异**。
|
||
|
||
> 例外:`erqiwang` 该文件仅 4629 行、与模板差 12379 行——属另一代模板,非同源演化,不作为依据。
|
||
|
||
**平台美术资源是"同名覆盖"模式**(`Game_Surface_3/assets` vs `doudizhu/assets`,全量 446 个文件,其中 444 个为可比对 PNG):
|
||
|
||
| 项 | 数量 | 占比(基数 444) |
|
||
|---|---|---|
|
||
| 内容完全相同(共用) | 328 | 73.9% |
|
||
| 被替换(同名不同内容) | 116 | 26.1% |
|
||
| — 其中**尺寸一致** | 114 | 占被替换的 **98.3%** |
|
||
| — 其中尺寸不同 | 2 | 占被替换的 1.7% |
|
||
| 非 PNG(未比对) | 2 | 不计入基数 |
|
||
| **子游戏缺失的文件** | **0** | — |
|
||
|
||
**推论(本 spec 的三块基石):**
|
||
|
||
- 约 74% 的平台资源在子游戏间共用 → **框架必须带一套完整默认皮肤**,否则这 328 张相同的图要在每个子游戏各存一份,且框架改了不会传播(违反 R3)。
|
||
- 文件清单完全一致、0 个缺失 → **同名路径覆盖**是充分的皮肤机制。
|
||
- 98.3% 的替换是同尺寸 → **"只换图片内容、保留 `.meta`"这条约束现实可行**;剩余 1.7% 走例外通道(§3.2)。
|
||
|
||
### 0.3 本 spec 取代架构 spec §5 的什么
|
||
|
||
架构 spec §5 的**方向保留**(子游戏定制全部落在自己的 `game/` 目录、框架真源一字节不改),**落地形式改变**:
|
||
|
||
| | 架构 spec §5 原方案 | 本 spec 方案 |
|
||
|---|---|---|
|
||
| 覆盖时机 | 运行时 `AssetResolver.load(逻辑路径)` 先查 override 后回退 | **构建期合成**(§5) |
|
||
| 资源加载 | 必须动态加载 | 保持**静态引用** |
|
||
|
||
**改变理由**:Cocos 中 Prefab 上的 `SpriteFrame` 是 UUID 硬引用,运行时覆盖机制拦不到;要让 `AssetResolver` 生效就必须把资源放进 `resources/` 或 Bundle,而那一档**无法被依赖树裁剪、全量进包**——被覆盖掉的框架默认资源会成为每个包里的死重量(按数据约 26%),直接损害 R1;同时框架 UI 将无法使用"美术在编辑器里拖图"这一 Cocos 主流开发方式。
|
||
|
||
前置事实(已与用户确认):**美术产出同名替换包、不进编辑器**,因此"编辑器里所见即所得地看到子游戏皮肤"不是需求,构建期合成的唯一代价被消除。
|
||
|
||
### 0.4 已确认决策
|
||
|
||
- 皮肤合成放**构建期**,框架资源保持**散图 + Auto Atlas**(Cocos 的自动图集在构建期才真正打包)。
|
||
- 框架带**完整默认皮肤**,子游戏只覆盖需要改的部分。
|
||
- theme 契约覆盖**资源覆盖 + 视觉变量 + 布局参数**三项;**平台功能开关不进 theme**,走远程配置(复用 `config/remote-config.ts` 的 `getParam`)。
|
||
- 定制通道共**三条**,组件替换是主力通道之一(§1),其完整设计另立 spec。
|
||
|
||
### 0.5 红线
|
||
|
||
- **服务器零改动**(CLAUDE.md 第一准则):本 spec 不涉及协议,`roomtype` / `deskinfo` 等协议契约归 `GameModule`,不进 theme(§4.1)。
|
||
- **数据源权威唯一、下游不兜底**(CLAUDE.md 第二准则):可覆盖资源清单**不另建文件**,框架文件树即清单(§3.1);override 匹配不上一律**报错中止**,不静默跳过(§3.2)。theme 的缺省值是该准则的**明文例外**,理由见 §4.3。
|
||
|
||
---
|
||
|
||
## 1. 三条定制通道
|
||
|
||
子游戏定制平台界面只有三条路,边界不得混淆:
|
||
|
||
| 通道 | 管什么 | 机制 | 本 spec |
|
||
|---|---|---|---|
|
||
| **资源覆盖** | 换图、换音效、换 Spine | 同名路径替换,构建期合成 | §2 §3 §5 |
|
||
| **theme 变量** | 颜色、字体、字号、坐标/尺寸数值 | 编译期配置对象 | §4 |
|
||
| **组件替换** | 换结构、换布局、换人数 | **注册**不同的 Prefab + 可选行为覆写 | 另立 spec |
|
||
|
||
**边界规则:**
|
||
|
||
1. **资源覆盖与组件替换是两条不同的路。** override 是"换同名资源的**内容**",组件替换是"注册一个不同的**实现**"。自定义 Prefab **不走** override 的同名校验体系,不放 `override/` 目录。
|
||
2. **能用 theme 解决的不做组件替换**,能用资源覆盖解决的不改 theme。就低不就高。
|
||
3. 三条通道的产物**全部落在子游戏 `game/` 目录**,框架真源一字节不改。
|
||
|
||
> **判断修正记录**:架构 spec §5 将插槽/组件定制定位为"辅",依据是旧项目布局高度一致(0 个文件缺失)。该数据反映的是**旧架构的能力上限**(旧项目只能靠 `Game_Config.Info.position[]` 硬配坐标微调),非新框架的目标。新框架需灵活支持**不同人数的不同布局**,故组件替换升格为主力通道。
|
||
|
||
---
|
||
|
||
## 2. 资源归属规则
|
||
|
||
**唯一判定标准:凡是放进 `framework/` 的,都会出现在每一个子游戏的包里。**
|
||
|
||
### 规则 1 — 框架带完整一套默认皮肤,不是占位图
|
||
|
||
依据 §0.2:73.9% 的平台资源在子游戏间共用。框架若不带默认,这部分要在每个子游戏各存一份,框架改了不会传播(违反 R3)。附带收益:新子游戏零美术即可跑通。
|
||
|
||
### 规则 2 — `framework/ui/` 只放"所有子游戏都需要"的平台界面资源
|
||
|
||
判定问句:**下一个子游戏还需要它吗?** 否则放子游戏 `game/`。
|
||
|
||
需主动挡住的反例:两三个子游戏共用的对局资源(如麻将与跑得快共用的牌型图)。"部分子游戏共用"**不构成**进框架的理由——放进去等于让其余子游戏白背包体。这类宁可各自拷贝,也不建"部分共享层"(共享层同样会进所有包)。
|
||
|
||
### 规则 3 — 目录划分 = 图集划分,由框架单独负责
|
||
|
||
Cocos 的 Auto Atlas 只对**同目录**散图生效,因此目录结构就是性能设计。按**加载时机**切分:
|
||
|
||
```
|
||
framework/ui/
|
||
├─ atlas-common/ 全程常驻:按钮、面板底、图标、通用弹窗 (.pac)
|
||
├─ atlas-login/ 登录场景专用 (.pac)
|
||
├─ atlas-hall/ 大厅场景专用 (.pac)
|
||
├─ atlas-room/ 房间/牌桌的平台部分专用 (.pac)
|
||
├─ standalone/ 超图集尺寸上限的大图、整屏背景(不进图集)
|
||
├─ spine/ 骨骼动画
|
||
├─ audio/ 平台音效
|
||
└─ font/ 字体
|
||
```
|
||
|
||
每个场景只加载自身图集 + `atlas-common`,不会为一张图拖进整张无关大图。图集划分**是框架的事,子游戏不参与**。
|
||
|
||
该结构同时保证 `framework/ui/` 整体可在将来直接转为一个 Asset Bundle(热更预留,本期不做,见 §7)。
|
||
|
||
### 规则 4 — 子游戏侧目录与框架逻辑路径逐字对应
|
||
|
||
```
|
||
games/<name>/assets/game/
|
||
├─ override/ 与 framework/ui/ 下路径一一对应的替换资源
|
||
├─ theme.ts 视觉变量 + 布局参数
|
||
├─ prefabs/ 自定义组件 Prefab(组件替换通道,不走 override)
|
||
└─ ... 对局逻辑与对局美术(与框架无关)
|
||
```
|
||
|
||
示例:替换大厅开始按钮 → `override/atlas-hall/btn_start.png`。路径必须逐字对应,合成脚本才能机械匹配(§3)。
|
||
|
||
### 一条原有约束在本方案下解除
|
||
|
||
早期分析曾要求"框架图集里只能放不换肤的图,否则换一张要重打整个图集、影响所有子游戏"。**本方案下该约束不成立**——构建期合成发生在打图集**之前**,换肤图与不换肤图可同处一个图集,构建时用替换后的散图重打即可。图集划分因此可纯按加载时机设计,不必为换肤让路。
|
||
|
||
---
|
||
|
||
## 3. override 契约与校验
|
||
|
||
### 3.1 契约本身:框架的文件树**就是**清单
|
||
|
||
override 下每个文件的路径,必须能在 `framework/ui/` 下找到同路径的默认资源。**这是全部契约。**
|
||
|
||
**不额外维护 `skinnable-assets.json` 之类的清单文件**——那会成为第二份真相,必然与实际文件树漂移。框架资源目录本身即权威清单(第二准则:数据源权威唯一)。
|
||
|
||
### 3.2 合成脚本的校验规则(构建期阻断式)
|
||
|
||
| 情况 | 处理 |
|
||
|---|---|
|
||
| override 文件在框架里**找不到对应路径** | ❌ **报错中止构建** |
|
||
| **图片**:同名、同尺寸、仅图片文件 | ✅ 换内容,框架 `.meta` **原封不动** |
|
||
| **图片**:同名、**尺寸不同**、仅图片文件 | ❌ 报错中止,提示改为同尺寸或走例外通道 |
|
||
| **图片**:需校验尺寸却**读不出尺寸**(非 PNG,或文件损坏) | ❌ 报错中止(`size-unreadable`)——**不得**当作通过静默放行 |
|
||
| 同名、override 自带 `.meta`(**例外通道**) | ✅ 拷贝两者,把框架默认 meta 的 `uuid` 覆写进去 |
|
||
| 扩展名/格式不一致(png → jpg) | ❌ 报错中止 |
|
||
| **非图片**(音效 / 字体):同名替换 | ✅ 换内容,`.meta` 原封不动,**不做尺寸校验** |
|
||
| **Spine**(`sp.SkeletonData`):骨骼数据 + atlas + 贴图 | ✅ 须**整套一起**覆盖,缺任一件即 ❌ 报错中止 |
|
||
| 框架有、子游戏未覆盖 | ✅ 正常,用默认(**不**强制全覆盖) |
|
||
|
||
**Spine 的特殊性**:新框架使用 **Cocos 原生 Spine 支持**(`sp.Skeleton` / `sp.SkeletonData`)。
|
||
|
||
> 旧项目因 gameabc 引擎不支持 Spine 而自行集成(`SpineMgr.js` / `spine-canvas.js`),**该实现对新框架无参考价值**,资源组织方式不照搬。
|
||
|
||
在 Cocos 的资源模型里,`SkeletonData` 是一个由骨骼数据、atlas 与贴图共同构成的复合资源,三者互相引用(骨骼数据引 atlas,atlas 引贴图区域坐标)。只替换其中一件会导致运行时错位或加载失败,且这类失败**不会在构建期暴露**——故强制整套覆盖,由校验兜住。
|
||
|
||
**Spine 贴图豁免"同尺寸"约束**:普通图片要求同尺寸,是因为区域坐标记在 `.meta` 里;而 Spine 的区域坐标记在 `.atlas` 文件内,整套替换时三者自洽,故贴图尺寸可变。校验须对 Spine 贴图跳过尺寸检查,否则会误报。
|
||
|
||
**为什么必须保留 `.meta`**:UUID 存放在 `.meta` 中,删除后重新导入会生成新 UUID,导致框架 Prefab 对该资源的引用全部断裂。
|
||
|
||
**例外通道的用途**:覆盖 §0.2 中 1.7% 的尺寸变化场景(含九宫格 border、pivot 等需要随尺寸调整的元数据)。由合成脚本负责覆写 `uuid`,保证引用不断。
|
||
|
||
**错误信息要求**:报错须指出**子游戏名 + 文件相对路径 + 期望的框架路径**,做到可定位(第二准则:错误早暴露、可定位)。
|
||
|
||
合成结束打印统计:`皮肤命中 116 处,框架可覆盖资源共 446 个`——一眼看出本次合成生效的范围。
|
||
|
||
### 3.3 该校验为资源层提供了"类 tsc 保护"
|
||
|
||
框架代码改动有 `tsc` 兜底,界面与资源改动此前**没有任何保护**。本校验补上资源层:
|
||
|
||
- 框架**新增**资源 → 子游戏无需任何动作,自动用默认 ✅
|
||
- 框架**删除或重命名**资源 → 所有覆盖过它的子游戏**下次构建立刻报错**,而非悄悄退回默认皮肤
|
||
|
||
即:框架改资源路径这类会波及全部子游戏的操作,从"无人知晓是否会炸"变为"构建期即炸,且指明炸在哪个子游戏"。
|
||
|
||
### 3.4 校验可脱离构建单独运行
|
||
|
||
`npm run check-skin <name>` 只执行本节校验,不实体化、不构建,秒级反馈。程序或美术拿到替换包即可自检。构建期那次是最后一道闸,不是唯一一道。
|
||
|
||
---
|
||
|
||
## 4. theme:视觉变量与布局参数
|
||
|
||
### 4.1 边界——哪些**不**进 theme
|
||
|
||
旧项目 `Game_Config` 是大杂烩,新方案须拆干净:
|
||
|
||
| 参数 | 归属 |
|
||
|---|---|
|
||
| 颜色、字体、字号、关键坐标/尺寸 | ✅ `theme.ts` |
|
||
| `roomtype` 数组长度与结构 | ❌ **协议契约**,归 `GameModule`;须与服务器逐字节一致,见 `docs/protocol/` |
|
||
| 支持的玩家人数集合、座位数 | ❌ 子游戏固有属性,归 `GameModule` |
|
||
| 手机绑定 / 支付按钮等**功能开关** | ❌ **远程配置**,复用 `config/remote-config.ts` 的 `getParam` 分层取参 |
|
||
| 重连间隔、心跳超时等协议常量 | ❌ 框架 `core/constants.ts`,子游戏无权改 |
|
||
|
||
### 4.2 形态
|
||
|
||
纯数据对象,编译期静态,无副作用。**类型由框架定义,子游戏实现**:
|
||
|
||
```ts
|
||
// framework/ui/theme/types.ts —— 框架定义契约
|
||
export interface ThemeConfig {
|
||
colors?: { /* 主色 / 强调色 / 文字主次色 / 禁用 / 警告 */ };
|
||
fonts?: { /* 字体资源 + 各级字号 */ };
|
||
layout?: { /* 关键位置与尺寸 */ };
|
||
}
|
||
|
||
// games/<name>/assets/game/theme.ts —— 子游戏只写要改的
|
||
import type { ThemeConfig } from '.../framework/ui/theme/types';
|
||
export const theme: ThemeConfig = {
|
||
colors: { primary: '#C8102E' },
|
||
layout: { myInfoAnchorX: 130 },
|
||
};
|
||
```
|
||
|
||
相对旧项目裸 JS 的 `Game_Config`,质变在于:**字段名或类型写错,`tsc` 当场报错**,不必等到真机上发现某个值没生效。
|
||
|
||
**字段的起步集不在本 spec 穷举**——见 §4.4。
|
||
|
||
### 4.3 缺省语义:第二准则的明文例外
|
||
|
||
`ThemeConfig` 全部字段**可选**;框架处提供一份 `DEFAULT_THEME` 承载全部缺省值;合并由框架内的 `resolveTheme(gameTheme)` 完成。
|
||
|
||
这看似"兜底",但正是 CLAUDE.md 第二准则明文列出的唯一例外:*"来源本身明确定义了「可选 + 缺省语义」,且该缺省写在来源处(而非散落在各下游)"*。框架是主题的权威来源,缺省集中于 `DEFAULT_THEME` 一处,下游子游戏无需任何兜底。
|
||
|
||
> **不得以此为先例**:本例外仅适用于 theme。协议数据(如 login 响应字段)缺失仍须显式暴露,不得 `??` 抹平。
|
||
|
||
### 4.4 「可改范围」如何维护才不失控
|
||
|
||
架构 spec §10 已识别风险:"皮肤变量预留不足,子游戏被迫改框架"。解法不是预先猜全清单,而是依靠两条性质:
|
||
|
||
1. **框架 UI 组件禁止硬编码**任何颜色、字号、字体、关键坐标,一律从 `ThemeProvider` 读取。这是框架实现期的硬规矩。
|
||
2. **加字段是非破坏性的**(可选 + 有默认),任何时候都能加;**删字段或改语义才是 breaking change**,须走版本说明通知所有子游戏。
|
||
|
||
因此本 spec **只定分组与规则,不预先穷举字段**:清单是框架 UI 抽象过程的产物,逐项浮现、按需扩充。起步集 = 实现第一个平台界面时确定需要的那几个。
|
||
|
||
### 4.5 消费方式:静态读取,不做响应式
|
||
|
||
皮肤在构建期即定死,运行时不变。框架启动时 `ThemeProvider.init(resolveTheme(theme))`,其后组件纯静态读取。
|
||
|
||
**不引入响应式**——`core/reactive.ts` 的 signal 服务于会变的状态(玩家资产、房间状态);主题不变,套用只是纯粹的复杂度。运行时切换皮肤见 §7 非目标。
|
||
|
||
---
|
||
|
||
## 5. 构建流水线
|
||
|
||
一条命令,从"挂着 junction 的开发工程"产出可发布包,同时完成校验、合成、零冗余裁剪。
|
||
|
||
```
|
||
npm run build-game <name> [--platform android]
|
||
|
||
1. 版本校验 子游戏与宿主 creator.version 一致,否则中止(复用 check-cocos-version)
|
||
2. 实体化 games/<name>/ → build-workspace/<name>/
|
||
assets/framework 由 junction 变为真实目录拷贝
|
||
3. 皮肤合成 assets/game/override/** 覆盖到 assets/framework/ui/**
|
||
按 §3.2 校验;任一不合法即中止;打印命中统计
|
||
4. 构建 调 Cocos CLI 在临时工程上构建
|
||
5. 产出 写 build-info.json(拷至 dist/<name>/ 本期未实现,见下)
|
||
```
|
||
|
||
> **`dist/<name>/` 拷贝:本期未实现。** `build-game.mjs` 目前止步于 `build-workspace/<name>/`
|
||
> 内调用 Cocos CLI 构建 + 写 `build-info.json`;把构建产物拷到独立的 `dist/<name>/` 尚未落地
|
||
> (构建产物的实际目录结构未经真实构建验证,贸然实现拷贝逻辑等于凭空猜测路径)。
|
||
|
||
### 5.1 四条硬规则
|
||
|
||
**① 绝不在框架真源上做合成。**
|
||
"改真源 → 构建 → 还原"的做法被否决:构建中断或崩溃会留下**被污染的真源**,而真源为所有子游戏共享的唯一副本。合成只在临时工程副本内进行,**真源全程只读**。
|
||
|
||
**② 发布链路完全不依赖 junction。**
|
||
第 2 步实体化直接从 `YouleNexus/assets/framework` 读真源拷贝,**不要求 junction 存在**。CI clone 后无需 `setup-links` 即可构建。
|
||
|
||
> junction 只服务开发期的即时性(R3);发布期靠实体化获得自包含工程。架构 spec §10 记录的风险"junction 不被 git 跟踪,团队成员环境缺链接"在发布链路上因此不复存在——不是绕过,是发布链路根本不走该路径。
|
||
|
||
**③ 临时工程不得置于 `games/` 下。**
|
||
否则 `scripts/lib/paths.mjs` 的 `listGameProjects()` 会将其识别为真实子游戏,污染 `setup-links` 与 `check-cocos-version`。固定放 `build-workspace/`,加入 `.gitignore`。
|
||
|
||
**④ Cocos 编辑器可执行文件路径只在一处定义**(环境变量或单一配置文件),其余脚本引用(第二准则常规应用)。
|
||
|
||
### 5.2 首次构建的性能问题
|
||
|
||
临时工程为全新工程,Cocos 首次打开须完整导入 assets 重建 `library`;框架 + 子游戏资源合计可达上万文件,该步骤可能耗时数分钟至十余分钟。
|
||
|
||
**对策**:保留 `build-workspace/<name>/library/` 跨次构建复用,仅在资源变化时增量导入。代价为磁盘占用(每子游戏一份 library)。
|
||
|
||
该对策列入 §6 待实测。**方案整体可行性不依赖它**(最差情况每次全量导入,只是慢),但流水线的日常可用性依赖它。
|
||
|
||
### 5.3 产物追溯:`build-info.json`
|
||
|
||
```json
|
||
{
|
||
"game": "doudizhu",
|
||
"frameworkCommit": "<git rev-parse HEAD>",
|
||
"builtAt": "<ISO 时间>",
|
||
"platform": "android",
|
||
"skin": { "matched": 116, "frameworkTotal": 446 }
|
||
}
|
||
```
|
||
|
||
落地"框架版本可追溯",成本近零:出问题可立刻查清该包所用框架版本与皮肤合成生效程度。
|
||
|
||
---
|
||
|
||
## 6. 待实测清单
|
||
|
||
以下三条是本方案的承重点,**须在第一个真实框架场景/资源出现后立即验证**,不得凭推断实现。
|
||
|
||
| # | 待验证 | 验证方法 | 失败后的备选 | 实测结论(2026-08-27,Task 2) |
|
||
|---|---|---|---|---|
|
||
| 1 | Cocos 3.8.8 的 Auto Atlas 确在**构建期**打包,构建前替换散图能被正确纳入图集 | 造一个 `.pac` 目录,替换其中一张散图后构建,检查产物图集内容 | 改为"图集整体作为覆盖单位",由子游戏提供整套 plist+png | ⏸ **阻塞(非证伪)**。真正卡住的不是"构建能否跑完",而是**"造不出一个真实的 Auto Atlas 资产"**:MCP `asset.create` 只支持 `url`/`content`/`overwrite`,没有资产类型参数;对 `.pac` 命名文件夹做 `refresh`/`reimport` 也不会把 `importer` 从 `"directory"` 转成 `"auto-atlas"`,多次独立尝试一致。(`editor.build`/`editor.open_build_panel` 确实也只打开构建面板要求人工点击——但项目自己的 `build-game.mjs` 已实现走 `CocosCreator.exe --project <dir> --build "platform=..."` 的官方无头 CLI 构建,不经过 MCP;只是本机 `COCOS_CREATOR` 未配置、也未定位到编辑器可执行文件,这条路本身也未验证可用,且即便可用也不能替代"创建 Auto Atlas"这一步——那始终是编辑器 Assets 面板 `Create > Auto Atlas` 的菜单命令,CLI 构建帮不上。)需人工在编辑器 GUI 内手动执行该菜单命令后再复测。**在此之前不得默认本假设成立去推进 Task 3 的图集相关设计。** |
|
||
| 2 | 例外通道中,除顶层 `uuid` 外,图片 `.meta` 的 `subMetas`(spriteFrame 子 uuid)是否亦需同步覆写 | 用尺寸不同的图走例外通道,检查 Prefab 引用是否仍有效 | 合成脚本一并覆写 subMetas 的 uuid | 🟡 **部分验证,范围窄于本行原意**。实测的是更基础的前提:保留原 `.meta`、只换图片内容(同尺寸)后刷新,顶层 `uuid` 与全部 `subMetas.uuid` 均未变化;换成 128×128(仍不提供新 `.meta`,未走例外通道)后刷新,顶层 `uuid` 依旧不变,且 sprite-frame 子 meta 的 `width`/`height`/`rawWidth`/`rawHeight`/`vertices`/`uv` 被 Cocos 自动重算为 128×128(前后 meta 全文见 task-2-report.md)。**未验证**的是本行字面所指的例外通道场景(override 自带一份独立生成的 `.meta`,合成脚本只覆写顶层 `uuid`,问 `subMetas` 里的子 uuid 要不要也同步覆写)——因待验证项 1 阻塞、无法在真实 `.pac` 图集环境下复测,这条更精确的问题仍待专项验证。spec §3.2 的例外通道设计**维持不变**,不降级、不简化。 |
|
||
| 3 | `build-workspace/<name>/library/` 跨次构建复用是否可靠,资源被替换后增量导入是否正确刷新 | 连续两次 build-game,第二次改动 override,比对产物 | 放弃缓存,每次全量导入(仅影响耗时) | ⏸ **阻塞:缺构建环境**(2026-08-27,Task 8)。实测需要 `COCOS_CREATOR` 环境变量指向 Cocos Creator 3.8.8 可执行文件——本机未配置该变量,且 `where.exe CocosCreator.exe` 未能在 PATH 上定位到编辑器可执行文件,`build-game.mjs` 的实际构建步骤(内部调用 `CocosCreator.exe --project <dir> --build "platform=..."`)无法执行,因而无法运行"连续两次 build-game 比对耗时/产物"的实测动作。此外,`buildGame` 当前实现每次都会 `rmSync` 整个构建工作区(含 `library/`)后重新实体化,即便环境就绪,字面意义上的"library 跨次复用"在现有实现下也不成立——`library` 目录会随工作区一起被清空重建,第二次构建拿到的是全新的 `library`,不存在缓存复用可言。故本行结论有两层:(a) 环境阻塞,实测动作未能执行;(b) 静态阅读代码可确认现有实现未实现"保留 library 复用"的设计,若要验证复用效果需先改造 `buildGame` 为"不整体 rmSync 工作区、只刷新 assets",这已超出本任务范围,留作 spec 结尾「后续计划衔接」中已列出的独立任务。**在此之前不得默认 library 复用已生效或已失效去做性能相关决策。** |
|
||
| 4 | Cocos 的 `sp.SkeletonData` 在"整套替换内容、保留全部 `.meta`"后引用是否仍自洽(骨骼数据 ↔ atlas ↔ 贴图的 uuid 关联记在何处) | 用一套不同的 Spine 资源整套替换,检查场景内动画是否正常播放 | Spine 改走组件替换通道(子游戏注册自己的 `sp.Skeleton` 节点),不走资源覆盖 | ⏸ **阻塞:仓库无 Spine 素材**。`projects/*/assets/spine/` 全部为空,无任何可用 Spine 导出资源。`cocos_spine` 工具的全部 action(`info`/`list_animations`/`list_skins`/`set_animation`/`set_skin`/`set_property`/`set_data`/`add_socket`/`remove_socket`)都要求场景内已存在带 `sp.Skeleton` 组件的节点,无法凭空验证。已在 `framework/ui/README.md` 加提示,待第一套真实 Spine 资源到位后专项验证,**不假定其可行**。 |
|
||
|
||
---
|
||
|
||
## 7. 非目标(YAGNI)
|
||
|
||
- **运行时切换皮肤**(同一包内多套皮肤、节日换肤)。皮肤构建期定死;§2 的目录结构不阻碍将来扩展。
|
||
- **强制子游戏全量覆盖**。覆盖 1 个文件与覆盖 116 个文件同样合法。
|
||
- **单独的可覆盖资源清单文件**。框架文件树即清单(§3.1)。
|
||
- **`framework/ui` 的 Bundle 化热更**。§2 规则 3 的目录结构为其预留,本期不实现;是否引入需另行评估合规与版本管理成本。
|
||
- **编辑器内预览子游戏皮肤**。已确认美术不进编辑器,无此需求。
|
||
|
||
---
|
||
|
||
## 8. 与其它 spec 的接口
|
||
|
||
- **`GameModule`**(sdk spec,待写):须声明**支持的玩家人数集合**与 `roomtype` 契约。注意人数是**运行时**属性(由房间 `roomtype` 决定,同一子游戏可支持多种人数),布局须按运行时人数选取。
|
||
- **远程配置**(`config-channel-design.md`):平台功能开关与 UI 类远程参数复用 `getParam` 分层取参,本 spec 不涉及。
|
||
- **组件替换 spec**(待写):`SeatView` 契约(统一 `render(state)` 而非逐字段 setter)、约定节点名清单、注册机制、按人数的布局配置结构。其前置是 `RoomStore` 的完整建模(platform Store 第二切片)。
|
||
- **工具链**(`monorepo-scaffold-toolchain.md`):新增 `build-game` / `check-skin`;`new-game` 需增建 `override/` 与 `prefabs/` 骨架目录。
|