Skip to content

feat(export-package): enforce complete game asset packages - #97

Open
xyh202131 wants to merge 2 commits into
1024XEngineer:mainfrom
xyh202131:feat/export-package
Open

feat(export-package): enforce complete game asset packages#97
xyh202131 wants to merge 2 commits into
1024XEngineer:mainfrom
xyh202131:feat/export-package

Conversation

@xyh202131

@xyh202131 xyh202131 commented Aug 4, 2026

Copy link
Copy Markdown

功能项

提供浏览器端游戏资产打包与导出边界,将已确认且质量合格的动作整理为结构稳定、可校验的 ZIP 资源包。

本次更新

  • 增加通用 meta.json 契约与 JSON Schema,并在打包前执行字段级校验。
  • 输出连续三位命名的逐帧 PNG、规则 Sprite Sheet、GIF 预览、meta.json 和包内 README。
  • 缺帧、质量未通过、非透明 PNG 或画布尺寸不一致时整包失败,不再用占位图生成“看似成功”的残缺包。
  • 导出面板增加质量门禁、阶段状态和具体错误提示。
  • 增加 AssetExportTarget 扩展边界与 Cocos target 占位实现,通用导出层不绑定具体引擎。
  • 增加打包、Schema、target 边界和导出面板测试。

业务边界

  • 只读取已经确认的 Character、Action 和 Frame 数据。
  • 不修改资产,不推进 WorkflowRun,不负责执行质量检测。
  • 不包含 Playtest 页面接线、资产库、Quick Start 或后端接口。
  • Cocos .anim/.meta 尚未生成:在真实 Creator 3.x 验证格式前,不宣称支持“拖入即播放”。
  • Playtest 后续只负责组装导出模型并调用本模块,不复制打包逻辑。

提交范围

  • frontend/src/features/export-package/ 下 11 个模块、测试与说明文件。
  • frontend/package.jsonfrontend/package-lock.json,用于 gifenc 与 Schema 测试依赖。
  • 共 13 个文件,无后端和构建产物。

验证

  • ExportPackage 定向测试:11 项通过
  • 前端全量测试:13 项通过
  • TypeScript:通过
  • Lint:通过
  • 目标文件格式化:通过
  • 生产构建:通过
  • JSON Schema 解析:通过
  • git diff --check:通过

Refs #94

@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
windup Ready Ready Preview Aug 4, 2026 6:35am

@xyh202131 xyh202131 changed the title feat(export-package): add game asset packaging feat(export-package): enforce complete game asset packages Aug 4, 2026
@xyh202131
xyh202131 marked this pull request as ready for review August 4, 2026 06:45

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two concrete issues found in the new export flow.

The archive root/filename is keyed only by character, and the PNG loader rejects valid PNGs when the response Blob has no image/png MIME type.

return safeSegment(id, 'id').slice(-8) || 'id'
}

function packageRoot(model: ExportPackageModel): string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High: this root omits outfitId/outfitName, even though the app routes exports by characterId and outfitId. Two outfits for the same character will generate the same ZIP name and internal folder root, so one export can overwrite or mask another on disk. Include the outfit identifier in the package root or filename.

const reason = error instanceof Error ? error.message : '未知错误'
throw new Error(`${field}: 图片读取失败(${reason})`)
}
if (blob.type.toLowerCase() !== 'image/png') throw new Error(`${field}: 文件类型必须是 image/png`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: this hard MIME check rejects valid PNG bytes whenever the response Blob has an empty or generic type (common when the server omits Content-Type). The code already validates the PNG signature and alpha channel from the bytes, so this extra gate can fail legitimate exports unnecessarily.

@johnnyzhang-eng

Copy link
Copy Markdown

#119 一起合并时构建失败:缺 ajv 依赖 + Frame.rootMotion 字段归属未定

main + #117 + #119 + #111 + #110 合成集成分支后再合本 PR,tsc -b 报两条(原样贴):

src/features/export-package/asset-export.test.ts(2,21): error TS2307: Cannot find module 'ajv/dist/2020.js' or its corresponding type declarations.
src/features/export-package/model.ts(10,21): error TS2339: Property 'rootMotion' does not exist on type 'Frame'.

两条性质不同:

第一条是依赖漏声明 —— ajv 没进 package.json,补上即可。

第二条是两个 PR 之间的真实契约不一致,不是合并冲突 —— 本 PR 假设 Frame 上有 rootMotion,而 #119 定义的 Frame 类型没有这个字段。git merge 不会报冲突(两边改的不是同一行),只有在合并后跑类型检查才暴露。

顺带说明后端侧的现状:CharacterFrame 的持久化结构目前是 { index, image_url, duration_ms }没有 root_motion;带这个字段提交会被 Pydantic 静默丢弃。所以这不只是前端类型对不齐,而是「位移轨这个数据存在哪一层」还没定:

  • 放帧级(Frame.rootMotion)——导出包按帧取位移,符合本 PR 的用法;
  • 放动作级(一条位移轨挂在 action 上)——数据量小,但导出时要自己按帧插值。

无论选哪个,后端 character_data 的形状要跟着改,建议在合并前先把归属定下来(相关契约讨论在 #81)。另外单位口径也要一并定:目前存在「像素」与「角色总高 = 1.0」两种写法,导出包已经在写这个字段了,口径不统一会直接导致播放位移错误。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants