Files
youle_cocos/docs/superpowers/specs/原工程数据模型.md
T
joywayerandClaude Opus 5 b6b11d7ca1 docs(spec): 原工程数据模型补全 gameabc.min.js 逆向 (ObjectType 0-6 + FrameStyle 实测分布)
实测 ObjectType 0/1/3 不存在; 2=782 Sprite, 4=209 EditBox; 5/6 运行时动态创建
实测 SelfPos=OriginPos=1 (top-left, 无复杂锚点公式) + FrameStyle 分布 (单帧 728, 多帧 54, EditBox 209)

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

16 KiB
Raw Blame History

原工程数据模型 (Game_Surface_3)

Status: Draft v1 (2026-09-03) Purpose: 为后续 UI 迁移提供 Game_Surface_3 数据契约的权威参考 基于: projects/Game_Surface_3/output/*.json + save/*.xml + js/gameabc.min.js(解析输出)

总览

Game_Surface_3 是 Cocos-JS (HTML5) 单页游戏平台,使用以下数据文件:

文件 大小 内容
output/gameabc_Project.json 778 B 全局配置(屏幕尺寸、事件名、fps 等)
output/gameabc_GroupList.json 8.3 KB Group→Object 列表(55 groups)
output/gameabc_Layer.json 12 KB Layer→Object 列表(55 layers)
output/gameabc_Object.json 905 KB 核心:991 个 object 的完整属性+事件+选项(3293 个槽位)
output/gameabc_Image.json 67 KB 439 张 sprite PNG 元数据(id → bmp + 多帧)
output/gameabc_Voice.json 252 B 音频文件映射(id → mp3)
output/gameabc_GameTxt.json 20 KB 多语言文案(GameTxtList)
save/*.xml 59 文件 Layer XML 定义(每层一个文件)
js/gameabc.min.js 185 KB 引擎核心(已 minify,难逆向)

Layer 8 (Notice_Layer) 关键交叉验证:

  • gameabc_Layer.json[8].ObjectList = [3122, 145, 3121, 3017, 5, 235, 604, 3057, 15] (9 个 object IDs)
  • save/Layer00008.xml 的 9 个 <Spirit ID="..."> IDs:完全匹配
  • gameabc_Object.json 中这 9 个 ID 的 GroupID = 3(145)或 GroupID = 95(其他 8 个)也完全匹配 XML 的 BelongGroupID 字段

gameabc_Project.json schema

type Project = {
  Property: {
    ProjectName: string;      // e.g. "Game_Surface_3"
    ScreenWidth: number;       // 1280
    ScreenHeight: number;      // 720
    GameSceneWidth: number;    // 1699 (logical scene width)
    GameSceneHeight: number;   // 1800
    ScreenFitMode: number;     // 1 (适配模式)
    TcpIP: string;
    TcpPort: string;
    Http: string;
    title: string;
  };
  Event: {
    gamestart, gamebegindraw, gameenddraw,
    mousedown, mousedown_nomove, mouseup, mousemove,
    gamemydraw, gamemydrawbegin, chongzhi,
    tcpconnected, tcpdisconnected, tcpmessage, tcperror, httpmessage,
    ani_doend, box_doend, onresize, ontimer, onloadurl: number;
  };
  Option: {
    fps: number;  // 30
    tag, tag1, tag2, tag3: number;
    showmodel: number;
  };
};

gameabc_Layer.json schema

type Layer = {
  Property: {
    LayerID: number;        // 1-620 (不连续)
    LayerName: string;      // e.g. "Login_Layer", "Notice_Layer"
  };
  ObjectList: number[];    // 该 layer 包含的 object ID 列表(按 IndexOfLayer 排序)
};

type LayerList = (Layer | {})[];  // 长度 621,空对象占位

示例:

{"Property":{"LayerID":8,"LayerName":"Notice_Layer"},"ObjectList":[3122,145,3121,3017,5,235,604,3057,15]}

gameabc_GroupList.json schema

type Group = {
  GroupID: number;        // 1, 3, 95, ...(55 个有效 group)
  ObjectList: number[];   // 该 group 包含的 object ID 列表
};

type GroupList = (Group | {})[];  // 长度未读(稀疏)

关键映射关系:Object.GroupID ↔ Object.ObjectList in Layer.ObjectList

gameabc_Object.json schema(核心)

type GameObject = {
  Property: { /* 共 41 字段,见下方 */ };
  Event?: { /* 事件→回调 object ID 映射 */ };
  Option?: { /* 选项 11 字段 */ };
};

type ObjectList = (GameObject | {})[];  // 长度 3293,索引 = object ID - 1

Property 字段(41 字段)

标识 + 位置(10 字段)

字段 类型 含义 XML 对应
ObjectID int 唯一 ID(也是数组索引+1) Spirit ID 属性
ObjectType int 组件类型(见下方映射表) Spirit SpiritType
ObjectName str 中文/英文节点名 Spirit Name
Left, Top int 绝对坐标 X, Y(左上角锚点) Spirit X, Y
Width, Height int 尺寸 W, H Spirit Width, Height
BelongLayerID int 所属 Layer ID Spirit BelongLayerID
IndexOfLayer int 同 layer 内排序 Spirit IndexOfLayer
GroupID int 所属 Group(0=根 layer) Spirit BelongGroupID
offX, offY int 同 Left/Top(冗余字段) —

ObjectType 映射(实测 - 完整版)

实测 gameabc_Object.json 991 个非空对象 + gameabc.min.js 逆向:

ObjectType 含义 ObjectList 出现次数 示例 Cocos 对应
0 (不存在) 0 — —
1 (不存在) 0 — —
2 Image/Sprite 782/991 id 1 (友乐背景, 1280×720 全屏), id 5 (通知, 150×70) cc.Sprite (单帧) 或 cc.Sprite _type=1 (SLICED 9-slice)
3 (不存在静态) 0 — —
4 Text/EditBox 209/991 id 65 (战绩文字内容), id 145 (公告内容) cc.Label 或 cc.EditBox (含 Text + FontSize)
5 (运行时动态) 0 (静态) gameabc.min.js 中 obj.uidata 子对象 (按钮 controller, SizeStyle 控件) 不需要静态迁移 (动态生成)
6 (运行时动态) 0 (静态) gameabc.min.js 中 this.ObjectType = 6 (SizeStyle 控件初始化) 不需要静态迁移

🟢 关键发现:ObjectList 静态数据只有 ObjectType 2 (Sprite) 和 4 (Text/EditBox)。Type 5/6 是 gameabc 运行时动态创建的对象(如 button controller、SizeStyle),不需要持久化迁移。

🟢 SelfPos=OriginPos=1 对所有 991 object 都成立 → 所有 object 都用 top-left 锚点(X,Y 即左上角绝对坐标,无需复杂 OriginPos 公式)。

9-slice border 字段(4 字段)

字段 类型 含义
L9, T9, R9, B9 int 左/上/右/下 9-slice border 像素值

🟢 Cocos 对应:cc.SpriteFrame.capInsets = [L9, T9, R9, B9](capInsets 全 0 = SIMPLE,否则 SLICED)

Frame 字段(实测 - 完整版)

字段 类型 全工程分布 含义
FrameStyle int None: 209 (EditBox), 0: 728 (单帧 Sprite), 1: 54 (多帧 Sprite) 0=单帧, 1=多帧动画
FrameIndex int 54 个 Sprite 用 当前显示的帧索引 (0 到 FrameStyle 总帧数-1)
TextFrames str 多帧配置字符串 (待具体逆向) (目前空字符串)

🟢 实测 FrameStyle 分布: 单帧 Sprite 占主导 (728/991=73%), 多帧 Sprite 占 54/991=5%, EditBox/Text 无帧 (21%)。多帧 sprite 需用 atlas 多张散图 (gameabc_Image.bmp + frame_all 字段 → 拆成多 PNG,参考 playbook §关键迁移规则)。

字体字段(EditBox/Text 字段,8 字段)

字段 类型 含义
FontSize int 字体大小
FontBold int 0/1
FontColorR/G/B int RGB 颜色分量
FontColor str "#FFFFFF" 形式
BackColorR/G/B/A int 背景色(带 alpha)
BackColor str 背景色 16进制
GameTxtStyle int 多语言文案索引(对应 gameabc_GameTxt.json)
LineSpace int 行距
Text str 文本内容

关系字段(4 字段)

字段 类型 含义
Parent int 父 object ID(0=无父)
OriginID int 锚点参考 object ID(0=绝对坐标)
OriginPos int 锚点位置:1=top-left, 9=center
SelfPos int 自身位置:1=top-left, 9=center

资源引用字段(2 字段)

字段 类型 含义 Cocos 对应
ImageFileID int sprite 资源 ID(→ gameabc_Image.json) _spriteFrame.__uuid__
VoiceFileID int 音效 ID(→ gameabc_Voice.json) (audio component)

杂项(4 字段)

字段 类型 含义
Data str 自定义数据字段
TimerInterval int 定时器间隔(0=无)
L9/T9/R9/B9 (见上) (见上)

Event schema

type Event = {
  mousedown?: number;       // 回调 object ID(值=0 表示无回调)
  mouseup?: number;
  mousemove?: number;
  ontimer?: number;
  // 注: Project.json 列出 20+ 事件类型, Object 的 Event 只观察到这 4 个常用
};

🟡 Project.json Event 列出 20+ 种 (gamestart, gamebegindraw, ...),但 Object.json 里只观察到 mousedown/mouseup/mousemove/ontimer 4 种。其他事件可能由 Project 级或 Group 级处理。

Option schema(11 字段)

type Option = {
  tag, tag1, tag2, tag3: number;  // 用户自定义标签
  vx, vy, vw, vh: number;        // velocity (动画/物理)
  canclick: number;              // 0/1, 是否可点击
  visbale: number;               // 0/1, 是否可见(注意:原工程拼写错误 "visbale" 而非 "visible"!)
};

gameabc_Image.json schema

type ImageRecord = {
  id: number;            // = ImgResID(与 Object.ImageFileID 对应)
  w_all: number;         // 完整 sprite 宽(含多帧拼接)
  h_all: number;         // 完整 sprite 高
  w: number;              // 帧宽
  h: number;              // 帧高
  frame_all: number;     // 总帧数
  bmp: string;            // "00001.png" 形式
  w1: number;             // 第 1 帧宽(多帧时 w 可能 != w1)
  h1: number;             // 第 1 帧高
};

type ImageFileList = (ImageRecord | {})[];  // 长度 440,索引 = id - 1

多帧 sprite 迁移规则(已写入 playbook §关键迁移规则):

  • id=N 单帧:bmp = "0000N.png" 直接用
  • id=N 多帧(frame_all > 1):bmp = "0000N.png" 含多帧 → Cocos 用 0000N_01.png ~ 0000N_NN.png 多张散图

gameabc_Voice.json schema

type VoiceRecord = { id: number; file: string; };  // file = "0000N.mp3"
type VoiceFileList = (VoiceRecord | {})[];

gameabc_GameTxt.json schema

type GameTxtList = (string | {})[];  // 长度 ≈ 274,多语言文案槽位

用法:Object.GameTxtStyle 是 GameTxtList 的索引 → 取字符串作为显示文本(支持多语言)。

Layer XML ↔ gameabc_Layer.json ↔ gameabc_Object.json 关系

Layer00008.xml                    gameabc_Layer.json           gameabc_Object.json
─────────────────────             ─────────────────────       ──────────────────────
<Spirit ID="145">                  LayerList[8]                ObjectList[144]
  <Property                          ObjectList: [               "Property":
    Name="公告内容"                   3122, 145, 3121,           ObjectID: 145,
    ImgResID="..."/>                 3017, 5, 235,             ObjectType: 4,
    X="81" Y="787"                   604, 3057, 15]            ObjectName: "公告内容",
    Width="60" Height="20"                                       Left: 81, Top: 787,
    BelongGroupID="3"/>                                            Width: 60, Height: 20,
  ...                                                                    GroupID: 3,
                                                                         Text: "公告内容",
                                                                         ...
                                                                       }
                                                                  }
gameabc_Object.json[144] 即 Object 145(数组 index = ObjectID - 1)

Layer 8 → ObjectList 9 IDs → 9 个 ObjectList 索引 完全 1:1 映射。

关键数据关系图

Project (gameabc_Project.json)
  └─ ProjectName + ScreenWidth/Height + 全局事件 + fps
       │
       ▼
Layer (gameabc_Layer.json)
  └─ LayerID + LayerName + ObjectList[]
       │
       ▼
Object (gameabc_Object.json) ← Layer ↔ Object N:1
  ├─ Property (41 字段)     ObjectID ↔ array index
  │   ├─ ObjectType (0-5)      ├─ 0=? 1=? 2=Image/Sprite 3=? 4=EditBox 5=?
  │   ├─ ObjectName (中文)     ├─ GroupID → 包含此 object 的 group ID
  │   ├─ Left/Top (绝对坐标)   │
  │   ├─ Width/Height          ├─ Parent (父 object, 0=无)
  │   ├─ GroupID (0=根)         └─ OriginID (锚点参考, 0=绝对坐标)
  │   ├─ ImageFileID          │
  │   │     │                  Group (gameabc_GroupList.json)
  │   │     ▼                     └─ GroupID + ObjectList[] (本组所有 object)
  │   └─ FrameIndex + FrameStyle
  │   │
  │   ├─ Text + FontSize (if EditBox/Text)
  │   ├─ L9/T9/R9/B9 (9-slice border)
  │   └─ offX/offY
  │
  ├─ Event (4 字段)
  │   └─ mousedown/mouseup/mousemove/ontimer → callback ObjectID
  │
  └─ Option (11 字段)
      ├─ canclick / visbale (拼写错误!)
      └─ tag1-3 / vx,vy,vw,vh (动画/物理)

Image (gameabc_Image.json)
  └─ id (=ImageFileID) + w_all/h_all/w/h/frame_all/bmp + w1/h1
       │
       ▼
  SpriteFrame (Cocos)
       ├─ name = bmp (00001.png)  
       ├─ rect (sub-region in atlas)
       ├─ capInsets = [L9, T9, R9, B9]
       └─ texture = atlas image asset UUID

Voice (gameabc_Voice.json) ──→ VoiceFileID → .mp3

GameTxt (gameabc_GameTxt.json) ──→ Object.GameTxtStyle → 多语言文案

与 Cocos prefab 的字段映射(迁移目标)

原工程字段 (gameabc_Object.Property) Cocos prefab 字段 转换公式/规则
ObjectID + ObjectName _id (数字) + _name 字符串直接
ObjectType cc.Sprite / cc.Label / cc.ProgressBar / cc.EditBox 按映射表(待 gameabc.min.js 逆向确认完整 0-5)
Left, Top _lpos.x, _lpos.y x = Left + Width/2 - 640, y = 360 - (Top + Height/2)
Width, Height _contentSize.width, .height 直接
GroupID (不映射为单独字段 → 父节点 group-X) group-X 容器 nodes + children
BelongLayerID cc.Node._parent 跨 layer 引用
ImageFileID + FrameIndex cc.SpriteFrame.uuid atlas-lookup (multi-frame rule)
Text cc.Label._string (if EditBox → cc.EditBox.string, else cc.Label.string)
FontSize, FontColor, BackColor, FontBold cc.Label._fontSize, ._color 直接
L9, T9, R9, B9 cc.SpriteFrame.capInsets 直接数组
OriginID + OriginPos + SelfPos cc.Node anchor logic 9=中心(待逆向公式)
offX/offY (冗余字段,验证用) —
FrameStyle + FrameIndex + TextFrames cc.ProgressBar._mode, ._progress 9-slice 多帧公式
Event: mousedown/mouseup Button._clickEvents (cocos Button click events 待 wire)
Option: canclick 决定是否加 cc.Button —
Option: tag1-3 (用户自定义数据) (忽略)
Option: vx/vy/vw/vh (动画/物理,Cocos 端 cc.Animation / Box2D 等) (deferred to game logic plan)
VoiceFileID cc.AudioSource 资源引用 (deferred)
GameTxtStyle cc.Label._string i18n lookup (deferred to i18n sub-task)

已知未映射字段(deferred sub-tasks)

字段 含义 状态
Option.vx/vy/vw/vh 动画/物理速度 deferred to game logic migration
VoiceFileID 音频 deferred to audio migration
GameTxtStyle i18n deferred to i18n migration
Event.gamestart 等 16+ Project 级事件 全局事件 deferred to game lifecycle
OriginID + OriginPos + SelfPos 锚点计算 gameabc.min.js 逆向待完成

后续步骤

按用户原始指令(先停下 UI 迁移,深入阅读原工程):

  • ✅ 7 个 JSON schema + XML 关系 + Object schema 完成
  • ✅ ObjectType 映射实测(修正之前误判:ObjectType=4 是 EditBox 不是 Label)
  • ✅ Layer 8 与 gameabc_Layer.json ObjectList 1:1 验证通过

TODO(等用户指示):

  1. 继续看 gameabc.min.js 完整 ObjectType 0-5 映射
  2. 读 FrameStyle/FrameIndex/TextFrames 在多帧 sprite 下的具体公式
  3. 读 OriginID + OriginPos + SelfPos 在 9-slice 布局的具体公式
  4. 输出原工程数据模型完整 spec 文档(已在本文件中)
  5. 给用户报告 + 等指示继续 UI 迁移