Files
youle_cocos/docs/superpowers/specs/2026-08-30-legacy-engine-api-mapping.md
joywayerandClaude Opus 5 02dc5d51ca chore(spec): 综合清理 + legacy-layer 迁移 spec/plan/data
主要改动:
- 切到 funplay-cocos-mcp v0.5.1 (用户级配置, 项目级 .mcp.json 删除)
- 仓库文档/CLAUDE.md/.gitignore 等清理过时 cocos-mcp-server 引用
- memory 文件同步: cocos-mcp-setup/path/blocker/spriteframe-uuid/prefab-persist 等加 funplay 实测警告
- memory 新建 funplay-cocos-mcp-pending-verification.md (后已被实测覆盖)
- spec/plan/data:
  - docs/superpowers/specs/2026-09-02-legacy-layer-migration-design.md
  - docs/superpowers/plans/2026-09-02-legacy-layer-migration.md
  - docs/superpowers/data/layer-spirit-summary.json
- YouleNexus: profiles.ts / defaults.ts / PlayerInfoView.prefab / scene 改动

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 07:36:54 +08:00

270 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 旧 gameabc 渲染/精灵 API → Cocos 映射(子系统 B·渲染子集)
> 状态:已与用户确认。
> 适用工程:`cocoscreator_projects/YouleNexus`(框架真源)。
> 数据源:`projects/Game_Surface_3/js/gameabc.min.js`(逐行逆向,行号见各节引用)。
> 关联:`2026-08-28-legacy-ui-migration-design.md`(子系统 A)——帧序/切帧/`FrameSet`/图集命名 的**权威定义在 A 的 §0.3、§3.2、§3.4**,本文只引用、不复写。
> 范围:子系统 B 中「渲染与精灵」相关的一小撮 `ifast_*` API + 支撑它们的 `set_self`/`get_self` 属性号 + 图片资源映射。**不含**网络(`ifast_ws`/`ifast_tcp_*`)、输入(`ifast_input`)、工具(`ifast_split` 等)——那些归 B 的后续章节,另立。
---
## 0. 最高原则(两条并列,贯穿全文)
旧引擎是**单 Canvas 立即模式**:每个精灵每帧把它的图 `drawImage` 到共享画布,`ifast_*` 都是「即时画一笔」。Cocos 是**保留式场景图 + 组件**:节点建一次,改值只改属性。
> **原则一 · 原理等价**:把「每帧即时绘制」翻译成「一次性建节点/挂组件 + 按需改属性」;能用一个现成组件的,绝不搬旧引擎的手工绘制。
> **原则二 · 表现等效,原理自由**:**一律采用 Cocos 高效、现代化、行业标准的处理方式和解决方案,不受旧引擎实现原理的束缚。** 只要做到**等效旧引擎的表现**即可——原理、处理方式可以完全不同。旧引擎的手工做法(逐位画数字、手算进度条源宽、手动 `spaceY*i` 排列表项)是「无引擎能力」的替代品,Cocos 有原生组件时**必须用原生组件**,不照搬替代品。
本文 §5 的每一条映射都给出「平替」与「更优(Cocos 行业标准)」两档,落地**默认取更优档**;「平替」仅在原生组件确实不适用时作为兜底。典型落实:进度条用 `ProgressBar`(§5.1)、图片数字用 `Label`+BMFont(§5.4)、列表用 `Layout`+`ScrollView`+`NodePool`(§5.5)。
---
## 1. 渲染模型差异(这是所有映射的根)
| 维度 | 旧引擎(gameabc) | Cocos Creator 3.8 |
|---|---|---|
| 渲染方式 | 立即模式,每帧重画全部精灵 | 保留式,场景图 + 组件,脏标记局部重绘 |
| 精灵本体 | `GameObject`(`gameabc.min.js:1534`),字段全平铺 | `cc.Node` + 组件(`Sprite`/`Label`/`UIOpacity`…) |
| 定位 | `obj.x/y/w/h`(左上原点,Y 向下) | `node.position` + `UITransform.contentSize`(中心原点,Y 向上) |
| 图片 | `obj.image`(HTML `Image`),按 `recid` 从 `ImageFileList` 取 | `Sprite.spriteFrame`(`SpriteFrame`,来自 Auto Atlas) |
| 文字 | `obj.caption`,引擎 `fillText` 画 | `Label.string` |
| 显隐 | `obj.visbale` | `node.active` |
| 帧动画 | `obj.frame`(1 基帧号),画对应源矩形 | `FrameSet.setFrame(n)`(A §3.4) |
| 「精灵上再画一张图」 | `ifast_mydrawbmp` 即时叠加 | **子 `Sprite` 节点**(一个 Sprite 只能一张图) |
坐标系换算(左上→中心)不是本文职责,权威公式在 A §2.2;本文只负责「API 语义 → Cocos 等价物」。
---
## 2. `GameObject` ↔ `cc.Node`(数据结构映射)
`GameObject`(`gameabc.min.js:1534-1719`)是一个大而全的平铺对象;`ifast_getobj(spid)`(`:3086` / `:3888`)返回 `gameabc_Object["sp"+id]`(`id<1` 时是画布根 `gameabc_face.obj`)。**`spid` 就是 `ObjectID`**,与 A 的 `legacy.objectId` 同一体系。
对应关系(只列渲染相关,其余字段见 §3 属性号):
| GameObject 字段 | 含义 | Cocos |
|---|---|---|
| `x` / `y` | 左上坐标 | `node.position`(注意坐标变换,见 A §2.2) |
| `w` / `h` | 显示宽高 | `UITransform.width / height` |
| `image` | 当前 HTML `Image` | `Sprite.spriteFrame` |
| `recid` | 图片资源号 | `SpriteFrame` 的来源(§4) |
| `caption` | 文字 | `Label.string` |
| `frame` | 当前帧号(1 基) | `FrameSet`(A §3.4) |
| `visbale` | 可见 | `node.active` |
| `canclick` | 可点击 | `Button.interactable` |
| `z_Order` / `z_index` | 绘制序 | 节点树 sibling 序(A 用 `siblingIndex`) |
| `scale` / `ap` | 缩放 / 透明度 | `node.scale` / `UIOpacity.opacity`(§3) |
> 命名陷阱(重要):`ImageFileList[recid]` 里的 `w`/`h` 是**网格列数/行数**,`w1`/`h1` 是**每帧像素宽高**(见 `gameabc_getrect2` `:3958-3961`);而 `gameabc_Object["sp"+id]` 里的 `w`/`h` 是**精灵显示宽高**。两个对象字段同名、含义不同,写映射代码时**必须**分清「资源元数据」和「精灵实例」。
---
## 3. `get_self` / `set_self` 属性号对照表
`set_self(id, cpid, val, mode, val2)`(`:3261`)与 `get_self(id, cpid, val, mode, val2)`(`:3154`)是平台层 1773 处调用的通用读写。`cpid` 是数字属性号,`mode` 经 `gamabc_getval(val, val2, mode)`(`:3895`)决定 `val2` 如何作用到当前值:
| mode | 语义 | 说明 |
|---|---|---|
| 0 | 直接赋值(`set`)/ 读原值(`get`,内部转 `-1`) | 最常用;三参调用 `set_self(id,cpid,val)` 默认 mode=0 |
| 1 | `当前 + val2` | 相对位移 |
| 2 | `当前 − val2` | 相对位移 |
| 3 | `⌊当前 × val2⌋` | 相对缩放 |
| 4 | `⌊当前 ÷ val2⌋` | 相对缩放 |
**mode 实证结论(2026-08-30)**:
- 全平台层 1773 处 `set_self` 中,**只有 mode=1(加法)被使用**,约 27 处(21 处 x/y 坐标相对位移 + 6 处 caption 的 mode 无效);`mode=2/3/4`(减/乘/除)**零使用**;`get_self` 亦无 mode 相对运算。
- mode=1 全部是坐标相对位移:`set_self(spid, 18/19, offmovex/offmovey, 1, 0)` → `node.x += offmovex`。**C 侧无需专门相对运算辅助方法**,直接 `node.x += off` 即可。
- ⚠️ **`set_self` 的 `switch` 分两类**:`case 6/7/16/28/58` 是**直接赋值、不走 `gamabc_getval`**,它们的 `mode`/`val2` 参数**无效**。最典型是 `set_self(spid, 7, text, 1, 0)`(caption)——那个 `1` 是历史冗余,与 `set_self(spid, 7, text)` 等价。C 移植时**只对「走 `gamabc_getval` 的 case」关心 mode**,其余一律当直接赋值。
属性号 → Cocos 映射(**✅ 已实证** = 由 `get_self`/`set_self` 的 `switch` 逐 case 确认;**⚠️ 推断** = 由字段默认值/命名推断,落地前须确认;**❓ 待逆向** = 语义未明,不得臆测,C 移植时按需补齐):
| cpid | 旧语义 | 读 | 写 | Cocos | 置信 |
|---|---|---|---|---|---|
| 1 | 图片资源号 `recid` | `gamabc_getval(obj.recid,…)` | 设 `recid` 并触发 `gameabc_load_img2` 换 `image`(`:3311-3320`) | `sprite.spriteFrame = 帧`(recid→帧见 §4) | ✅ |
| 7 | 文字 `caption` | 返回 `caption`,或 `measureText` | `val.toString()` 存入 `caption`(`:3297-3309`) | `label.string` | ✅ |
| 18 | 坐标 x | `obj.x` | `obj.x` | `node.x`(经 A §2.2 换算) | ✅ |
| 19 | 坐标 y | `obj.y` | `obj.y` | `node.y` | ✅ |
| 20 | 宽 w | `obj.w` | `obj.w` | `UITransform.width` | ✅ |
| 21 | 高 h | `obj.h` | `obj.h` | `UITransform.height` | ✅ |
| 28 | 矩形 `{x,y,w,h}` | 返回四值对象 | 一次设四值(`:3279-3286`) | 同时设 `position` + `contentSize` | ✅ |
| 37 | 可见 `visbale` | `obj.visbale` | `obj.visbale` | `node.active` | ✅ |
| 43 | 当前帧 `frame`(1 基) | `obj.frame` | `obj.frame` | `FrameSet.setFrame(n)` | ✅ |
| 44 | 分组 `groupid` | `obj.groupid` | `obj.groupid` | A §2.1 已并入父节点,无需单独映射 | ✅ |
| 58 | 颜色 `color` | `obj.color` | `obj.color` | `Label.color` / `Sprite.color`(按节点类型) | ✅ |
| 33 | 缩放 `scale`(百分比,100=原始) | `obj.scale` | `obj.scale` | `node.scale`(`= val / 100`) | ✅(`gameabc_charge:4963-4967` `ctx.scale(val/100)`) |
| 41 | 可点击 `canclick` | `obj.canclick` | `obj.canclick` | `Button.interactable` | ✅ |
| 16 | 引用 `f`(父/关联对象) | `obj.f` | `val<=0→0`,否则 `ifast_getobj(val)` | 节点引用(父节点 `node.parent` 或业务引用) | ⚠️ 推断(引用语义,具体用途待 C 侧确认) |
| 6 | 图片对象 `image`(底层) | `obj.image` | 直接塞 HTML `Image` | 一般不用;用 cpid=1 换 `spriteFrame` 即可 | ✅(但少用) |
| 35 | 透明度 `ap`(0–255,默认 255) | `obj.ap` | `obj.ap` | `UIOpacity.opacity` | ✅(`gameabc_charge:4949-4956` `globalAlpha=ap/255`;**平台层实际使用**:按钮按下/选中态) |
| 34 | 旋转 `arge`(度,绕中心) | `obj.arge` | `obj.arge` | `node.angle` | ✅(`gameabc_charge:4944-4946` `rotate(arge·π/180)`;引擎已实现,本项目无调用) |
| 36 | `hu` | `obj.hu` | `obj.hu` | **忽略** | ✅(引擎**空字段**:`gameabc_charge:4962` `if(obj.hu!=0){}` 无任何作用) |
| 45 / 46 | 变换中心偏移 `cx` / `cy`(像素,默认 0) | `obj.cx` / `obj.cy` | 同左 | 调整 `anchorPoint`(≈ `(0.5+cx/w, 0.5−cy/h)`,注意 Y 向) | ✅(`gameabc_toclient:4926-4930` `translate(w/2+cx, h/2+cy)`;本项目无调用) |
| 51 | 镜像 `jx`:1=垂直翻转、2=水平翻转 | `obj.jx` | `obj.jx` | `node.scale.y=-1` / `node.scale.x=-1` | ✅(`gameabc_charge:4969-4980` `scale(1,-1)`/`scale(-1,1)`;本项目无调用) |
| 57 | 定时器间隔 `ontime`(毫秒,0=停) | `obj.ontime` | 设 `ontime`,为 0 时清 `click_time` | `component.schedule(cb, ms/1000)` / `unschedule` | ✅(`drawone:1878-1898` 触发 `ontimer`/`ontimer_<objid>`;**平台层实际使用**:登录等待/VIP 刷新/倒计时) |
> **逆向结论(2026-08-30)**:上表属性号已全部逆向。其中 `arge`(34) / `hu`(36) / `cx`·`cy`(45/46) / `jx`(51) 引擎**实现了渲染逻辑但本项目未用**——`gameabc_Object.json` 静态数据无这些字段、平台层与子游戏层也**没有** `set_self`/`get_self` 调用(已 grep 全量确认)。属「引擎预留能力」,C 移植只需在碰到调用时按上表映射即可,无需专门适配。真正仍属「未定」的只有 `default` 分支的 `other[cpid]`(业务自定义属性,按需在 `gameabc.min.js` 对应处补齐后回填)。
---
## 4. 图片资源映射:`recid` / `ImageFileList` → `SpriteFrame`
旧引擎按资源号 `recid` 从 `gameabc_Image.ImageFileList[recid]` 取图(`gameabc_load_img2` `:4597`),图可能是单帧或多帧网格。A 已把多帧图切成散图、单帧图原样保留,并约定:
- 资源 `recid` 的多帧图 `00014.png`(3×4)→ 散图 `00014_01.png … 00014_12.png`(**1 基、补零对齐字典序**,A §3.2)
- 散图按 bucket 落进 `framework/ui/atlas-*`,构建期由 Auto Atlas 打包(A §3.3)
- 每张散图在 Cocos 里就是一张 `SpriteFrame`
**帧号 ↔ 源矩形** 的换算(用于把 `ifast_mydrawbmp` 的 `(bmp_x, bmp_y, bmp_w, bmp_h)` 反推成帧号):
```
帧宽 w1 = ImageFileList[recid].w1;帧高 h1 = .h1
网格列数 cols = .w;网格行数 rows = .h
帧号 frame(1 基)→ 源矩形:
列 = (frame − 1) % cols,行 = ⌊(frame − 1) / cols⌋
源矩形 = (列 × w1, 行 × h1, w1, h1)
源矩形 → 帧号(逆运算,供 mydrawbmp 反推):
列 = bmp_x / w1,行 = bmp_y / h1
frame = 行 × cols + 列 + 1
```
权威公式在 A §0.3(已实证:通知=10、设置=11、战绩=4、背包=5、仓库=6、任务=3 六项全命中)。本文复述仅为自洽,**改动以 A 为准**。
---
## 5. 核心 API 逐条映射
### 5.1 `ifast_mydrawbmp(spid, recid, sp_x, sp_y, sp_w, sp_h, bmp_x, bmp_y, bmp_w, bmp_h)` — 在精灵上叠加绘制图片
**旧语义**(`:2627`):在精灵 `spid` 上、其坐标系内偏移 `(sp_x, sp_y)` 处、以 `(sp_w, sp_h)` 尺寸,叠加绘制图 `recid` 的子矩形 `(bmp_x, bmp_y, bmp_w, bmp_h)`。**不动 spid 自己的图**。`bmp_w <= 0` 时退化为 `ifast_drawtext`(画文字,§5.2)。
关键证据:`gameabc_load_img2(recid)` 加载的是 **recid** 的图,`drawImage(img, bmp_x, …, obj.x + sp_x, obj.y + sp_y, sp_w, sp_h)`(`:2639`)——坐标加的是 spid 偏移,spid 的 `image` 全程未改。
**Cocos 对应**:保留式场景图里一个 `Sprite` 只能显示一个 `spriteFrame`,所以「精灵之上再叠一张图」不能合并到同一 Sprite,**必须挂一个子 `Sprite` 节点**:
```
spid (Sprite,自己的 spriteFrame 不变)
└─ childSprite spriteFrame = 子矩形对应的 SpriteFrame(§4 反推帧号)
position = (sp_x, sp_y)
size = (sp_w, sp_h)
```
```ts
// 伪代码(Cocos 3.8)
function myDrawBmp(spid: Node, recid: number,
spX: number, spY: number, spW: number, spH: number,
bmpX: number, bmpY: number, bmpW: number, bmpH: number) {
const frame = rectToFrame(recid, bmpX, bmpY, bmpW, bmpH); // §4 逆运算
const child = new Node('drawbmp');
child.setPosition(spX, spY);
const ui = child.addComponent(UITransform);
ui.setContentSize(spW, spH);
const sprite = child.addComponent(Sprite);
sprite.spriteFrame = loadFrame(recid, frame); // 散图 SpriteFrame
spid.addChild(child);
}
```
**两种典型动态用法 → 更优方案**:
| 旧用法 | 旧写法 | Cocos 更优解 |
|---|---|---|
| **进度条**(子矩形宽随进度变,`11_GameUI.js:2912`) | `ifast_mydrawbmp(spid,96,…, bmp_w = LoadCount*416/100, …)` | `cc.ProgressBar` + `Sprite.FillType.FILLED`/`SLICED`,设 `progress∈[0,1]`,**不手算源宽** |
| **动态图标/帧片段**(`11_GameUI.js:2896` 画 47 的两个子矩形) | 多次 `ifast_mydrawbmp` 叠加 | 多个子 `Sprite`(语义等价),或若就是换帧则用 `FrameSet.setFrame`(A §3.4) |
> 判断「是叠加还是换帧」:看目标精灵 `spid` 自己有没有图。若 `spid` 自身无图、`mydrawbmp` 是唯一内容,等价于「给 spid 换帧」,用 `FrameSet`/`spriteFrame`;若 `spid` 有图、`mydrawbmp` 是叠加层,用子 `Sprite`。
### 5.2 `ifast_mydrawtext(spid, recid, sp_x, sp_y, sp_w, sp_h)` — 在精灵上绘制文字
**旧语义**(`:2643`):在精灵 `spid` 上画 `gameabc_GameTxt.GameTxtList[recid]` 的 `Text`(颜色 `Color`,字号 `sp_h`,`textBaseline=top`)。`spid<=0` 时画在画布 `(sp_x, sp_y)`。
**Cocos 对应**:`cc.Label`。若文字要「贴」在某个精灵上,就挂一个子 `Label` 节点(与 §5.1 同理);若文字就是精灵自己的 caption,直接 `set_self(spid, 7, text)` → `label.string`(§3 cpid=7)。
> 注意:`recid` 在这里是 **`GameTxtList` 的索引(文字 id)**,不是图片资源号——与 `ifast_mydrawbmp` 的 `recid` 是两套 id。`set_rec`/`get_rec`(`:3089`/`:3092`)读写的就是这份文字列表(§5.6)。
### 5.3 `ifast_mydrawsprite(spid, spidsourse, sp_x, sp_y)` — 在精灵上绘制另一个精灵
**旧语义**(`:2596`):把精灵 `spidsourse` 画到 `spid` 的偏移 `(sp_x, sp_y)` 处(临时改 `spidsourse` 坐标、`draw`、恢复)。
**Cocos 对应**:直接把 `spidsourse` 作为 `spid` 的**子节点**,设 `position = (sp_x, sp_y)`。保留式场景图下「一个节点画在另一个节点内」就是**父子关系**,无需每次临时改坐标再恢复。
### 5.4 数字图片精灵
旧引擎**没有专门的数字 API**,数字只有两条路,Cocos 各对应:
| 旧路径 | 旧实现 | Cocos 对应 |
|---|---|---|
| **文字数字** | `set_self(spid, 7, "积分:" + score)`(caption)→ 引擎 `fillText` | `Label.string`(系统字体 / TTF)——**平台层绝大多数数字走这条,1:1 平替** |
| **图片数字** | `ifast_mydrawbmp` 逐位切数字图集的帧、手算 `bmp_x = 位×字宽` | **`Label` + BMFont(`.fnt`)**——原生数字图片精灵:一个节点、自动逐位取字模、自动对齐/字距/缩放,**彻底取代手动画子图 + 手算偏移** |
**BMFont 落地的注意点**:旧数字图集是「多帧网格 PNG」(`w × h = frame_all`,A §0.3),**不是 BMFont 格式**。要用 BMFont,须把 0–9(及可能的小数点/`+`/`-`/`:`)字模重打包成 `.fnt`+`.png`。若美术暂不重打包,可降级用「逐位 `Sprite` + 数字图集帧」手动排——**这是把旧引擎手工做法搬过来,不推荐**,只作为过渡。
### 5.5 `ifast_addtospritefromspritecopy(fspid, copyspid, x, y, tag)` / `ifast_dllpritefromspritecopy(fspid, tag)`
**旧语义**(`:3556`,核心 `GameObject.addtospritefromspritecopy` `:1624`):把模板精灵 `copyspid` **深拷贝**(`copyme` 复制全部字段、共享 `image`/`uidata`)出一份副本,挂到父精灵 `fspid` 下、放到 `(x, y)`、打 `tag`、入 `addlist`,返回 id 串 `fspid+'add'+tag`。配套 `ifast_dllpritefromspritecopy`(`:3564`)按 `tag` 删除副本。
典型用法是**列表渲染**:一个模板 + 循环 N 份,`y = 首项 + spaceY*i`(房间列表 `11_GameUI.js:3707`、任务列表 `:4679`、VIP 榜 `:9629`)。
**Cocos 对应**(三层,从平替到更优):
| 层级 | Cocos 做法 | 说明 |
|---|---|---|
| 平替 | `cc.instantiate(prefab)` + `node.setParent(parent)` + `node.setPosition(x, y)`;删除 `node.destroy()` / `node.removeFromParent()` | 模板做成 **prefab**,等价深拷贝 |
| 更优 | `cc.Layout`(垂直/水平/网格) | 自动排布,**干掉 `spaceY*i` 手算** |
| 更优 | `cc.ScrollView` + `NodePool`(复用,不反复 new/destroy) | 长列表滚动 + 对象池回收 |
```ts
// 平替(等价深拷贝 + 定位)
const item = instantiate(templatePrefab);
item.setParent(listParent);
item.setPosition(x, y);
// 更优:对象池复用(长列表)
const item = pool.get() ?? instantiate(templatePrefab);
item.setParent(listParent);
item.setPosition(x, y); // 交给 Layout 则连这行都省
// 用完:pool.put(item)
```
`ifast_dllpritefromspritecopy(fspid, tag)` → `node.destroy()`;要复用 → `NodePool.put(node)` / `NodePool.get()`。
> 与 A 的衔接:A §0.2 指出「991 个对象里一部分是模板、一部分是容器」,且 A **有意不识别模板/容器**(A §1.5)。识别「哪个 ObjectID 是 `ConstVal.myRoomList.bgSp` 这类模板」是 **C(平台逻辑移植)** 的职责;B 只负责给出「克隆模板 → instantiate/Layout/Pool」这条映射规则。
### 5.6 `set_rec(stringid, stringv)` / `get_rec(stringid)` — 读写文字表
**旧语义**(`:3089`/`:3092`):读写 `gameabc_GameTxt.GameTxtList[stringid].Text`——一份**全局文字串表**(资源号是文字 id,非图片)。
**Cocos 对应**:`get_rec(id)` 读取的文本最终进某个 `Label`。文本常量本身在 C 里应由 `theme.fonts` / 文案表提供(见 `2026-08-27-ui-asset-and-skin-design.md` §4),不散落各处。**不要**为 `GameTxtList` 单独建一份全局可变文字表——它是旧引擎「无富文本能力」的产物,Cocos 用 `Label` 直接承载。
---
## 6. 落地约定(C 移植时遵守)
1. **`spid` = `ObjectID` = `legacy.objectId`**(A §1.3),三者同源;C 侧通过 A 的 `manifest.json`(ObjectID → 节点路径)由 `spid` 定位节点。
2. **换帧统一走 `FrameSet`**(A §3.4):`set_self(spid, 43, n)` → `frameSet.setFrame(n)`,帧号同为 1 基,无需心算减一。
3. **坐标/尺寸**一律经 A §2.2 的换算后再赋值;本规范不重复坐标公式。
4. **叠加绘制(§5.1/5.2)永远用子节点**,不得试图把多张图塞进一个 `Sprite`。
5. **列表渲染(§5.5)默认上 `Layout` + `NodePool`**;只有一次性、数量少、非滚动的场景才用裸 `instantiate`。
6. 遇到 ❓ 属性号或本表未列的 API,**回到 `gameabc.min.js` 对应 `switch`/函数补齐**,再回填本表——**禁止臆测**(第二准则)。
---
## 7. 红线与待验证
**红线**
- 不手写 `.prefab`/`.scene`/`.meta`(CLAUDE.md)——本规范的落地产物仍走 MCP / 编辑器。
- 不臆测属性号语义;未定项以 ❓ 标记,C 移植时按需逆向。
- 帧序/切帧/`FrameSet`/图集命名 只以 A §0.3/§3 为权威,本文不复写。
**已解决(本次逆向坐实,2026-08-30)**
- 属性号 34/35/36/45/46/51/57 语义已逆向完成并回填 §3(由 `gameabc_charge` / `gameabc_toclient` / `drawone` 源码坐实)。`ap`=透明度(0–255 → `UIOpacity.opacity`)**实证成立**,不再是推断。
- `mode` 相对运算占比已统计:全平台层仅 `mode=1`(加法)约 27 处(其中 6 处 caption 的 mode 无效),`mode=2/3/4` 零使用 → **C 侧无需相对运算辅助方法**(详见 §3)。
**待验证(落地前)**
| # | 待验证 | 方法 |
|---|---|---|
| 1 | 图片数字是否真用 BMFont(需美术重打包字模),还是暂用逐位 Sprite 过渡 | 看子游戏层数字素材现状,与美术确认 |
| 2 | `ifast_mydrawbmp` 的「叠加 vs 换帧」判定在 C 侧是否要更细(有些 spid 自身无图但语义上是「画内容」而非「换帧」) | C 移植首屏时抽样核对 |