主要改动: - 切到 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>
21 KiB
旧 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 移植时遵守)
spid=ObjectID=legacy.objectId(A §1.3),三者同源;C 侧通过 A 的manifest.json(ObjectID → 节点路径)由spid定位节点。- 换帧统一走
FrameSet(A §3.4):set_self(spid, 43, n)→frameSet.setFrame(n),帧号同为 1 基,无需心算减一。 - 坐标/尺寸一律经 A §2.2 的换算后再赋值;本规范不重复坐标公式。
- 叠加绘制(§5.1/5.2)永远用子节点,不得试图把多张图塞进一个
Sprite。 - 列表渲染(§5.5)默认上
Layout+NodePool;只有一次性、数量少、非滚动的场景才用裸instantiate。 - 遇到 ❓ 属性号或本表未列的 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 移植首屏时抽样核对 |