本 Issue 定义导出包的产物契约,并实现 Cocos Creator 适配。
背景
#37 §3.9 规定了导出包的内容(GIF 预览、逐帧透明 PNG、sprite sheet、JSON 元数据、导入说明)与行为(仅全部帧通过的动作可进包,不产出残缺包)。决策三定 Cocos 为首发适配目标,§5.2 第 6 条给出验收口径:导出包在 Cocos Creator 与微信小游戏中首次播放成功,无需重切帧、批量改名、逐帧调锚点。
这是清单,不是契约。目录如何组织、图集如何切分、JSON 有哪些字段、字段的坐标系与单位,目前无一处规定。仓库现状与之一致:docs/module-split.md 将 export 列在待实现模块表内,frontend/src/features/export/index.ts 为六行空壳,backend/packages/ 下无实现。
MS1 的 #22 定义过一版规格:产物为 sprite_sheet.png、walk.gif、meta.json 与 README,meta.json 含 frames[]、fps、anchor、foot_y、canvas,并要求删掉任一帧后导出报错而非产出残缺包。它依附的骨架 PR #5 未合并即关闭,gac/ 从未进入主干;#22 已于 08-04 以 not planned 关闭。现有后端为 windup_app 与 ai_engine,这份规格没有迁移过来。本 Issue 承接它。
08-03 导师会将项目级一键导出全部角色资产列为服务端待办,该接口的返回物即本契约定义的包,契约需先于接口。
要定义什么
导出物分两层。
通用层是引擎无关的事实:一个动作有多少帧、帧率、是否循环、画布尺寸、锚点与脚底线的位置。它描述资产本身,与消费方无关,落在 meta.json 与一套固定目录结构里。
引擎适配层由若干 target 组成,每个 target 把通用层翻译成一家引擎原生认识的资源。本期只实现 cocos target,做到用户把导出目录拖进 Creator 工程的 assets/ 后即可播放。
分层的收益在加引擎时兑现:新增 Unity 即新增一个 target,通用层与 cocos target 不动,对应 #37 §3.9 的「导出器按引擎以适配器组织」。其次,即便全部 target 都不合用,用户拿 frames/ 与 meta.json 自行导入仍然可行,资产不锁定于任何一家(决策三)。
代价是多一层间接。只做 Cocos 一家的话,直接输出 Creator 格式更省事;但策划案已写明 Unity 与 Godot 可手动导入,这层迟早要有,现在划出边界的成本低于将来从 cocos 实现中反向拆分。
为什么不配套做 Cocos 编辑器插件
备选方案是提供 Cocos Creator 扩展,由插件把导出包导入工程。本 Issue 选择让导出物本身即 Creator 可识别的资源结构,用户拖入即用。
若产物可被 Creator 直接识别,插件替用户省下的只是一次拖拽;而扩展是一份独立代码资产,有自己的 API、编辑器进程模型与版本兼容面,需长期维护,与其收益不相称。
该判断依赖一个尚未验证的事实:Creator 对外部生成的动画剪辑与图集元数据识别到什么程度。故第一步为实测。若实测表明必须由插件写入 asset-db 才能得到可播放资源,插件方案重新成立,届时另开 Issue,通用层不受影响。
第一步:先实测,再定字段
手工构造一份符合下文草案结构的样例包,拖入真实的 Cocos Creator 3.x 工程,记录:
图集与切分数据以何种形式被识别,是否需在编辑器内手工设置。
.anim 能否由外部生成后直接使用,.meta 中的 uuid 是否需自行生成。
从拖入到播放的完整手工步骤与耗时。此项即 Windup 产品策划案:面向国产小游戏的 2D 角色素材生成资产工作台 #37 §5.1 假设 5 的验证方式,数据可直接回填。
两个以上 3.x 小版本之间的格式差异。
结论回写本 Issue 字段表,字段定稿后进入实现。素材沿用 #87 的 samurai:base.png 与 idle、walk、jump 各 36 帧,256×256 RGBA,全程真实帧。
一、通用层:产物契约
目录结构草案:
<character>/
meta.json
README.md 导入说明
preview/walk.gif
frames/walk/walk_000.png … walk_035.png
atlas/walk.png
meta.json 字段草案:
字段
类型
含义
schema_version
string
契约版本。字段变动后,旧包据此判断兼容性
character.id / character.name
string
角色标识与显示名
canvas.w / canvas.h
int
画布像素尺寸,同一角色的全部动作一致
actions[].name
string
动作名,同时是帧文件的命名前缀
actions[].fps
int
播放帧率
actions[].loop
bool
是否循环
actions[].frames[]
array
{index, file},index 连续无缺口
actions[].anchor
{x, y}
归一化 0–1,坐标系见下
actions[].foot_y
int
脚底线,画布顶为 0 的像素值
actions[].atlas
object
{file, cols, rows, cell{w,h}}
source
object
生成记录引用,对应 #37 §5.2 第 7 条的过程可追溯
其余约定:帧文件命名为 <action>_<三位序号>.png;PNG 带 alpha 通道;任一动作缺帧则导出失败,不产出残缺包。
坐标系
契约统一使用图像坐标系:原点在画布左上角,y 向下,anchor 归一化到 0–1。
Creator 的 anchorPoint 也是归一化的,但 (0, 0) 在左下、(1, 1) 在右上,默认 (0.5, 0.5)。同一个脚底中心,契约里写作 (0.5, 0.95),Creator 里是 (0.5, 0.05),相差近一整张画布的高度。两边各按各的约定读同一个数,角色会悬空或陷入地面。转换由 target 负责。
如此规定有两条依据。其一,同一份 meta.json 中 foot_y 使用图像坐标,两个字段共用一套坐标系可减少出错。
其二,各引擎的约定互不相同,同一引擎内部亦不统一。Cocos 如上。Unity 的 Sprite.pivot 以像素计,而导入期的 SpriteMetaData.pivot 归一化,且官方文档写作「矩形左上为 (0,0)、右下为 (1,1)」,与 Cocos 上下颠倒。Godot 的 Sprite2D 无归一化 pivot,用 centered 开关配合以像素计的 offset,y 轴向下。通用层采用其中任何一家,另两家的 target 仍要换算,换算总量不变,只是把该家的语义写进了通用层字段;此后该引擎调整约定或新增引擎与之冲突,改动落在通用层,波及全部 target 与已发出的包。故通用层自定义坐标系并写明,换算由 target 承担。
二、Cocos target
在通用层之上追加 Creator 可识别的资源文件,草案为图集与其切分数据、每个动作一份 .anim 及对应 .meta,确切清单由实测确定。
使用路径为下载导出目录、拖入工程 assets/、在场景中挂载播放。README 写明固定的 Creator 版本与工程模板,对应 #37 §5.1 假设 5 的失败退路。
本期范围
本期只实现 cocos 一个 target。通用层与 target 的边界即扩展点,Unity 与 Godot 的原生资源将来在通用层之上追加,不改通用层字段,两者不在本期。微信小游戏的 4MB 主包与分包组织属部署层,不改变产物本身,不在本期(#37 §3.9)。
验收标准
取一份后端实际产出的导出包,拖入固定版本的 Creator 工程,不做重切帧、批量改名、逐帧调锚点即可播放 walk 循环(Windup 产品策划案:面向国产小游戏的 2D 角色素材生成资产工作台 #37 §5.2 第 6 条)。
meta.json 通过随 PR 附带的 JSON Schema 校验;字段缺失或类型不符时导出失败,并报出具体字段名。
删除任一帧后重新导出,报错且不产出残缺包(实现:导出成品包 packaging + export.cocos(填桩 · Refs #3) #22 、Windup 产品策划案:面向国产小游戏的 2D 角色素材生成资产工作台 #37 §3.9)。
新增一个仅返回未实现的空 target,无需修改通用层的代码与字段;需要改动通用层即为不通过。
一名未参与开发的成员按包内 README 独立完成导入并播放,全程无需询问。
关联
产品定义见 #37 §3.9(导出包与环境)、决策三(首发微信 + Cocos)、§5.1 假设 5、§5.2 第 6 条。规格底稿见已关闭的 #22 。演示素材与原型惯例见 #87 。模块现状见 docs/module-split.md 的待实现模块表。
本 Issue 定义导出包的产物契约,并实现 Cocos Creator 适配。
背景
#37 §3.9 规定了导出包的内容(GIF 预览、逐帧透明 PNG、sprite sheet、JSON 元数据、导入说明)与行为(仅全部帧通过的动作可进包,不产出残缺包)。决策三定 Cocos 为首发适配目标,§5.2 第 6 条给出验收口径:导出包在 Cocos Creator 与微信小游戏中首次播放成功,无需重切帧、批量改名、逐帧调锚点。
这是清单,不是契约。目录如何组织、图集如何切分、JSON 有哪些字段、字段的坐标系与单位,目前无一处规定。仓库现状与之一致:
docs/module-split.md将 export 列在待实现模块表内,frontend/src/features/export/index.ts为六行空壳,backend/packages/下无实现。MS1 的 #22 定义过一版规格:产物为
sprite_sheet.png、walk.gif、meta.json与 README,meta.json含frames[]、fps、anchor、foot_y、canvas,并要求删掉任一帧后导出报错而非产出残缺包。它依附的骨架 PR #5 未合并即关闭,gac/从未进入主干;#22 已于 08-04 以 not planned 关闭。现有后端为windup_app与ai_engine,这份规格没有迁移过来。本 Issue 承接它。08-03 导师会将项目级一键导出全部角色资产列为服务端待办,该接口的返回物即本契约定义的包,契约需先于接口。
要定义什么
导出物分两层。
通用层是引擎无关的事实:一个动作有多少帧、帧率、是否循环、画布尺寸、锚点与脚底线的位置。它描述资产本身,与消费方无关,落在
meta.json与一套固定目录结构里。引擎适配层由若干 target 组成,每个 target 把通用层翻译成一家引擎原生认识的资源。本期只实现 cocos target,做到用户把导出目录拖进 Creator 工程的
assets/后即可播放。分层的收益在加引擎时兑现:新增 Unity 即新增一个 target,通用层与 cocos target 不动,对应 #37 §3.9 的「导出器按引擎以适配器组织」。其次,即便全部 target 都不合用,用户拿
frames/与meta.json自行导入仍然可行,资产不锁定于任何一家(决策三)。代价是多一层间接。只做 Cocos 一家的话,直接输出 Creator 格式更省事;但策划案已写明 Unity 与 Godot 可手动导入,这层迟早要有,现在划出边界的成本低于将来从 cocos 实现中反向拆分。
为什么不配套做 Cocos 编辑器插件
备选方案是提供 Cocos Creator 扩展,由插件把导出包导入工程。本 Issue 选择让导出物本身即 Creator 可识别的资源结构,用户拖入即用。
若产物可被 Creator 直接识别,插件替用户省下的只是一次拖拽;而扩展是一份独立代码资产,有自己的 API、编辑器进程模型与版本兼容面,需长期维护,与其收益不相称。
该判断依赖一个尚未验证的事实:Creator 对外部生成的动画剪辑与图集元数据识别到什么程度。故第一步为实测。若实测表明必须由插件写入 asset-db 才能得到可播放资源,插件方案重新成立,届时另开 Issue,通用层不受影响。
第一步:先实测,再定字段
手工构造一份符合下文草案结构的样例包,拖入真实的 Cocos Creator 3.x 工程,记录:
.anim能否由外部生成后直接使用,.meta中的 uuid 是否需自行生成。结论回写本 Issue 字段表,字段定稿后进入实现。素材沿用 #87 的 samurai:
base.png与 idle、walk、jump 各 36 帧,256×256 RGBA,全程真实帧。一、通用层:产物契约
目录结构草案:
meta.json字段草案:schema_versioncharacter.id/character.namecanvas.w/canvas.hactions[].nameactions[].fpsactions[].loopactions[].frames[]{index, file},index 连续无缺口actions[].anchor{x, y}actions[].foot_yactions[].atlas{file, cols, rows, cell{w,h}}source其余约定:帧文件命名为
<action>_<三位序号>.png;PNG 带 alpha 通道;任一动作缺帧则导出失败,不产出残缺包。坐标系
契约统一使用图像坐标系:原点在画布左上角,y 向下,
anchor归一化到 0–1。Creator 的
anchorPoint也是归一化的,但 (0, 0) 在左下、(1, 1) 在右上,默认 (0.5, 0.5)。同一个脚底中心,契约里写作 (0.5, 0.95),Creator 里是 (0.5, 0.05),相差近一整张画布的高度。两边各按各的约定读同一个数,角色会悬空或陷入地面。转换由 target 负责。如此规定有两条依据。其一,同一份
meta.json中foot_y使用图像坐标,两个字段共用一套坐标系可减少出错。其二,各引擎的约定互不相同,同一引擎内部亦不统一。Cocos 如上。Unity 的
Sprite.pivot以像素计,而导入期的SpriteMetaData.pivot归一化,且官方文档写作「矩形左上为 (0,0)、右下为 (1,1)」,与 Cocos 上下颠倒。Godot 的 Sprite2D 无归一化 pivot,用centered开关配合以像素计的offset,y 轴向下。通用层采用其中任何一家,另两家的 target 仍要换算,换算总量不变,只是把该家的语义写进了通用层字段;此后该引擎调整约定或新增引擎与之冲突,改动落在通用层,波及全部 target 与已发出的包。故通用层自定义坐标系并写明,换算由 target 承担。二、Cocos target
在通用层之上追加 Creator 可识别的资源文件,草案为图集与其切分数据、每个动作一份
.anim及对应.meta,确切清单由实测确定。使用路径为下载导出目录、拖入工程
assets/、在场景中挂载播放。README 写明固定的 Creator 版本与工程模板,对应 #37 §5.1 假设 5 的失败退路。本期范围
本期只实现 cocos 一个 target。通用层与 target 的边界即扩展点,Unity 与 Godot 的原生资源将来在通用层之上追加,不改通用层字段,两者不在本期。微信小游戏的 4MB 主包与分包组织属部署层,不改变产物本身,不在本期(#37 §3.9)。
验收标准
meta.json通过随 PR 附带的 JSON Schema 校验;字段缺失或类型不符时导出失败,并报出具体字段名。关联
产品定义见 #37 §3.9(导出包与环境)、决策三(首发微信 + Cocos)、§5.1 假设 5、§5.2 第 6 条。规格底稿见已关闭的 #22。演示素材与原型惯例见 #87。模块现状见
docs/module-split.md的待实现模块表。