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

21 KiB
Raw Permalink Blame History

旧 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)
// 伪代码(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) 长列表滚动 + 对象池回收
// 平替(等价深拷贝 + 定位)
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 移植首屏时抽样核对