GNM/GDK、前向+、虚拟几何、GI、ECS、MIR 等术语,以及 WebGL2 扩展的真相
00 · 概念手册
这份文档是给"术语陌生但工程经验充足"的人看的。每个概念回答四件事: 它是什么 → 为什么需要它 → 拿什么类比 → 在 MagicEngine 里落在哪一层。 最后一节修正一个常见的技术前提错误。
1. GNM / GNMX / GDK —— 主机平台的"私房 API"
是什么
| 名称 | 归属 | 是什么 |
|---|---|---|
| GNM | PlayStation 4 / 5 | 索尼的低层图形 API。显式、直接写命令缓冲、几乎无驱动开销,风格接近 Vulkan 但更贴近硬件 |
| GNMX | PlayStation 4 / 5 | GNM 之上的高层封装。帮你处理常见模式,手感接近 D3D12 / Vulkan,多数 PS 游戏用它 |
| GDK | Xbox | Game Development Kit,Xbox 的开发套件(不只是图形 API)。图形部分就是 D3D12 + Xbox 扩展 |
| NVN | Nintendo Switch | 任天堂基于 NVIDIA 的定制低层 API |
为什么需要它
主机是固定硬件:每台 PS5 的 CPU/GPU/SSD 完全一致。这意味着性能是确定可预测的——你 profile 一次,结果对所有人成立。代价是:不能靠"玩家升级显卡"来救,必须把每一字节内存、每一毫秒都算准。
所以主机的 API 设计哲学是"把控制权全部交给你,把责任也全部交给你"。GNM 就是这么来的:draw call 极其便宜,但你要自己管内存别名、同步、资源屏障。
类比
- PC 的 D3D12/Vulkan 像"手动挡汽车"——你要自己换挡
- GNM 像"序列式变速箱 + 直接连到轮轴"——更直接,但操作错了立刻爆缸
- GNMX 像"带自动补油的序列箱"——保留性能,降低失误率
- GDK 不是变速箱,是"整车厂的开发套件"(含工具链、认证工具、调试器)
在 MagicEngine 里的位置
L3 RHI 的后端之一。 但这些 SDK 全是 C++、且受 NDA 保护,所以:
- RHI 边界必须是 C ABI(见 ADR-001),否则 Rust 内核接不进去
- 主机后端不在 P2 之前实现(见 ADR-005 / 05-roadmap)
常见误解
"支持主机 = 加个编译目标"
错。 主机支持 ≈ 重写一个渲染后端 + 重做平台服务(存档/网络/成就/手柄)+ 通过认证(索尼 TRC / Xbox TCR / 任天堂 Lotcheck,100+ 项技术要求,每补丁都要重新过)。第一次做,3–6 个月很正常,且几乎必然反复失败。
2. 前向+(Forward+ / Clustered Forward)—— 用"分格子"解决多光源
是什么
它是前向渲染(Forward)的改良版。三代演进:
| 方案 | 做法 | 问题 |
|---|---|---|
| Forward | 每个物体画一遍,画的时候把所有光源都算一遍 | 光源一多就爆炸(N 物体 × M 光源) |
| Deferred | 先把几何信息写进 G-Buffer(法线/材质/深度),再对每个像素统一算光照 | 带宽大;透明物体没法用;移动端 TBDR 上极贵 |
| Forward+ | 先把屏幕切成小格子(cluster),每个格子只记录"影响我的少数几盏灯",然后每个物体画的时候只算这几盏 | 兼顾前向的简单 + 支持大量光源 |
为什么需要它
移动 GPU 几乎全是 TBDR(Tile-Based Deferred Rendering:Apple / Mali / Adreno / PowerVR)。
TBDR 的特点:把屏幕分 tile,在片上内存里完成渲染,最后一次性写回主存。好处是省带宽;坏处是每多一个渲染目标(RT),就多一次 tile 内存往返。
Deferred 需要 3–4 个 G-Buffer RT → 在 TBDR 上是灾难。所以移动端必须前向。
但纯前向撑不住"几百盏灯"(现代游戏需要大量局部光源)。Forward+ 用 cluster 把光源分配问题变成"每格子查表",成本可控。
类比
一个大厅里有 200 盏灯,你要给每个客人化妆:
- Forward:每个客人挨个走遍 200 盏灯下面看一遍 → 200 次往返
- Deferred:先给每个客人拍一张"素颜全身照"存档,再统一修图 → 存档很占地方(带宽)
- Forward+:先把大厅切成 20 个小隔间,每个隔间只记"这间有 3 盏灯"。客人进哪间隔间就只看那 3 盏 → 又快又准
在 MagicEngine 里的位置
L4 Renderer 的主管线,Tier0–Tier2 使用。Tier3/4 走 Visibility Buffer + Deferred Material。
实现要点:cluster 构建在 WebGL2(无 compute)上要退化——静态光源用预烘焙 cluster 表,动态光走 uniform 数组。这条写进 02-rendering.md 的 §8。
常见误解
"Forward+ 就是 Forward 加了点优化"
不完全。它改变了复杂度模型:Forward 是 O(物体 × 光源),Forward+ 是 O(物体 × 该物体所在格子的光源)。光源数量从"乘数"变成"局部常量",这是质变。
3. 虚拟几何(Virtual Geometry)—— 让美术不用做 LOD
是什么
一套自动管理几何细节层级的系统。核心思想:
- 把高模切成固定大小的小块(cluster,常见 128 三角面)
- 给 cluster 建一棵层级 DAG(不是简单的树,允许复用)
- 运行时根据"这个 cluster 在屏幕上占多少像素"决定用哪一级
- 只渲染刚好够用的细节
UE5 的 Nanite 是这套思路最著名的实现。
为什么需要它
传统流程里,一个高模要美术手工做 4–6 级 LOD(高/中/低/更低/远景),还要处理 LOD 切换的接缝、阴影 LOD、碰撞 LOD。这是巨大的、纯人力成本。
虚拟几何把这件工序消灭了:美术只交一份高模,剩下全自动。
这才是它真正的价值。不是画质更高,是省掉了人的工作。 这也是 MagicEngine 判断"新技术必须有工序价值"的典型案例。
类比
一张 8K 全景照片:
- 传统做法:人工做 5 个尺寸的缩略图,切换时可能"跳"
- 虚拟几何:文件自带金字塔结构,看哪里就解哪里,永远清晰度刚好
区别在于:照片是 2D 的 mipmap,虚拟几何是 3D 空间里的层级细分 + 遮挡剔除,复杂得多。
在 MagicEngine 里的位置
L4 Renderer 的几何子系统,P2 才做。分三步走(见 ADR-004 附录):
- 编译期生成 cluster LOD 链 + CPU 选择(Tier1/2)
- compute 软件光栅到 visibility buffer(Tier2/3)
- mesh shader 路径 + 完整 DAG(Tier3/4)
常见误解
"有了虚拟几何就不需要优化面数了"
错。 Nanite 降低的是"几何细节的成本",但付出了:内存占用更高、固定开销(culling 与光栅化)更大、不支持骨骼蒙皮(角色还是传统管线)、半透明不支持。
所以它是"静态环境的解决方案",不是"所有几何"的解决方案。
4. GI(Global Illumination,全局光照)—— 让光"反弹"
是什么
直接光 = 光源直接照到表面。 间接光 = 光照到墙,墙再照亮旁边的地板和天花板。
GI 就是把间接光也算进去。真实世界里,一个房间有 80% 的亮度来自反弹光。
为什么需要它
没有 GI 的画面,会呈现典型的"游戏感":
- 阴影里死黑一片(现实中阴影里也有光)
- 物体像"贴"在背景上(缺少环境色渗透)
- 室内场景怎么打光都不对
三代技术
| 方案 | 原理 | 代价 | 用在哪 |
|---|---|---|---|
| 光照贴图(Lightmap) | 离线算好,烘成纹理贴到表面 | 静态、不能动、占内存 | 小游戏、移动端 |
| 光照探针(Light Probe / SH) | 在空间里撒探针,存环境光的方向分布 | 精度低、有漏光 | 动态物体 |
| DDGI(动态漫反射 GI) | 探针实时更新,用射线/距离场更新探针 | 需要 compute | 移动高端 / PC |
| 硬件光追 GI | 真射线追踪 | 需要 RT 硬件 | PC / 主机 |
类比
手电筒照墙:
- 直接光:光斑打在墙上,你能看见
- 间接光:整个房间因为墙的反光而变亮,你能看见墙角、天花板
烘焙 = 提前把"这个房间白天/晚上各长什么样"拍照存下来,运行时贴上去。 实时 GI = 每次移动光源都重新算一遍。
在 MagicEngine 里的位置
L4 Renderer 的光照子系统,按 Tier 分级:
- Tier0:Lightmap + SH 探针(AI 在工具期生成并烘焙)
- Tier1/2:SDF-DDGI + 反射探针
- Tier3/4:HWRT-DDGI + 屏幕空间反射
关键设计:Tier0 只有烘焙,但要让玩家"感觉是动态的"——用 SH 探针给角色补环境光、关键场景烘多套光照按时间切换。
5. 角色栈(Character Stack)—— 写实的胜负手
是什么
不是"一个 shader",而是一整套专为角色(人和动物)设计的渲染子系统:
| 子模块 | 解决什么 | 关键技术 |
|---|---|---|
| 皮肤 | 肉感、透光、油光 | 预积分 SSS、双层高光、曲率图 |
| 毛发 | 真实感的最大来源 | Strand(发丝)/ Shell(壳层)/ Card(卡片)三档 |
| 眼睛 | "活不活"的一半 | 角膜折射层 + 巩膜 + 虹膜视差 + 微扫视 |
| 布料 | 褶皱与垂坠 | XPBD 模拟 / 预烘焙骨骼 |
| 面部 | 表情不"橡皮" | FACS blendshape + 皱纹法线混合 |
为什么需要它
一个场景里的建筑、地形、道具,现代管线已经能做得很像。一眼让人看出"这是游戏"的,永远是角色:
- 皮肤像塑料(缺 SSS)
- 头发像头盔(缺发丝或壳层太糙)
- 眼睛像玻璃球(缺角膜层和微动)
所以角色必须单独一套管线,不能混在通用 PBR 材质里。UE 的 "MetaHuman" 就是这条路线的极致。
类比
画人像 vs 画风景:
风景可以靠构图和光影"糊"过去;人像只要眼睛和皮肤有一点不对,整张画就废了。角色渲染就是"人像",需要专门的技法。
在 MagicEngine 里的位置
L4 Renderer 的独立子系统,P2 才做。02-rendering.md §7 有完整的预算表。
常见误解
"角色就是蒙皮模型 + PBR 材质"
如果你的目标是"卡通/风格化",这话基本对。如果目标是"写实",这句话会让你在 P2 阶段推倒重来。
6. ECS(Entity-Component-System)—— 数据导向的组织方式
是什么
把游戏对象拆成三样东西:
| 概念 | 是什么 | 例子 |
|---|---|---|
| Entity | 一个 ID(就是个数字) | #1024 |
| Component | 纯数据,无逻辑 | Position{x,y,z}、Health{hp} |
| System | 纯逻辑,处理"拥有某组组件的所有实体" | MoveSystem:遍历所有有 Position+Velocity 的实体 |
为什么需要它
传统 OOP 是"每个对象一个类,类里既有数据又有方法":
Player 对象 { x, y, hp, 贴图, 音效, AI逻辑, 网络同步... } ← 一个巨大的对象
Enemy 对象 { x, y, hp, 贴图, 音效, AI逻辑, 网络同步... } ← 大量重复
问题:
- 内存不连续:遍历 10000 个敌人,内存跳来跳去,CPU 缓存全废
- 继承地狱:想做个"会飞的敌人",只能多重继承或复制粘贴
- 无法并行:逻辑和数据混在一起,没法安全地多线程
ECS 的做法:按组件类型分数组,同类数据在内存里连续排列。
Positions: [x1,y1,z1, x2,y2,z2, x3,y3,z3, ...] ← 连续!CPU 缓存命中率高
Velocities: [vx1,vy1,vz1, vx2,vy2,vz2, ...]
遍历时是"扫一遍数组",比"追 10000 个指针"快一个数量级。
类比
- OOP 像一柜子档案袋:每个员工一个袋子,要统计"所有员工的年龄"得挨个开袋子
- ECS 像一张 Excel 表:一列是年龄,统计就扫这一列
在 MagicEngine 里的位置
L5 Runtime 的世界模型。但有个细节:ECS 本身不支持"父子层级"(场景树),而编辑器需要。
解决方案:ECS 之上单独维护一份 HierarchyIndex(父子关系是稀疏数据,不放进 archetype)。这是 Unity DOTS 和 Bevy 都踩过的坑。
7. 流式加载(Streaming)—— 边玩边下
是什么
不把整个游戏一次性加载进内存,而是按需、渐进地加载:玩家走到哪,加载到哪;看不清的,先放低清版本。
三种粒度:
| 粒度 | 场景 |
|---|---|
| 分包流式 | 小游戏:主包 4MB 先跑起来,其他包按需下载 |
| 资产流式 | 大世界:按玩家位置加载周边地形/贴图 |
| 渐进式精化 | 先给低模/低清,再异步换成高模/高清 |
为什么需要它
两个硬约束:
- 小游戏主包 4MB:装不下任何像样的资产
- 大世界:几百 GB 的内容不可能全进内存
类比
- 下载完再看 = 下载整部电影再播放(等 2 小时)
- 流式 = Netflix 边看边缓冲(点开就播,卡了降码率)
渐进式精化 = 先看 480p,网络好了自动换 4K,你几乎察觉不到。
在 MagicEngine 里的位置
L5 Runtime 的 Streaming 子系统。小游戏的策略写在 01-architecture.md §5.3:
- 主包只含引擎最小运行时 + 首场景 MIR + 占位资产
- 进场景后立刻预下载相邻分包(
wx.preDownloadSubpackage) - 高模/高清贴图按视距与重要性替换
- 利用平台缓存(微信 IAA 可申请 1GB)
关键指标:首帧 <3 秒。 这是小游戏留存的生死线。
8. 预算注解(Budget Annotation)—— MagicEngine 的核心创新
是什么
在 MIR 的每个节点上,标一个"这个节点允许花多少资源"的显式声明:
node n_alley_wall : mesh.static {
budget: { tri=8000, tex=2MB, draw=4, ms=1.2, mem=6MB }
}
不是"事后统计",而是节点的一等属性。
为什么需要它
传统引擎的性能优化流程是:
美术做完 → 打包 → 上真机 → 掉帧 → profile → 找到问题 → 反推是哪个资产 → 让美术改
这条路的问题:反馈延迟是"天"级,而且问题定位靠人。
有了预算注解,流程变成:
生成/导入时就知道预算 → 超了立刻报错(编译期) → AI 可以局部重算
三个直接收益:
- 定位精确:报错是"节点 n_alley_wall 超预算 240%",不是"场景掉帧"
- 可协商:Planner 可以在节点间"借贷"预算(从余量池借),借贷必须编译期归零
- AI 可优化:优化目标从"让它更快"(模糊)变成"把这 3 个节点压到预算内"(明确)
类比
装修预算表:
- 没有预算表:装完了发现超支,只能砸墙返工
- 有预算表:水电多少、瓷砖多少、家具多少,每项超标立刻知道,可以互相调剂
预算注解就是给每个 MIR 节点发一张"预算表"。
在 MagicEngine 里的位置
L6 MIR 的属性 + 编译链的 budget pass + CI 的硬门禁。
9. MIR(Magic Intermediate Representation)—— 引擎的"源码"
是什么
游戏内容的中间表示。不是二进制资产,而是带类型、带预算、带溯源的结构化图。
四类图,共用一套机制:
| 图 | 表达什么 |
|---|---|
| SceneGraph | 场景层级、变换、组件挂载 |
| MaterialGraph | 材质节点图(分层 PBR) |
| AnimGraph | 状态机、混合树、Motion Matching 配置 |
| BehaviorGraph | 游戏逻辑(可编译为 WASM) |
为什么需要它
因为AI 需要一个能被精确读写、能被机器验证的中间层。
对比三种资产形态:
| 形态 | AI 能改吗 | 能 diff 吗 | 能验证吗 |
|---|---|---|---|
| 二进制资产(.uasset) | 只能猜 | ✗ | ✗ |
| 纯 JSON(Cocos prefab) | 能改,但容易破坏引用 | ✓ | 需要额外写 |
| MIR | 能精确改(有 schema) | ✓(稳定排序) | ✓(内置校验) |
MIR 不是为人类设计的,是为 Agent 设计的。 这是它和"序列化格式"的根本区别。
类比
- 传统资产 = 建好的房子。想改一个房间,得拆了重砌
- MIR = 施工图。改图纸、审图纸、对比图纸,然后重新施工
编译链 = 施工队。图纸通过审核(硬校验)才能开工。
在 MagicEngine 里的位置
L6,全局枢纽。运行时不接受绕过 MIR 的资产直读。这条是架构宪法第 2 条。
AI 只能通过 mir.query / patch / measure 三个接口碰它。
10. 硬校验(Hard Validation)—— 门禁,不是建议
是什么
由确定性代码(不是 LLM)执行的、不通过就阻断编译的检查。
在 MagicEngine 里由 Guardian 角色执行,检查项包括:
| 检查项 | 例子 |
|---|---|
| 预算 | 三角面/纹理/内存/draw call 超限 |
| 引用完整性 | 材质引用了不存在的贴图 |
| 变体数 | 单个材质族编译出 40 个变体(上限 32) |
| 几何合法性 | 碰撞体未闭合、法线反向 |
| 包体约束 | 主包 4.1MB(上限 4MB) |
| 确定性 | 同 seed 两次构建产物不一致 |
为什么需要它
因为 LLM 会幻觉,且在"看起来对"和"确实对"之间没有可靠界限。
如果让 LLM 判断"这个模型能不能用",它会说"看起来不错"。然后你上线,玩家撞进了墙里。
所以架构上的分工是铁律:
- 硬约束 → 代码判定(可证明、可复现、零幻觉)
- 软判断 → LLM 判定(好不好看、符不符合风格)
类比
- 硬校验 = 机场安检门。响了你就是过不去,不管你觉得自己的行李多合理
- AI 评审(Critic) = 同行评议。给意见,但不拦你
两者不能混。
在 MagicEngine 里的位置
L7 Agent Runtime 的 Guardian 角色 + 编译链的门禁。
设计原则(04-toolchain-platform.md §8):所有能在编译期发现的问题,绝不留给运行期。 编译失败比线上闪退便宜 1000 倍。
11. 修正一个技术前提:WebGL2 到底有什么扩展
你提到"需要额外开启 EXT_color_buffer_float / WebGL2 Compute 扩展"。这两件事必须分开说,因为一个是真的,一个已经死了。
11.1 EXT_color_buffer_float —— 真实存在,非常重要
- 作用:允许把渲染结果写进浮点/半浮点的颜色缓冲(普通 WebGL2 只能写 8 位整数)
- 用途:HDR 渲染、后处理链(Bloom/TAA 需要浮点中间结果)、GPGPU(把数据编码到纹理)
- 支持情况:它是 OpenGL ES 3.0 的常见扩展,2018 年后的主流 Android GPU(Adreno 5xx+ / Mali-G 系列 / PowerVR Rogue)普遍支持。
- 但不是保证:必须运行时检测 + 兜底(退化为 RGBA8,HDR 用色调映射压缩)
配套的还有几个,引擎必须逐项探测:
| 扩展 | 作用 | 缺了会怎样 |
|---|---|---|
EXT_color_buffer_float |
浮点渲染目标 | 无法做 HDR 后处理链 |
OES_texture_float_linear |
浮点纹理线性过滤 | 浮点纹理采样变块状 |
EXT_float_blend |
浮点缓冲混合 | 部分粒子/透明效果失效 |
WEBGL_compressed_texture_astc |
ASTC 纹理压缩 | 包体暴涨(移动端最关键) |
WEBGL_compressed_texture_etc |
ETC2 纹理压缩 | 同上(Android 通用) |
KHR_parallel_shader_compile |
异步编译 shader | 首次进入卡顿 |
WEBGL_compressed_texture_astc是小游戏包体的命门。 没有纹理压缩,4MB 主包里塞不下几张图。
11.2 "WebGL2 Compute 扩展" —— 这条不存在,别再指望
这是必须纠正的重点:
| 事实 | 依据 |
|---|---|
| WebGL 2.0 Compute 只是一个编辑草案(Editor's Draft,2021-03-31) | Khronos 官方规范页 |
| 官方明确写:"this specification should be considered obsolete" | 同上 |
| 原因:Khronos 决定停止扩展 WebGL API,把 compute 能力交给 WebGPU | 同上 |
| 实际实现:只有 Chrome / Edge Insider 的实验性通道,从未推广 | 多方资料一致 |
| 标准 WebGL2 不支持 compute shader | 规范本身 |
所以任何"在 WebGL2 上跑 compute shader"的设计都是空中楼阁。
WebGL2 里做 GPGPU 的正确姿势(如果真需要):
transform feedback(WebGL2 原生)—— 顶点着色器输出写回 buffer,适合粒子- fragment shader + 浮点 RT(需要
EXT_color_buffer_float)—— 把计算编码成"画一张图",结果读纹理 - 多个 RT(MRT,WebGL2 原生)—— 一次输出多个结果
这些能做的事远少于真 compute(没有 shared memory、没有原子操作、没有 work group 同步),所以不要按"有 compute"来设计渲染器。
11.3 那微信小游戏 iOS 的 Metal 后端呢?
这是另一回事,也是好消息:
微信在 iOS 侧提供了原生 Metal 渲染后端(2025 年 Unite 大会公布)。它绕过 WebGL,直接走 Metal,因此可以用 Compute Shader、Indirect 调用、PSO、UAV。官方数据:执行效率 +10%,功耗 −15%。
这意味着:
- 微信 iOS 可以做 GPU driven、大规模植被、GPU 粒子 → Tier1
- 微信 Android 仍是 WebGL2(无 compute)→ Tier0,必须有退化路径
同一款游戏在两个平台要用两份不同的编译结果。 这就是为什么 MagicEngine 的能力分级必须按"后端能力"而不是"平台名"——这条写进了 ADR-004。
11.4 结论(给架构的三条硬规则)
- 永远不设计依赖 WebGL2 compute 的方案。 它不存在,也不会存在。
EXT_color_buffer_float等扩展必须运行时探测 + 分级降级,不能假设。- RHI 接口第一件事就是
capability_query(),返回一张能力表,渲染器据此选择预编译好的路径(不是运行期 if)。
12. 能不能超越 Cocos / Laya?
先说结论,再说依据。
结论
| 维度 | 判断 |
|---|---|
| 1–2 年内全面超越 | 不可能,也不该作为目标 |
| 单点超越(AI 内容生产效率) | 可能,P0 就能验证 |
| 结构性超越(跨端到主机 / 写实角色) | 架构上成立,但要 3 年 |
| 生态、稳定性、包体极限 | 短期必然落后,靠时间补 |
可以超越的地方(三条,都是结构性的)
① AI 内容生成的"可验证性"
Cocos / Laya 的 AI 是外挂式:对话给建议,人手动落地。MagicEngine 的 AI 产出 MIR diff,经硬校验、可 review、可回滚、可复现。
差别不在"AI 强不强",而在AI 的产出能不能被工程化管理。这是架构差异,不是功能差异。
② 跨端上限(到主机)
Cocos / Laya 没有 PS5 / Xbox 后端,也没有这个计划——因为它们的抽象层次(JS/TS 运行时)撑不到主机级性能。这不是努力问题,是架构天花板。
MagicEngine 的 RHI 从第一天就按"多后端 + C ABI"设计,主机是"还没实现",不是"实现不了"。
③ 写实角色的渲染上限
Cocos 的 3D 管线做不了 SSS 皮肤 / 发丝 / 角膜折射那一套。如果你的目标里真有"渲染真实的人物、动物",这是一条硬需求,而 Cocos 满足不了。
不能超越的地方(也要认清)
① 包体极限优化
Cocos 在小游戏包体上积累了多年(引擎裁剪、资源压缩、代码分包),P0 阶段我们大概率做不过它。这是经验问题,只能用真实项目喂出来。
② 微信生态适配深度
Cocos 与微信有官方级合作,新能力(如 Metal 后端、GLX 模式)往往第一时间适配。我们要自己追。
③ 生态与人才池
Cocos 有插件市场、教程、社区、招聘池、免费开源。这些是"网络效应",新引擎从 0 开始,这块基本是 0 分。
④ 稳定性
Cocos 被几万个项目踩过。我们的 P0 版本一定有一堆边界 bug。这是必经之路,不能靠架构设计跳过。
一个必须承认的事实
Cocos Creator 的 .prefab / .scene 本来就是 JSON,可 diff。所以"资产可 diff"不是我们的独占优势——这一点我上一版文档里说得过重了,这里修正。
我们真正的差异是三条:预算注解 + AI 精确编辑 MIR + 编译期跨端。前两条 Cocos 没有,第三条它做不到。
所以战略上应该怎么定位
不做"另一个 Cocos",做"AI 时代的另一种生产方式"。
具体来说:
- 短期:它是悦居自用的生产工具(用真实项目喂,不对外卖)
- 中期:在"小游戏要做 3D/写实"和"小游戏要上主机"这两个细分场景上,它是唯一选项
- 长期:如果 P0 的效率优势被实测证明,它才有资格谈"产品化"
最危险的做法是:还没证明效率优势,就先按"通用引擎产品"来做。 那会同时输掉技术和商业。
一句话定位、三条信念、明确不做什么、成功标准
MagicEngine / 悦居魔法引擎
AI-native game engine。从第一行代码开始,AI 就是编译链的一等公民,而不是编辑器里的一个对话框。
| 项 | 值 |
|---|---|
| 版本 | v0.1 — 架构基线(未实现) |
| 状态 | Proposed 待评审;音频子系统 SfxForge 已可运行 |
| 代号 | MagicEngine(ME) |
| 主体 | 北京悦居智能科技中心 |
| 日期 | 2026-10-22 |
1. 一句话定位
一个预算感知(budget-aware)的跨端游戏引擎:
游戏内容以可读、可验证、可复现的中间表示(MIR)存在; 由 AI Agent 生成与优化; 由编译链按目标平台预算编译为可执行包。
不是"3D 引擎加个 AI 助手",而是"把游戏当作可编译程序的产物"。
2. 我们相信的三件事
① 引擎的瓶颈不是画质算法,是内容成本。 Nanite 之所以改变行业,不是因为它的光栅化算法更优雅,而是因为它消灭了 LOD 制作这道人力工序。任何一项新技术如果不削减人的工作量,就进不了生产管线。AI 的价值同理——它必须削减工序,而不是增加一个"要点一下"的按钮。
② AI 必须进编译链,不能只做面板。 Cocos / Unity / Unreal 都在加 AI,路径都是"引擎外部挂一个 Copilot"。这条路的天花板是人的手速:AI 给建议,人手动落地。我们的路径是 AI 直接产 MIR 变换(diff),由 Validator 硬校验、由编译器落盘。人只做 review,不做搬运。
③ 跨端不是"导出按钮",是编译目标。
同一份 MIR,编译到 WebGL2 与编译到 PS5,差别不在"打包方式",而在预算差 4 个数量级(每帧三角面 5 万 vs 2000 万)。所以平台差异必须在类型系统里表达,而不是在运行时用 #ifdef 兜。
3. 不做什么(与做什么同等重要)
架构的第一性工作是划边界。以下为明确非目标,违反即架构失控:
| 非目标 | 原因 |
|---|---|
| 5 年内在"写实画质"上超过 UE5 | UE5 有 20 年、数千人年积累与 Quixel 资产库。正面硬刚必输,必须换赛道 |
| 全平台同步交付 | 主机 SDK 有资质门槛(NDA/审核),且工作量为小游戏的 20 倍以上 |
| 自研物理引擎 | 刚体/约束求解是 30 年成熟领域,集成 Jolt 即可;自研只做"游戏性特化层" |
| 自研音频中间件 | 集成 Wwise / FMOD;小游戏端自研轻量混音器。注意区分:自研的是音效合成(SfxForge,见 07),不是混音总线/3D 引擎 |
| 自研视频编解码 | 用平台原生 |
| 支持 30 种平台 | 每个平台的测试矩阵成本是超线性的 |
| 做通用低代码/无代码平台 | 与"面向 3A"直接冲突,会毁掉引擎的表达力上限 |
4. 差异化:三条护城河
4.1 AI Bake —— 解决"小游戏端零 AI 算力"
小游戏跑在 WASM + WebGL2 上,没有 NPU、没有 CUDA、单线程为主。任何"运行时跑模型"的方案在这里都不成立。
ME 的机制:工具期用大模型生成,运行期只消费烘焙产物。 AI 的产出被编译成确定性的数据(光照探针、导航网格、LOD 层级、材质参数、动画片段),烘焙进包或 CDN 分包。运行时零推理开销。
这让"AI 引擎"第一次能跑在 4MB 主包里。
4.2 MIR —— 带预算的中间表示
MIR(Magic Intermediate Representation)之于游戏引擎,如同 MLIR 之于编译器。
- 可读:文本形态,LLM 可直接读写、可 diff、可 review
- 带预算:每个节点标注
tri / tex / draw / ms / mem - 可验证:Validator 是硬门禁,不通过不给进包
- 可复现:
seed + model_version + recipe_hash三元组锁定,任何人可重跑出同一结果
4.3 Performance Contract —— 性能契约
游戏在项目设置里声明目标:微信 iOS / 低端机 / 30fps / 峰值内存 600MB。
引擎把这条契约编译成每个资源的预算分配,并在 CI 里持续验证。
不达标时 Perf Agent 自动产出降级方案(MIR diff),人 review 后合入。
性能从"上线前救火"变成"每次提交的断言"。
5. 成功标准(可证伪)
引擎项目最容易死在"永远差半年"。所以定义可证伪的里程碑:
| 阶段 | 可证伪标准 |
|---|---|
| P0 | 1 款真实上线微信小游戏,主包 ≤4MB,低端安卓 30fps 稳定,全程未手写 shader |
| P1 | 该游戏 3D 版本上线微信+抖音双端;AI 生成的关卡占比 >50% 且通过 review |
| P2 | 同一份 MIR 编译出 PC 与主机包,画质差距 ≤1 档;外部团队能用它做出第 2 款游戏 |
| P3 | 有第 3 方团队付费使用;单项目美术工时相比 Unity 路线下降 ≥30%(可测量) |
关键判据:引擎只有被 ≥3 个项目复用才可能回本。 所以 P0 就必须用真实商业项目喂,不能做"先做引擎再做游戏"。
6. 文档地图
| 文档 | 内容 |
|---|---|
| 01-architecture.md | 分层架构、MIR 设计、依赖规则、编译链 |
| 02-rendering.md | 渲染分级、能力矩阵、"逼真+性能"的技术拆解 |
| 03-ai-kernel.md | ★ AI 内核:Agent 拓扑、预算感知生成、确定性 |
| 04-toolchain-platform.md | 工具链、平台矩阵与降级策略 |
| 05-roadmap.md | 分期路线、组织、成本、风险登记册 |
| 06-asset-intelligence.md | 资产复用梯度、世界语法、构件化生成、规范化闸门、冷启动 |
| 07-audio.md | ★ 音频与音效:AI 生成配方而非波形、七项硬校验、声学体检、场景音景闭环 |
| 08-audio-gen.md | ★ 生成式音频选型:11 个后端许可核验、可调用性实测、三档选型、三分区架构 |
| 09-development-plan.md | ★ 开发总计划:P0–P3 路线、六个里程碑 DoD、WBS 与工时、关键路径、三种降级方案 |
| 10-p0-sprint-plan.md | ★ P0 迭代执行计划:13 个双周迭代的任务/人日/DoD,前 10 个工作日排班 |
| 11-parallel-agents.md | ★ 四 Agent 并行开发:四类冲突、扇区模型、crate 内分权、共享面设计、并发度曲线、同步协议 |
| plan/agents.json | 扇区定义(机读唯一事实源)+ 4 份 Agent 启动简报 |
| interface/ | 契约目录:MANIFEST.json 登记版本与哈希,冻结后改动必须 bump |
| tools/ci/check_ownership.py | 越界守卫:files / contracts / dupes 三个检查 |
plan/gantt.html |
可交互甘特图(数据源 plan/p0-plan.json,改排期只改 JSON) |
| adr/ | 架构决策记录(6 份关键决策) |
7. 当前未决问题(Open Questions)
- 内核语言最终定 Rust 还是 C++ —— 见 ADR-001,已提前到 P0 第 2 周(S0 的 D0 门)做 FFI 冒烟测试后定论
- 主机资质路径(PS5 对国内中小团队的实际审批周期)
- 自研超分 vs 集成 FSR/XeSS 的取舍(授权与主机认证问题)
- ~~MIR 的文本语法最终形态(是否采用 S-expression)~~ → 已定:块语法 + 稳定属性顺序,不用 S-expression(理由见 10 号文档 S1)
- 商业模型:自用 / 授权 / 开源核心 —— 影响架构的开放性设计
八层结构、依赖宪法、MIR 设计、七段编译链
01 · 总体架构
1. 设计约束(先约束,再方案)
| 编号 | 约束 | 对架构的硬性影响 |
|---|---|---|
| C1 | 微信小游戏主包 4MB / 总包 30MB | 内核必须极小;资产必须可远程分包;不能带完整引擎运行时 |
| C2 | 小游戏 Android 侧仅 WebGL2(≈GLES3.0,无 Compute Shader) | 渲染器必须有 Compute-free 路径,不能假设 compute 存在 |
| C3 | 小游戏 iOS 侧已有原生 Metal 后端(compute / indirect / UAV 可用) | 能力分级要看"后端能力",不能按"平台"分级 |
| C4 | 小游戏 WASM 单线程为主,CPU 弱 | 主线程绝不做重计算;Job 系统要能退化到"时间切片"而非线程 |
| C5 | 主机 SDK 仅 C++,且需资质 | 内核语言选择必须留下 C ABI 逃生口 |
| C6 | 目标跨度:5 万三角面/帧 → 2000 万三角面/帧 | 差异必须在类型系统层表达,不能靠运行时分支 |
| C7 | 引擎须被 ≥3 项目复用才回本 | 必须"游戏驱动",架构要允许局部裁剪 |
2. 分层架构
┌──────────────────────────────────────────────────────────────────────┐
│ L8 Magic Studio(编辑器 / 工具层) │
│ 场景编辑 · 材质图 · 动画图 · AI 工作台 · 性能工作台 · 资产库 │
├──────────────────────────────────────────────────────────────────────┤
│ L7 Agent Runtime(AI 内核)★ │
│ Intent → Planner → Generator → Validator → Optimizer │
│ 模型适配层:云 LLM / 本地小模型 / 烘焙缓存 │
├──────────────────────────────────────────────────────────────────────┤
│ L6 MIR(Magic Intermediate Representation)★★ 全局枢纽 │
│ SceneGraph · MaterialGraph · AnimGraph · BehaviorGraph │
│ + 预算注解 + provenance + 可 diff 文本格式 │
├──────────────────────────────────────────────────────────────────────┤
│ L5 Runtime Subsystems(运行时子系统) │
│ World(ECS) │ Anim │ Physics │ Audio │ Script │ Net │ UI │ Streaming │
├──────────────────────────────────────────────────────────────────────┤
│ L4 Renderer(Render Graph 驱动) │
│ VirtualGeom │ GI │ Material │ Character │ PostFX │ Culling │
├──────────────────────────────────────────────────────────────────────┤
│ L3 RHI(渲染硬件抽象,C ABI 边界) │
│ WebGL2 │ WebGPU │ Metal │ Vulkan │ D3D12 │ GNM │ GNMX │ D3D12X │
├──────────────────────────────────────────────────────────────────────┤
│ L2 PAL(平台抽象) │
│ FS · Thread · Time · Input · Net · Pay · Ads · Account · Storage │
├──────────────────────────────────────────────────────────────────────┤
│ L1 Platforms │
│ 微信/抖音小游戏(WASM) │ iOS │ Android │ Win │ macOS │ Linux │ PS5 │ Xbox │ Switch │
└──────────────────────────────────────────────────────────────────────┘
2.1 依赖规则(架构的"宪法")
- 只允许向下依赖。L5 不许 import L7;L4 不许知道 L8 存在。
- L6 是唯一真相源。运行时不接受任何"绕过 MIR 的资产直读"。这条保证了 AI 可介入性——凡是不能表达为 MIR 的东西,AI 就碰不到,也就无法优化。
- L3 边界必须是 C ABI。理由见 ADR-001:内核语言可能变,RHI 后端几乎必然是 C++(贴 SDK)。C ABI 是唯一稳定契约。
- L2 之上禁止出现
#if PLATFORM。平台差异收敛为 PAL 的接口实现 + MIR 的 target 注解。允许的例外只有构建脚本。 - L7 不许直接改文件。Agent 只能产出 MIR diff 或 recipe,落盘由编译链统一执行(保证可审计、可回滚)。
违反第 4 条是最常见的死法:三年后代码里 3000 个
#ifdef,引擎就废了。
3. MIR:引擎的枢纽
3.1 为什么需要它
传统引擎的资产是"二进制 blob"(.uasset / .prefab / .meta)。问题:
- AI 读不懂 → 只能靠截图识别,无法精确编辑
- 无法 diff → 多人协作靠锁文件,AI 改动无法 code review
- 无预算概念 → 优化只能在事后由人肉做
MIR 把资产变成带类型的程序。
3.2 节点模型
每个 MIR 节点是一个类型化 Operator:
node <id> : <op-type> {
inputs: [ ... ]
attrs: { ... }
budget: { tri=12k, tex=4MB, draw=3, ms=0.8, mem=6MB }
provenance: { by="agent:artisan", seed=0x9f2a, model="xxx@v3", at="2026-10-22T09:00Z" }
}
四类图(Graph),共用同一套节点/预算/溯源机制:
| 图 | 表达 | 主要消费者 |
|---|---|---|
| SceneGraph | 层级、变换、组件挂载 | World / Renderer / Culling |
| MaterialGraph | 材质节点图(PBR 分层) | 着色器生成器 |
| AnimGraph | 状态机 + 混合树 + Motion Matching 配置 | Anim 子系统 |
| BehaviorGraph | 游戏逻辑(可视化节点) | Script 子系统(可编译为 WASM/原生) |
3.3 文本与二进制双形态
- 文本(
.mir):人类可读、LLM 可直接写、git 可 diff。开发期权威格式。 - 二进制(
.mirb):编译产物、零解析开销、可内存映射。发布期权威格式。
两者由同一份 schema 生成,CI 强制校验往返一致性(mir → mirb → mir 必须字节级等义)。
3.4 预算注解是核心创新
预算不是"事后统计",而是节点的一等属性。这样:
- Generator 可以在生成时就知道"这个场景只能花 12k 三角面"
- Validator 可以本地化报错:"节点
n_0x3f超预算 240%",而不是"整个场景掉帧" - Optimizer 可以在局部做变换,不需要全局重算
对比:UE 的 Nanite 让几何"免预算",但代价是把成本转移到固定开销与内存。ME 的选择是显式预算 + 自动协商,因为小游戏端必须有硬上限。
4. 编译链
MIR 到可执行包,是一条标准的多 pass 编译流水线:
.mir 文本
│ ① parse → AST
│ ② legalize → 目标平台能力合法化(如 compute → 伪 compute)
│ ③ budget-pass → 预算分配与协商(父子节点间借贷)
│ ④ ai-pass → ★ Agent 优化(可选,可多次迭代)
│ ⑤ lower → 平台特化 IR(着色器变体、资产编码、场景烘焙)
│ ⑥ link → 分包切分(4MB 主包约束)、资产寻址、依赖排序
│ ⑦ pack → 平台包(小游戏 zip / APK / IPA / 主机镜像)
▼
发布产物
关键设计:
- ② legalize 是跨端的核心。例如
virtual_geometry在 WebGL2 目标上被合法化为"预烘焙 cluster LOD 链",而不是"报错不支持"。降级是编译期决定的,不是运行期判断的——运行期判断会让 shader 变体爆炸。 - ④ ai-pass 是唯一的非确定性 pass。它必须记录完整的 provenance,且可以
--no-ai关闭(用于复现 bug)。 - ⑥ link 阶段做分包。这与传统链接器分配段地址同理:把 30MB 内容切成"4MB 主包 + N 个 CDN 分包",按场景可达性做图划分。
5. 运行时架构
5.1 World(ECS)
- Archetype-based ECS(数据导向,cache 友好)
- 编辑器需要层级视图 → ECS 之上维护一份
HierarchyIndex(父子关系是稀疏数据,不放进 archetype) - 系统调度:静态依赖图 + 自动并行;无线程时退化为时间切片执行器(每帧给 Job 系统 2ms CPU 预算,超时任务跨帧续跑)
5.2 内存模型
三级:
| 级别 | 用途 | 小游戏策略 |
|---|---|---|
| Frame Arena | 每帧临时数据,帧末整体重置 | 固定大小,超限即报错(早失败优于 OOM) |
| Asset Heap | MIR 编译产物,引用计数 + 句柄 | 按分包粒度整体卸载 |
| Persistent | 全局单例、配置 | 极小 |
小游戏端峰值内存目标是 iOS 低端机 <600MB(Unity 官方建议 <1GB 才不易闪退,我们取更保守值留余量)。
5.3 资产流式
小游戏的启动预算:首帧 <3 秒,主包 <4MB。策略:
- 主包只含:引擎最小运行时 + 首场景 MIR + 占位资产(低模 + 纯色材质)
- 首场景进入后立即预下载相邻分包(
wx.preDownloadSubpackage) - 高模/高清贴图按视距与重要性分优先级流式替换("渐进式精化")
- 缓存利用平台能力(微信 IAA 游戏可申请 1GB 缓存)
6. 代码组织(仓库形态)
magic-engine/
├── crates/
│ ├── me-core/ # 基础类型、数学、句柄、错误
│ ├── me-mir/ # MIR schema、解析、序列化、校验
│ ├── me-compiler/ # 编译链 7 个 pass
│ ├── me-runtime/ # L5 运行时(no_std 友好,可编 WASM)
│ ├── me-render/ # L4 渲染器
│ ├── me-rhi/ # L3 抽象 + 各后端(C ABI)
│ ├── me-pal/ # L2 平台抽象
│ ├── me-agent/ # L7 AI 内核
│ └── me-studio/ # L8 编辑器后端
├── backends/ # C++ 实现的 RHI 后端(Metal/Vulkan/D3D12/GNM)
├── studio-ui/ # 编辑器前端(TS + React)
└── samples/ # 垂直切片项目
若 ADR-001 最终选 C++,此结构不变,仅语言替换——这正是把 RHI 边界定在 C ABI 的收益。
7. 与成熟引擎的取舍对照
| 维度 | Unreal | Unity | Cocos | MagicEngine |
|---|---|---|---|---|
| 资产格式 | 二进制 uasset,不可 diff | YAML prefab + meta(可 diff 但冗长) | JSON + 二进制 | MIR(带预算/溯源,LLM 原生) |
| 跨端方式 | 各平台独立后端 | IL2CPP + WebGL 转换(有转换损耗) | TS 原生 + 多后端 | MIR 多目标编译(WASM 一等公民) |
| AI 定位 | 外部助手(Copilot) | 外部助手 | 外部助手 | 编译链 pass + Agent 闭环 |
| 小游戏 | 转换方案,包体压力大 | 转换方案,需大量裁剪 | 原生优势 | 原生 WASM-first,预算编译 |
| 优化方式 | 人工 profile | 人工 profile | 人工 profile | 性能契约 + Perf Agent 自动产 diff |
| 学习曲线 | 极陡 | 中 | 平缓 | 取决于 AI 生成质量(风险点) |
我们的赌注:AI 让"引擎复杂度"对开发者的暴露面大幅下降。UE 难学是因为要人理解它的抽象;如果 Agent 能正确产出 MIR,人只需要 review。这个赌注如果成立,复杂度就是护城河;如果不成立,复杂度就是负债。这是全项目最大的单点风险,见 05-roadmap 风险 R1。
逼真的六个维度、Tier0-4 能力矩阵、角色栈、Compute-free 路径
02 · 渲染架构
目标原话:"渲染既要逼真,又要性能好,可以渲染真实的世界和人物、动物。"
先把这句话拆开,否则无法工程化。
1. "逼真"的六个维度(拆解)
"看起来真"不是一个指标,是六个正交维度。每个维度的技术手段与成本完全不同,必须分开预算,否则优化会打空。
| # | 维度 | 人眼敏感度 | 关键技术 | 成本量级 |
|---|---|---|---|---|
| V1 | 几何保真 | 中 | 微多边形/置换、扫描资产、虚拟几何 | 高(内存) |
| V2 | 光照保真 | 高 | 多次反弹 GI、能量守恒、体积介质 | 极高(算力) |
| V3 | 材质保真 | 高 | 多层 BRDF(清漆/布料/皮肤)、各向异性、吸收散射 | 中(shader 复杂度) |
| V4 | 动态保真 | 高 | 毛发、布料、软体、破碎 | 高(模拟) |
| V5 | 时序保真 | 极高 | TAA、超分、去噪、抗闪 | 低(后处理) |
| V6 | 感知保真 | 极高 | 物理相机、景深、运动模糊、色调映射、镜头特性 | 低 |
核心判断 1:V5/V6 的性价比远高于 V1/V2
人眼对闪烁、抖动、噪点、锯齿的容忍度极低,对"光线反弹了几次"几乎无感。所以:
预算分配顺序:V5 → V6 → V3 → V4 → V1 → V2
这解释了为什么"一个稳定的 30fps 移动游戏"看起来比"一个抖动的 60fps 高模场景"更专业。也解释了为什么 TAA/超分必须在 P1 就做完,不能留到最后。
核心判断 2:写实的真正门槛是"人物与动物"
真实世界的建筑/地形,现代管线已经能做得很好。真正让人一眼看出"这是游戏"的,是角色:皮肤的次表面感、毛发的透光、眼睛的高光形状、布料褶皱。所以渲染架构里 Character Stack 是一个独立的一等子系统,不混在通用材质里。
2. 能力分级矩阵
分级依据是后端能力,不是平台名(因为微信 iOS 已有原生 Metal:compute/indirect/UAV 可用;而 Android 仍是 WebGL2:无 compute)。
| 能力 | Tier0 WebGL2 |
Tier1 小游戏+Metal |
Tier2 移动原生 |
Tier3 PC |
Tier4 主机 |
|---|---|---|---|---|---|
| Compute Shader | ✗ | ✓ | ✓ | ✓ | ✓ |
| Indirect Draw | ✗ | ✓ | ✓ | ✓ | ✓ |
| Mesh Shader | ✗ | ✗ | ✗ | ✓(部分) | ✗(GCN) ✓(RDNA) |
| 硬件光追 | ✗ | ✗ | ✗ | ✓ | ✓ |
| 稀疏纹理 / 虚拟纹理 | ✗ | ✗ | ✓ | ✓ | ✓ |
| 目标分辨率 | 720–1080p | 1080p | 1080–1440p | 1440p–4K | 4K |
| 三角面/帧 | 5 万 | 30 万 | 100 万 | 1000 万+ | 3000 万+ |
| Draw call/帧 | <100 | <400 | <1000 | 动态 | 动态 |
| 帧率 | 30 | 30–60 | 30–60 | 60–120 | 60 |
| 峰值内存 | 400MB | 600MB | 1.5GB | 8GB | 12GB |
面数不是靠"优化"从 5 万堆到 3000 万,而是靠完全不同的几何管线。这就是为什么"一份内容全平台跑"必须靠 MIR 编译期的 legalize pass 做形态转换,而不是运行期 LOD 切换。
3. 渲染管线选型
3.1 移动端必须 Forward+
移动 GPU 绝大多数是 TBDR(Tile-Based Deferred Rendering:Apple / Mali / Adreno / PowerVR)。TBDR 的片上 tile memory 让"多 pass 全屏写 G-Buffer"极其昂贵(每多一个 RT 就多一次 tile 内存往返 + 带宽)。
所以:
| 目标 | 管线 | 理由 |
|---|---|---|
| Tier0/1/2 | Clustered Forward+ | 单 pass、MSAA 友好、带宽可控;用 light cluster 支持多光源 |
| Tier3/4 | Visibility Buffer → Deferred Material | 虚拟几何天然产 vis-buffer(只存 triangleID+depth),material resolve 延后,带宽最优 |
两条管线共享同一个 Render Graph 描述层,差异由 legalize pass 决定,不是两套代码。
3.2 Render Graph
所有 pass 不直接绑定资源,而是声明"读/写哪个虚拟资源",由 Graph 编译器做:
- 资源别名(transient 纹理复用同一块内存)
- 自动 barrier / layout 转换
- 异步 compute 队列调度(Tier1+)
- pass 裁剪(输出未被消费的 pass 直接删)
这是现代渲染器的标配(Frostbite FrameGraph / UE RDG),但从 0 做的好处是可以把预算直接建进去:每个 pass 声明 ms_budget,Graph 编译时报"总预算超支 X%"。
4. 几何:Virtual Geometry
目标:让美术不用做 LOD(这是 Nanite 的真正价值,不是画质)。
4.1 两种实现路径
| 路径 | 前提 | 做法 |
|---|---|---|
| A. Mesh Shader 路径(Tier3/4) | mesh shader 可用 | cluster 层级 culling 在 task shader 里做,直接输出 micro-triangle |
| B. 软件光栅路径(Tier1/2) | compute 可用 | compute 做 cluster cull + 软件光栅到 vis-buffer,硬件光栅只处理"边缘像素" |
| C. 预烘焙 LOD 链(Tier0) | 无 compute | 编译期离线生成 4–6 级 cluster LOD,运行时按视距/屏幕占比选级 |
关键:A/B/C 是同一个 MIR 几何源的三种 lower 结果。美术只交一份高模。
4.2 数据布局
- Cluster = 128 三角面,固定大小(便于 GPU 并行)
- 每 cluster 存:包围球、锥体(cone)、LOD 误差、父 cluster 索引
- 层级 DAG(不是树),允许 cluster 复用(Nanite 的核心洞察)
4.3 Tier0 的现实妥协
WebGL2 没有 compute、没有 indirect,所以"GPU driven"在小游戏低端机上不成立。妥协方案:
- 编期生成 LOD 链 + 距离选择(CPU 端,每帧几百次,可接受)
- 静态场景合批(static batching)到极少 draw call
- 大量使用 impostor(远景树木/建筑用 billboard)
- 动态物体严格限面数(角色 3k–8k 三角面)
这是硬边界,不要幻想在 4MB 主包里跑 Nanite。
5. 光照:三级 GI
| Tier | 方案 | 说明 |
|---|---|---|
| Tier0 | Lightmap(烘焙)+ SH 探针 | 完全离线。AI 在工具期生成光照并烘焙 |
| Tier1/2 | SDF-DDGI + 反射探针 | 动态物体有间接光,代价可控(DDGI 需要 compute/纹理反馈) |
| Tier3/4 | HWRT-DDGI + 屏幕空间反射 + 体积雾 | 硬件光追做次级光追与反射 |
关键判断:Tier0 的 GI 必须"看起来是动态的"
小游戏端只能烘焙,但玩家会看到"角色走过墙边没有颜色渗透"而觉得假。解决: - 角色用 SH 探针采样环境光(近似) - 关键场景用"烘焙 + 屏幕空间补光" - AI 在工具期以"多套光照方案"烘焙,运行时按时间/天气切换(等效动态)
6. 材质:分层材质系统(Layer Stack)
写实材质的真相是"没有单一 BRDF 能表达真实表面"。
6.1 分层模型
Layer 3: 清漆 / 污渍 / 水膜 (薄膜、Fresnel 调制)
Layer 2: 细节法线 / 划痕 / 织物 (微表面扰动)
Layer 1: 基础 BSDF (GGX / 布料 / 皮肤 / 毛发)
Layer 0: 吸收与次表面 (体积吸收、SSS)
每层是一个 MIR MaterialGraph 子图,可独立被 AI 生成/替换。这是 AI 能介入材质的关键:AI 不需要生成"最终 shader",只需要生成"某一层的参数与拓扑"。
6.2 扫描材质与 AI 生成
- 工具期:照片/扫描 → AI 分解为 albedo/normal/roughness/AO/height + 分层结构
- Validator 硬校验:能量守恒(albedo × 光照不超 1)、法线强度合理、无缝 tiling、PBR 通道完整性
- 输出为 MIR
MaterialGraph,不是贴图集合 —— 保证可编辑、可换分辨率、可 diff
6.3 着色器变体治理
变体爆炸是引擎的隐形杀手(Unity 的 shader variant 噩梦)。ME 的策略:
- 材质图编译为"超着色器(ubershader)+ 静态分支",分支由 uniform 控制,减少变体数
- 变体 key 由 MIR 静态推导,编译期枚举(不是运行时发现)
- 缺失变体 = 编译错误(不是运行时黑屏)
- 小游戏端强制"变体预算":单个材质族 ≤32 个变体
7. Character Stack(人物与动物专用)
这是"写实"的胜负手。独立子系统,包含:
7.1 皮肤
- 预积分 SSS(Pre-integrated Skin,Penner 方案):用曲率图 + LUT 近似次表面散射,成本极低
- 双层高光(油膜 + 真皮)
- 微表面细节法线(毛孔),按视距淡出防闪烁
- 皱纹法线混合(表情驱动),避免"橡皮脸"
7.2 毛发(含动物)
三级方案:
| 方案 | Tier | 说明 |
|---|---|---|
| Strand(曲线) | Tier3/4 | 真实发丝,需要 compute + 深度排序;UE 的 Hair Strands 路线 |
| Shell(壳层) | Tier0/1/2 | 20–40 层壳,每层偏移 + alpha 噪声;成本可控,远看效果尚可 |
| Card + 深度排序 | Tier0 | 传统方案,配合双面渲染与深度剥离 |
动物毛发的额外难点:密度高(几百万根)、有方向性(grooming)、有长短过渡。方案是 Shell 为主 + 少量 Strand 做近景("混合毛发"),并用 AI 从参考图推断 grooming 方向场(工具期烘焙成 3D 纹理)。
7.3 眼睛
写实角色"死不死",一半看眼睛:
- 眼球三层:巩膜 / 虹膜(视差 + 折射)/ 角膜(Fresnel 高光层)
- 湿润感:角膜层独立高光 + 环境反射
- 视线驱动 + 微扫视(saccade)—— 静止不动的眼睛立刻显假
7.4 布料与软体
- 布料:位置动力学(PBD/XPBD),Tier0 用预烘焙骨骼动画 + 少量实时
- 头发/裙摆的"次级运动"用弹簧骨链(cheap,但效果显著)
7.5 角色渲染的预算分配(Tier2 移动端,1080p)
| 项 | 预算 |
|---|---|
| 主角三角面 | 60k |
| 主角骨骼 | 120 |
| 同屏角色数 | 8 |
| 皮肤 shader | ≤2.5 ms |
| 毛发(主角) | ≤3 ms |
| 布料模拟 | ≤1 ms |
8. Compute-free 路径:最容易被低估的工程难点
Tier0(Android 小游戏)没有 compute,但渲染器有 80% 的现代技术依赖 compute。必须有一套"退化实现":
| 依赖 compute 的技术 | WebGL2 退化方案 | 质量损失 |
|---|---|---|
| GPU 视锥/遮挡剔除 | CPU 剔除 + occlusion query 异步回读(延迟 1–2 帧) | 轻微 |
| Cluster 光照构建 | 预烘焙 cluster 表(静态光)+ 动态光走 uniform 数组 | 中 |
| 软件光栅(虚拟几何) | 编期 LOD 链 | 中 |
| GPU 粒子 | transform feedback(WebGL2 支持) | 轻 |
| 后处理降噪 | 空间滤波(不用时序) | 中(会闪) |
| Bloom / DOF | mip 链近似 | 轻 |
架构要求:所有"依赖 compute"的能力,必须在 RHI 接口层就提供 is_compute_available(),且渲染器有明确的两条构建路径(不是运行期 if)。这条如果做错,后期无法补救。
9. 性能预算与验证
每个 pass 在 MIR 里带 ms_budget,CI 自动跑基准场景,超支即 fail。
| Tier | 帧预算 33.3ms(30fps)分配 |
|---|---|
| 几何 + 剔除 | 4 ms |
| 主 pass(不透明) | 9 ms |
| 透明 / 毛发 | 5 ms |
| 光照 / GI | 4 ms |
| 后处理(TAA/超分) | 4 ms |
| 动画 + 模拟 | 3 ms |
| 逻辑 / 脚本 | 3 ms |
| 余量 | 1.3 ms |
硬规则:永远留 5% 余量。 没有余量的引擎在真实设备上必然掉帧,因为真机温度墙、后台占用、系统调度都不可控。
10. 小游戏渲染的现实底线(写在最前面,避免幻想)
- 主包 4MB:引擎最小运行时目标 ≤1.2MB(gzip 后)。这要求 L5+L4 极度精简,且 Tier0 必须裁掉 Tier3/4 的所有代码(编译期 feature gate,非运行期)。
- 首帧 3 秒:必须"占位资产先跑起来",不能等资源。
- 30fps 是目标不是底线:低端安卓实测 25fps 就要算失败,因为用户手机会更差。
- 发热:小游戏 iOS 用 Metal 后端可降功耗 15%(微信官方数据),这直接影响留存,所以 Tier1 后端要尽早接。
AI 三层、五角色拓扑、预算感知生成、AI Bake、确定性锁
03 · AI 内核(差异化核心)
"Cocos、Unity、Unreal 都是扩展 AI 能力,而我们从 0 开始开发,从 0 就融入 AI 的思考和推理。"
这份文档回答一个问题:"从 0 融入 AI" 在架构上到底意味着什么?
1. 三种 AI 定位,只有一种能形成护城河
| 定位 | 做法 | 天花板 | 谁在做 |
|---|---|---|---|
| A. 外挂助手 | 编辑器里加聊天面板,AI 给建议,人手动改 | 人的手速 | Cocos / Unity / UE 现状 |
| B. 工具期生成 | AI 生成贴图/模型/代码,导入后再人工整理 | 人的整理成本 | 各类 AIGC 工具 |
| C. 编译链一等公民 | AI 输出 MIR 变换,由编译器硬校验后落盘 | 验证器质量 | MagicEngine |
选 C。理由不是"更先进",而是 A 和 B 都有一个共同死穴:AI 的产出不进入可验证的中间层,所以质量不可控、不可复现、不可回滚,最终只能当"灵感工具"用。
C 的前提是引擎有一个能被 AI 精确读写、且能被机器验证的中间表示——这就是 MIR 存在的根本原因。MIR 不是为人类设计的,是为 Agent 设计的。
1.1 为什么成熟引擎做不了这件事
不是技术能力问题,是历史包袱 + 组织约束:
| 障碍 | 说明 |
|---|---|
| 资产格式不可精确编辑 | .uasset / .prefab+meta 是给人用的,AI 只能"猜",改一个字段可能破坏引用图 |
| 无预算概念 | 优化只能事后人肉做,AI 没有可优化的目标函数 |
| 跨端是"多后端"而非"多目标编译" | 平台差异散落在运行时 #ifdef,AI 无法在编译期消除 |
| 工作流已固化 | 数百万用户依赖现有操作方式,激进改动会引发迁移灾难 |
这四条构成了一个真实的窗口期:新引擎没有兼容负担,可以一开始就按"Agent 操作 IR"来设计。
2. 三层 AI(必须分开,否则成本失控)
| 层 | 时机 | 算力位置 | 典型任务 | 是否必需 |
|---|---|---|---|---|
| AI-1 工具期 | 编辑/构建时 | 云端大模型 | 关卡生成、材质生成、代码生成、风格评审 | 必需(P0) |
| AI-2 编译期 | 打包时 | 云端 + 本地 | MIR 自动优化(合批、LOD 策略、预算重分配) | 必需(P1) |
| AI-3 运行期 | 游戏运行时 | 设备 / 云端 | NPC 行为、动态叙事、程序化内容、AI 超分 | 可选(P2+) |
关键决策:AI-3 在 P0/P1 一律不做。 小游戏端没有算力,做运行期推理等于自杀。AI-3 的所有产出形态必须先被"烘焙"成数据(见 §5 AI Bake)。
3. Agent 拓扑
五个角色,职责严格分离。分离的目的不是"像人",而是让每个角色有独立的可验证目标。
┌─────────────┐
用户意图 ─────▶ │ Planner │ 拆解任务、分配预算、仲裁冲突
(自然语言/文档) └──────┬──────┘
│ Task DAG + Budget
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Builder │ │ Builder │ │ Builder │ 场景/材质/动画/逻辑
│ (scene) │ │ (material│ │ (anim) │ 各自只改自己领域的 MIR 子图
└────┬─────┘ └────┬─────┘ └────┬─────┘
└─────────────┼─────────────┘
▼ MIR diff
┌─────────────┐
│ Guardian │ 硬约束校验(编译期门禁,非建议)
└──────┬──────┘
▼ 通过 / 带诊断退回
┌─────────────┐
│ Critic │ 多模态评审:截图 vs 概念图 / 真实参考
└──────┬──────┘
▼ 评分 + 差异报告
┌─────────────┐
│ Optimizer │ 读 profiler → 产 MIR 优化 diff → A/B 验证
└─────────────┘
3.1 每个角色的输入输出契约
| 角色 | 输入 | 输出 | 禁止 |
|---|---|---|---|
| Planner | 用户意图 + 项目预算 + 现有 MIR 摘要 | Task DAG(带预算配额) | 禁止直接改 MIR |
| Builder | 单个 Task + 局部 MIR 视图 | MIR diff(patch) | 禁止跨领域改(材质 Builder 不能动场景层级) |
| Guardian | MIR diff | pass / reject(diagnostics) |
禁止"宽容放行",禁止用 LLM 判断硬约束 |
| Critic | 渲染截图 + 参考图 + 风格向量 | 0–100 分 + 具体差异描述 | 禁止无依据打分(必须指出差异区域) |
| Optimizer | profiler trace + 预算报告 | MIR 优化 diff + 预期收益 | 禁止无 A/B 验证就提交 |
Guardian 必须是确定性的代码,不是 LLM。 这是整个系统可信度的基石:硬约束(碰撞体闭合、预算超限、引用完整性、变体数)永远由代码判定。LLM 只做"软"判断(好不好看、符不符合风格)。
4. Agent 与引擎的唯一接口:三个 API
Agent 不调用引擎 API,不操作文件系统。它只有三个能力:
mir.query(selector) → 结构化子图 + 预算实测值
mir.patch(diff) → 提交变更(schema 校验 → Guardian → 落盘)
mir.measure(scope) → 运行基准场景,返回性能/内存/包体实测
为什么必须这么窄
| 好处 | 说明 |
|---|---|
| 可复现 | diff + seed + 模型版本 = 完全可重放 |
| 可 review | 所有 AI 改动都是文本 diff,像 code review 一样过 |
| 可回滚 | 一次 patch 就是一个 commit |
| 平台无关 | Agent 不需要知道目标是小游戏还是 PS5,预算信息在 MIR 里 |
| 可组合 | 多个 Builder 可以并行改不同子图,靠 diff 合并 |
反模式:给 Agent 一个
engine.createObject()之类的命令式 API。这样 AI 变成一个"手速极快的实习生",改动无法 review、无法合并、无法回滚。这是绝大多数"引擎 + AI"项目的失败原因。
5. AI Bake —— 让小游戏也能是"AI 引擎"
5.1 问题
小游戏运行环境:WASM + 单线程 + 无 NPU + 4MB 主包。任何运行期推理都不可能。
5.2 机制
工具期(云端) 编译期 运行期(设备)
───────────────── ───────────── ─────────────────
大模型推理 AI 结果编译为 零推理
↓ 确定性数据 只读数据
生成光照/导航/动画/ → 探针/LUT/骨骼/ → 直接采样
LOD/风格参数 纹理/表
具体可烘焙的 AI 产出:
| AI 产出 | 烘焙为 | 运行期开销 |
|---|---|---|
| 光照与反弹 | Lightmap + SH 探针 + DDGI 探针 | 采样成本(极低) |
| 场景布局 | 静态场景图 + 合批结果 | 零 |
| 毛发 grooming 方向场 | 3D 纹理(低分辨率) | 一次采样 |
| 动画重定向结果 | 骨骼动画片段 | 零 |
| LOD 层级与切换阈值 | Cluster 层级数据 | 距离比较 |
| 材质参数 | 常量缓冲 + 纹理 | 零 |
| NPC 行为策略 | 行为树 / 状态机 / 权重表 | 状态机求值 |
| 风格向量 | 项目级常量 | 零 |
5.3 这条机制的战略价值
它让"AI 引擎"和"低端设备"不再矛盾。竞品的 AI 能力在中高端设备上才能体现;ME 的 AI 能力在所有设备上都体现,因为 AI 的作用发生在构建时,而非运行时。
这是本架构最重要的一条洞察。
6. 预算感知生成(Budget-aware Generation)
6.1 常规做法的问题
生成 → 检查预算 → 超标 → 简化 → 再检查 → ...
这是"生成后裁剪",问题:可能生成的东西本身就是错的,简化救不回来;且迭代次数不可控。
6.2 ME 的做法:预算进入 Prompt 与搜索空间
Budget 作为生成请求的硬参数:
{
"intent": "雨夜的江南小巷,青石板泛光,两侧三米高老墙,巷宽2.5米,尽头一盏路灯",
"target": "wechat-android-lowend",
"budget": { "tri": 18000, "tex": 6MB, "draw": 40, "ms": 8.0 },
"style_lock": "project:yueju-wuxia@v3",
"seed": 29281
}
Builder 在生成时就在预算内构造(例如选择"贴图代替几何"、"烘焙光照代替动态光"),而不是生成完再砍。
6.3 预算借贷(Budget Arbitration)
需求天然冲突时(想加细节但超预算),由 Planner 仲裁:
- 从"余量池"借款(其他节点未用完的预算)
- 借贷必须记账,编译期必须归零
- 归零失败 → 编译失败(不是警告)
这让"预算"变成和内存一样的硬资源,而不是软目标。
7. 确定性锁(Determinism Lock)
没有确定性,AI 生成的工程不可维护。 这是很多 AI 工具的死穴。
每次生成记录三元组:
seed : 0x9f2a
model_version : <provider>@<model>@<version>
recipe_hash : sha256(完整输入上下文)
三者 + 输入快照 = 任何人可重跑出同一结果。要求:
- 模型调用必须 pinned 版本(不接受
latest) - 温度、top_p 等参数进 recipe
- 生成结果与 provenance 一起入库
me build --no-ai必须能在无网络下重建(用已缓存结果)- CI 定期重跑历史 recipe,检测模型漂移
第 2、5 条最容易被忽略,但它们是"三年后还能维护"的唯一保障。
8. 风格锁定(Style Lock)
AI 生成最大的工程问题不是"单个资产不好",而是"拼在一起不协调"。
机制:
- 项目级风格向量(来自参考图集,工具期提取一次,锁定)
- 每次生成产出的截图由 Critic 打分(与风格向量的相似度 + 具体维度:色彩/材质粗糙度/边缘硬度/光照对比)
- 低于阈值 → 拒绝并带诊断重生
- 风格向量变更 = 项目级 ADR,必须人工批准(防止风格漂移)
9. 一个完整的闭环(示例:做一个可上线的关卡)
[Day 1]
策划:"做一个雨夜江南小巷,玩家长按屏幕蓄力跳跃,躲避水洼里的陷阱"
Planner:
├─ 读项目 MIR 摘要 → 已知:2D 横版、剪纸风格、目标微信低端机
├─ 产 Task DAG:场景(40%) / 材质(25%) / 玩法(25%) / UI(10%)
└─ 分配预算:tri 18k / tex 6MB / draw 40 / 帧内 8ms
Builder(scene):
├─ 生成 MIR SceneGraph:巷道体块、青石板、墙、路灯、水洼
└─ 提交 diff
Guardian:
├─ ✗ 巷道宽度 1.8m,与"2.5m"意图不符 → reject(diag)
└─ ✗ 水洼数量 14 个,超出 draw 预算(每个独立材质)→ reject(diag)
Builder(scene) 修正 → 通过
Builder(material):
├─ 生成 MaterialGraph:湿石板(分层:基础→水膜→细节法线)
└─ 通过(能量守恒校验 OK)
Builder(anim/玩法):
├─ 生成蓄力跳跃的 BehaviorGraph + 参数曲线
└─ 通过
Critic:
├─ 截图 vs 参考图:78 分
└─ 差异:路灯辉光过硬、墙面缺少雨水流痕、整体偏冷
→ 回流给 Builder(material)
Optimizer(CI 自动跑):
├─ 低端机实测 11.2ms(超 3.2ms)
├─ 定位:透明水洼 overdraw 占 6.1ms
├─ 产 diff:水洼改用不透明 + 假反射(省 4.8ms),视觉差异 Critic 打分 -3 分
└─ 人 review 后合入 → 6.4ms ✓
[Day 3] 打包:主包 3.7MB,分包 12MB(CDN),首次进入 2.1s
这个闭环里,人只做了两件事:写意图、review diff。这就是"AI 进编译链"的实际体感。
10. 反模式清单(写下来防止走偏)
| 反模式 | 为什么错 |
|---|---|
| Agent 直接产二进制资产 | 不可 diff、不可 review、不可回滚 → 工程不可维护 |
| 用 LLM 判硬约束 | 幻觉 → 放出坏包 → 线上事故 |
| 运行期跑大模型 | 小游戏端直接死刑 |
用 latest 模型版本 |
三个月后无法复现,等于技术债爆炸 |
| AI 一次生成整关 | 上下文超限 + 预算失控;必须 Task DAG 分解 |
| 无 Critic 直接入库 | 单个资产"能看",拼起来"难看",返工成本极高 |
| 让 AI 决定架构 | AI 优化局部,架构必须人定 |
11. 成本模型(必须算,否则不可持续)
AI 生成的成本对标是美术/策划工时,不是 token 价格。
计算方式(每关卡):
AI 成本 = (生成 token + 重试 token + Critic 视觉调用) × 单价
人力成本 = 节省的美术工时 × 时薪 + 返工工时 × 时薪
经验规律(需在 P0 实测校准): - 一次性资产(场景/关卡):AI 优势最大,因为避免了"沟通成本" - 高频复用资产(角色/UI 组件):AI 优势小,因为人做一次能用一百次 - 需要 IP 一致性的资产(主角/品牌元素):AI 优势最小,因为风格锁定最难
结论:AI 优先攻"一次性、低复用、高沟通成本"的内容——这正是关卡与场景,而不是角色。 这个判断直接影响 P0 的切入点选择。
12. 风险
| 风险 | 影响 | 缓解 |
|---|---|---|
| AI 生成质量不达"可上线" | 引擎价值主张崩塌 | P0 就用真实商业项目验证;提前准备"人工兜底工作流" |
| 模型能力/价格突变 | 成本模型失效 | 模型适配层抽象;关键能力要求可本地化(小模型蒸馏) |
| 上下文长度限制 | 大关卡无法一次生成 | Task DAG + 局部视图查询(已在架构里) |
| 版权与合规 | 商用风险 | 生成资产溯源链 + 商用模型白名单 + 人工审核门 |
| 团队不会用 | 采纳失败 | 编辑器必须"AI 可选、手动可用",不能强绑 |
同源原则、MIR 协作模型、平台矩阵、主机门槛
04 · 工具链与平台矩阵
"完善的工具链,加速游戏的开发流程。"
工具链不是引擎的附属品,工具链是引擎产品的本体。开发者日常 90% 的时间在编辑器里,不在引擎源码里。
1. 工具链设计的第一原则:同源
编辑器与运行时必须是同一份代码。
| 引擎 | 编辑器渲染 | 运行时渲染 | 结果 |
|---|---|---|---|
| Unreal | Slate + 编辑器视口 | 独立管线 | WYSIWYG 永远差一点,需"Standalone 再调" |
| Unity | 编辑器视口(近似) | 独立管线 | 类似问题 |
| ME | 同一个 Renderer,离屏渲染到编辑器纹理 | 同一份 | 编辑器里看到的 = 玩家看到的 |
实现方式:引擎作为库(libme),编辑器进程内嵌引擎实例,渲染到共享纹理(Metal/Vulkan 外部纹理 → 编辑器 UI 合成)。不做两套渲染路径。
代价:引擎必须能在"编辑器模式"下运行(可暂停、可单步、可回滚),这对 ECS 与资源生命周期提出更高要求。这个代价必须付。
2. Magic Studio(编辑器)
2.1 技术选型
| 层 | 选择 | 理由 |
|---|---|---|
| UI 框架 | Tauri + React + TypeScript | 包体小(比 Electron 小一个数量级)、内存低、招人容易 |
| 编辑器服务 | Rust(me-studio) |
与引擎同语言,零序列化开销;可处理大 MIR |
| 通 信 | 本地 IPC(MessagePack) | 比 JSON 快,比 gRPC 轻 |
| 视口 | 引擎离屏渲染 → 外部纹理句柄 | 见 §1 |
| 图谱编辑器 | 自研 Canvas/WebGL 渲染(不用 DOM 节点) | 大图(万级节点)性能 |
为什么不用 Qt/C++:编辑器 UI 的迭代速度决定产品生死,C++ UI 的迭代速度是 TS 的 1/5。UE 的 Slate 是它最难改的部分。
2.2 核心模块
| 模块 | 职责 | 优先级 |
|---|---|---|
| Scene Editor | 场景搭建、层级、变换、组件 | P0 |
| Inspector | MIR 节点属性编辑(所见即 MIR) | P0 |
| Material Editor | 分层材质图编辑 + 实时预览 | P1 |
| Anim Editor | 状态机、混合树、Motion Matching 配置 | P1 |
| Behavior Editor | 可视化逻辑(编译为 WASM) | P1 |
| AI Workbench | 意图输入、Task DAG 进度、diff review、Critic 报告 | P0 |
| Perf Workbench | 时间轴 profiler、预算热力图、优化建议 | P1 |
| Asset Browser | 资产库、依赖图、引用完整性 | P0 |
| Build & Publish | 分包预览、包体分析、一键发布 | P0 |
2.3 AI Workbench 是人机协作的核心界面
这个面板的设计决定"AI 引擎"是否好用。三个必须的视图:
- 意图输入 + 预算设定:自然语言 + 可视化预算滑块(三角面、内存、包体)
- Diff Review:像 GitHub PR 一样,左侧 MIR 文本 diff,右侧实时渲染对比(生成前 / 生成后 / 参考图)
- 诊断面板:Guardian 的 reject 原因 + Critic 的差异区域(在图上高亮)
关键交互原则:AI 的每一步都可"退回一步"。 不是"重新生成"(成本高、结果不可控),而是"撤销这一次 patch"。
3. 协作模型:MIR 是文本,所以 git 可用
这是相对 Unity/UE 的结构性优势。
| 场景 | Unity | Unreal | ME |
|---|---|---|---|
| Prefab/场景合并 | 需锁文件 / 第三方工具,冲突频发 | 二进制,必须锁 | 文本 diff 自动合并 |
| Code Review | 代码可以,资产不行 | 同 | 资产也能 review |
| 分支开发 | 场景不敢开分支 | 同 | 正常 git flow |
| CI 校验 | 有限 | 有限 | 全量 MIR 校验 + 预算回归 |
配套要求:
- MIR 文本格式必须稳定排序(节点按 id 排序,属性按 schema 顺序),否则 diff 噪声大
- 提供
me fmt(格式化,类似 gofmt) - 提供
me merge(结构化三方合并,比行级合并聪明)
这条如果做扎实,"多人协作体验"就是可感知的产品优势,成本却很低。
4. 资产管线
导入 → 净化 → 规范化 → MIR 化 → 编译 → 打包
| 阶段 | 做什么 | AI 介入点 |
|---|---|---|
| 导入 | 支持 glTF/FBX/OBJ/PNG/EXR/WAV 等 | — |
| 净化 | 去重、合并材质、修正法线、重算切线 | AI 判断"哪些是重复材质" |
| 规范化 | 单位/坐标系/命名统一 | — |
| MIR 化 | 转为 MIR 节点(不是二进制 blob) | AI 补全材质层、生成 LOD 策略 |
| 编译 | 平台特化(纹理压缩、着色器变体、LOD 烘焙) | — |
| 打包 | 分包切分、寻址、压缩 | AI 做"分包图划分"建议 |
关键:资产导入是幂等的、可重放的。 同一源文件 + 同一 pipeline 版本 → 同一 MIR。CI 校验这条,防止"某人的机器上能跑"。
5. 热重载(三级)
| 级别 | 内容 | 目标延迟 | 状态保留 |
|---|---|---|---|
| L1 | 材质参数、光照参数、数值配置 | <50ms | 完全保留 |
| L2 | MIR 结构变更(增删节点、改层级) | <300ms | 保留(实体按 id 映射) |
| L3 | 代码(BehaviorGraph / 脚本) | <1s | 保留(WASM 模块热替换 + 状态序列化) |
L3 是最难的:需要 WASM 模块级热替换 + ECS 状态迁移。做法: - 脚本编译为独立 WASM 模块,模块间通过稳定 ABI 通信 - 热替换时序列化组件数据 → 卸载旧模块 → 加载新模块 → 反序列化 - 组件 schema 变更时走"迁移函数"(AI 可生成迁移代码)
目标体感:改一行脚本,游戏不中断,0.5 秒后行为变了。
6. 性能工作台
传统 profiler 只告诉你"哪慢"。ME 的目标是"哪慢 + 为什么 + 怎么改 + 改了会怎样"。
| 视图 | 内容 |
|---|---|
| 时间轴 | CPU 主线程 / Job 线程 / GPU 队列,三层对齐 |
| 预算热力图 | 场景里哪些节点超预算,直接高亮在 3D 视口 |
| 归因 | 一个 draw call 的成本,追溯到 MIR 节点链 |
| 建议 | Optimizer 产出的 diff + 预期收益 + Critic 画质影响评分 |
| 回归 | 与上个版本对比,新增的耗时归因到 commit |
真机调试:小游戏端无法本地 profile,需要"远程调试隧道"(WebSocket 推送 trace 到编辑器)。这是必须做的基建,P0 就要有。
7. 平台矩阵
7.1 小游戏(第一战场)
| 平台 | 主包 | 总包 | 技术约束 | 我们的策略 |
|---|---|---|---|---|
| 微信 | ≤4MB | ≤30MB(普通分包不限大小,可 CDN) | WASM 单线程为主;Android=WebGL2;iOS 已有原生 Metal 后端(compute/indirect/UAV 可用) | 主目标。Tier0 + Tier1 双路径 |
| 抖音 | ≤20MB(无分包)/ 4MB(有分包) | ≤20MB(IAP ≤30MB) | 类似微信,包体更宽松 | 第二目标,复用编译链,仅换 PAL |
| 快手 / vivo / OPPO / 小米 / 荣耀 | 4–6MB | 20–30MB | 各家 API 差异大 | P2 后按商务需要接入 |
微信的两个关键事实(决定架构):
- iOS 侧有原生 Metal 后端 → 可以做 GPU driven、compute 粒子、大规模植被。这意味着"高端小游戏"是一条真实的产品线,不是幻想。
- Android 侧仍是 WebGL2 → 必须有无 compute 的退化路径。同一款游戏在两个平台要用不同的 lower 结果,这对编译链是硬要求。
7.2 移动原生
| 平台 | 图形 API | 关键点 |
|---|---|---|
| iOS | Metal 3 | 统一内存、TBDR、ProMotion 120Hz |
| Android | Vulkan 1.1+ / GLES3.2 回退 | 碎片化严重,必须有设备分级表 |
设备分级:维护一份"设备能力数据库"(GPU 型号 → Tier 映射),安装包首次启动上报,服务端下发画质档位。这是移动端的标配。
7.3 PC
| 平台 | API |
|---|---|
| Windows | D3D12(主)/ Vulkan(备) |
| macOS | Metal |
| Linux | Vulkan |
PC 端要支持动态分辨率 + 帧生成 + 超分(FSR/XeSS/DLSS 三选一或抽象层)。这里有个取舍:自研超分 vs 集成,见 ADR-005。
7.4 主机(最远,但要现在就留口)
| 平台 | 门槛 | 工作量 |
|---|---|---|
| Xbox | ID@Xbox 计划,相对最容易申请 | GDK(D3D12X)+ 认证(XR 要求) |
| PlayStation 5 | 索尼开发者账号 + 公司资质审核 + devkit 采购 + NDA | GNM/GNMX(C++)+ TRC 认证 |
| Switch / Switch2 | 任天堂开发者门户,审核较严 | NVN(C++)+ lotcheck |
现实提醒(必须写进计划):
- 主机 SDK 全部是 C++,且受 NDA 保护,代码不能上公有云、不能给外包看
- 认证(TRC/TCR/lotcheck)是独立的 3–6 个月工作,且第一次做几乎必然反复失败
- 主机移植的工作量约等于"重做一个渲染后端 + 全套平台服务",不是"改个编译目标"
- 中国团队拿 PS5 devkit 的周期不可控,这是商务风险不是技术风险
架构结论:主机支持在架构上"留口"(RHI 的 C ABI 边界、PAL 的平台服务抽象),但不在 P2 之前投入实现。详见 ADR-001。
8. 构建与发布管线
me build --target=wechat-ios # Tier1(Metal)
me build --target=wechat-android # Tier0(WebGL2)
me build --target=android # Vulkan
me build --target=win # D3D12
me build --target=ps5 # GNM(NDA 环境)
每次构建自动执行:
| 检查 | 失败后果 |
|---|---|
| MIR 往返一致性 | 编译失败 |
| 变体数预算 | 编译失败 |
| 三角形/纹理/内存预算 | 编译失败 |
| 主包 4MB 约束 | 编译失败(并给出可分包建议) |
| 引用完整性(无悬空引用) | 编译失败 |
| 确定性(同 seed 同产物) | 编译失败 |
| 真机基准(异步,不阻塞) | 报警 + 阻断发布 |
设计原则:所有能在编译期发现的问题,绝不留给运行期。 编译失败比线上闪退便宜 1000 倍。
9. 工具链的优先级(P0 必须有 vs 可以后补)
| P0 必须有 | 可以后补 |
|---|---|
| MIR 文本编辑 + diff | 可视化材质图(P1) |
| 场景搭建(基础) | 动画编辑器(P1) |
| AI Workbench(意图 + diff review) | Motion Matching 可视化(P2) |
| 一键构建到微信 + 包体分析 | 多平台并行构建农场(P2) |
| 真机远程 profile | 协作光标(P2) |
| 资产导入 + MIR 化 | 插件市场(P3) |
P0 的判断标准:能做出并上线一款真实游戏的最小工具集。 凡是"让工具更爽"但"不阻塞上线"的,全部推后。
P0-P3 分期、决策门、风险登记册
05 · 路线图、组织与风险
这一份最重要的作用不是"规划未来",而是让你能判断什么时候该停。
1. 总原则
引擎不能"先做完再找游戏"。 必须在有真实项目喂的前提下迭代,否则做出来的是"想象中的需求"。
所以路线图的形状是:每个阶段都以"一款能上线的游戏"为交付物,引擎是副产品。
P0 ──────▶ P1 ──────▶ P2 ──────▶ P3
2D小游戏 3D移动 PC/主机预研 3A管线
(6个月) (12个月) (18个月) (长期)
2. P0:2D/2.5D 小游戏垂直切片(0–6 个月)
目标
上线一款真实的微信小游戏,主包 ≤4MB,低端安卓 30fps 稳定,全程不手写 shader。
这个目标看似"低",但它同时验证了引擎最难的四个假设:
| 假设 | 验证方式 |
|---|---|
| H1:WASM-first 架构能塞进 4MB 主包 | 实测包体 |
| H2:MIR 能被 Agent 正确生成并 review | 关卡内容 AI 生成占比 >50% |
| H3:预算编译能保证真机性能 | 低端机 benchmark 通过 |
| H4:AI Bake 闭环真的省人力 | 对比手作工时的实测数据 |
H2 和 H4 如果 P0 就失败,说明整个方向错了,应立即止损。 这是最重要的决策门。
交付清单
| 模块 | P0 范围 |
|---|---|
me-core |
数学库、句柄、错误、Arena 分配器、日志 |
me-mir |
SceneGraph + MaterialGraph(简) + 文本/二进制双形态 + 校验器 |
me-compiler |
parse → legalize(Tier0) → budget → link(分包) → pack(微信) |
me-runtime |
Archetype ECS、2D/2.5D 渲染、输入、音频、资源流式 |
me-rhi |
仅 WebGL2 后端 |
me-pal |
仅微信(FS/网络/支付/广告/账号) |
me-agent |
Planner + Builder(scene) + Guardian(不含 Critic/Optimizer) |
| Magic Studio | 场景树、Inspector、AI 面板、构建发布、包体分析 |
| 工具基建 | MIR 往返校验、me fmt、me merge、真机远程 profile |
刻意不做(P0 砍掉的东西)
3D、蒙皮动画、物理(2D 用简单 AABB)、阴影、后处理、材质图编辑器、抖音、Metal 后端、Critic、Optimizer、多人在线。
砍掉清单比保留清单更重要。 P0 里每多加一项,上线时间就往后推一个月。
开发方式的一个加成
引擎代码里 70% 是"A 类代码"(样板多、模式清晰、可测试),非常适合 AI 辅助生成;只有 30% 是"B 类代码"(性能关键路径、平台适配、架构决策)必须人写。
这意味着 P0 用 3 人(1 架构 + 1 引擎 + 1 工具/TA)+ AI 辅助是可行的,而不是行业中常见的 8–10 人。这本身就是对"AI 重构引擎开发"的第一个证明。
3. P1:3D 移动端(6–18 个月)
目标
同一款游戏的 3D 版本上线微信 + 抖音双端;AI 生成的关卡占比 >50% 且通过 review。
交付
| 模块 | 内容 |
|---|---|
| 渲染 | Clustered Forward+、PBR、级联阴影、后处理链、TAA、超分 |
| RHI | 新增 Metal 后端(Tier1,解锁 compute 路径) |
| 动画 | 蒙皮、状态机、混合树、根运动 |
| 物理 | 集成 Jolt(3D 刚体)+ 自研角色控制器 |
| 材质 | MaterialGraph 编辑器 + 分层材质 + 着色器生成器 |
| MIR | v1:完整预算注解 + provenance + 变更迁移 |
| Agent | + Critic、+ Optimizer(Perf Agent 闭环上线) |
| 平台 | 抖音小游戏;Android/iOS 原生 |
| 编辑 器 | 材质编辑、动画编辑、Perf Workbench |
关键里程碑
- M6:Tier1(Metal/compute)路径跑通,能跑 GPU 粒子
- M9:Perf Agent 第一次自动产出"被采纳"的优化 diff
- M12:抖音端上线
- M15:MIR schema v1 冻结(此后变更需迁移脚本)
MIR schema 冻结是很重要的时间点。 之前可以随便改,之后每次改都要写迁移,成本陡增。
4. P2:PC 与高端渲染(18–36 个月)
目标
同一份 MIR 编译出 PC 包,画质与移动端拉开明显差距;完成主机移植的技术验证。
交付
| 模块 | 内容 |
|---|---|
| 几何 | Virtual Geometry(先 Tier1 软件光栅,再 Tier3 mesh shader) |
| 光照 | SDF-DDGI → HWRT-DDGI、屏幕空间反射、体积雾 |
| 角色 | Character Stack(皮肤 SSS、毛发 Shell+Strand、眼球、布料) |
| RHI | D3D12、Vulkan |
| 平台 | Windows / macOS |
| 工具 | 资产管线成熟、构建农场、多人协作 |
| 主机 | 申请 devkit(Xbox ID@Xbox 先行),完成一次 RHI 移植冒烟 |
关键判断
P2 才是"逼真"真正开始的阶段。 P0/P1 的渲染是"够用",P2 才谈"写实"。如果 P2 之前就在追画质,会拖死整个项目。
5. P3:3A 与商业化(36 个月+)
- 主机正式支持(PS5 / Xbox / Switch)
- 大世界流式加载、影视级角色管线
- 运行时 AI(NPC 行为、动态叙事)
- 商业化:引擎授权 / 云烘焙服务 / 垂直行业方案
6. 人力与成本(量级参考)
| 阶段 | 全职等效人力 | 角色构成 |
|---|---|---|
| P0 | 3–5 人 | 1 架构、1–2 引擎、1 工具/TA、0.5 策划 |
| P1 | 8–15 人 | +渲染 2、+动画 1、+平台 1、+工具 2、+美术 1 |
| P2 | 25–40 人 | +渲染 4、+角色 3、+主机 3、+QA 3、+美术 5 |
| P3 | 50+ 人 | 完整引擎团队 |
资金量级(一线城市,含社保与设备):P0 约 150–300 万元,P1 约 800–1500 万元/年,P2 约 2500–4000 万元/年。
这些数字是让你判断可行性用的,不是预算书。如果 P0 的 300 万拿不出来,那就应该把 P0 缩到"一个人 + 一款极小游戏",而不是砍架构。
7. 风险登记册
按"影响 × 概率"排序。R1 是唯一的项目级风险,其他都是执行风险。
| ID | 风险 | 概率 | 影响 | 缓解 | 触发止损条件 |
|---|---|---|---|---|---|
| R1 | AI 生成质量达不到"可上线" | 中高 | 致命 | P0 用真实项目验证;保留人工兜底工作流;优先攻"一次性内容"(关卡/场景)而非"高一致性内容"(主角/IP) | P0 结束时 AI 生成内容需人工重做 >60% → 停止 AI 主线,退化为"AI 辅助的传统引擎" |
| R2 | 范围爆炸(想做的太多) | 高 | 高 | 每阶段硬性"不做清单";决策门评审 | 任一阶段超期 50% → 强制砍范围 |
| R3 | 小团队产能不足 | 高 | 高 | AI 辅助写引擎;不做全平台;P0 只做 2D | 连续 2 个里程碑延期 → 缩到单人 MVP |
| R4 | 主机资质拿不到 / 周期不可控 | 中 | 中(延后) | 架构留口不实现;Xbox ID@Xbox 先行 | 不影响 P0–P2,仅影响 P3 时间 |
| R5 | 模型成本/能力突变、供应商锁定 | 中 | 中 | 模型适配层;关键能力要求可蒸馏到本地小模型 | 单关卡 AI 成本 > 人力成本 50% → 改用本地小模型 |
| R6 | 与 Cocos 竞争(免费+成熟+小游戏强势) | 高 | 高 | 差异化只能是"AI 带来的效率提升",必须可量化;先自用不对外卖 | P0 无法证明效率优势 → 引擎转为内部工具,不对外 |
| R7 | 引擎不回本(只有 1 个项目用) | 高 | 中 | 计划中必须有 ≥3 个项目的路径 | 2 年内第 2 个项目未启动 → 降级为框架库 |
| R8 | MIR schema 演进失控 | 中 | 高 | P1 末冻结 + 强制迁移脚本 + 版本协商 | schema 变更导致 >1 周返工 → 冻结窗口 |
| R9 | 引擎人才招不到/留不住 | 中 | 高 | 核心 2 人必须稳定;其余靠 AI + 文档 | 核心成员流失 → 暂停扩张 |
| R10 | 小游戏平台政策变化(包体/分成/审核) | 中 | 中 | 平台抽象层隔离;不押单一平台 | 主平台政策不利 → 切抖音/快手 |
R6 值得单独说
小游戏引擎市场已经有 Cocos(免费、成熟、微信生态深度绑定)、Laya、Egret,还有 Unity 的中国特供版。"我做了个新引擎"本身不是卖点。
唯一可能的卖点是:用 MagicEngine 做一款同类游戏,工期是 Cocos 的 1/2,或者同等工期下品质高一档。 如果这个不能被实测数据证明,那么 MagicEngine 的正确归宿是"悦居自用的生产工具",而不是"对外销售的引擎产品"。
这两个定位的架构是不同的(自用可以不管插件生态、文档、向后兼容;对外必须管)。建议 P0/P1 按"自用"设计,P2 再决定是否产品化。 这是一个可逆决策——反过来(先按产品做)不可逆,会拖死项目。
8. 决策门(Go / No-Go)
每个阶段结束必须开一次评审,用数据说话,不用感觉说话。
Gate 0(第 6 个月)
| 指标 | 通过线 |
|---|---|
| 游戏上线 | 微信小游戏真实上线,有真实用户 |
| 主包体积 | ≤4MB |
| 真机性能 | 低端安卓(骁龙 6 系及以下)30fps,帧时间 p95 ≤33ms |
| AI 生成占比 | 关卡内容 ≥50% 由 AI 生成,人工 review 通过率 ≥80% |
| 效率对比 | 同等内容量,工时 ≤ Cocos 路线的 70% |
| 无手写 shader | 全程零手写 GLSL |
6 项中 ≥4 项通过 → Go;3 项 → 有条件 Go(缩范围);≤2 项 → 重新评估。
Gate 1(第 18 个月)
3D 双端上线、Perf Agent 产出的优化被采纳率 ≥30%、MIR v1 冻结、第 2 个项目启动。
Gate 2(第 36 个月)
PC 端上线、主机移植冒烟通过、外部团队能独立使用(第 3 个项目)。
9. 第一个项目建议
用自己的产品做第一个项目。 理由:
- 不依赖外部客户,节奏自主
- 需求真实(不是"为了验证引擎而造的需求")
- 失败不外泄
- 成功即样板(对外最好的宣传是"我们自己用它做了上线产品")
候选:悦居的文旅/适老化场景做成轻量互动体验,或现有小程序的游戏化版本。关键是:必须是"能上线、有真实用户、有留存指标"的产品,不能是 Demo。
10. 一句话总结
这个项目的成败不取决于渲染有多强,而取决于 P0 那 6 个月里,AI 生成的内容能不能真的省下人力。 能,则后面所有阶段都是"加能力";不能,则越往后投入越亏。 所以 P0 的设计目标不是"做出引擎",而是"用最小成本验证这个假设"。
复用梯度、双索引检索、世界语法、构件化生成、规范化闸门、冷启动
06 · 资产智能与复用架构
这一篇回答:AI 智能体如何积累、检索、适配、装配资产,以及"描述一个世界就生成一个世界"在架构上到底怎么成立。
1. 先把"复用"拆开——三个层次,价值差 10 倍
大多数人说"复用",想的是第一层。真正有价值的是第三层。
| 层次 | 动作 | 人工成本 | 价值 |
|---|---|---|---|
| L1 复制 | 找到 → 拿走 → 改路径 | 低 | 低(等于多存了一份) |
| L2 适配 | 找到 → 改参数/接口/规格 → 接入 | 中 | 中 |
| L3 组合 | 找到构件 → 按规则装配成新东西 | 低 | 高 |
为什么 L3 才是终局:
- L1 的问题是"复用了但用不上"——资产规格和项目不匹配,改起来比重做还慢
- L2 解决了规格,但每次都要人参与
- L3 把"资产"降级为"构件",把"复用"升级为"装配"。这样同一个墙体构件可以出现在 100 栋房子里,而 100 栋房子都不是"复制"出来的
架构结论:资产库的核心不是"仓库",是"构件化 + 装配规则"。 这一条决定了后面所有设计。
2. 复用率梯度——投入优先级由它决定
不是所有资产都值得投入。按品类给出真实梯度:
| 品类 | 复用率 | 为什么 | 投入优先级 |
|---|---|---|---|
| 音效 / 音景 | 90%+ | 事件级触发,不依赖上下文;可参数化(pitch/filter/volume)产生变体 | 最高 |
| UI / 图标 | 80% | 纯功能性,与世界观弱相关 | 高 |
| 材质 / 贴图 | 70% | 可跨项目复用(砖、木、金属、布料) | 高 |
| 系统代码 | 60% | 输入、存档、相机、寻路、设置、本地化——每个游戏都要 | 高 |
| 道具 / 建筑构件 | 45% | 需适配风格,但构件级可复用好 | 中 |
| 场景布局 / 关卡 | 20% | 关卡是设计,不是资源 | 低 |
| 角色 / IP 形象 | 10% | 角色是识别度,必须专属 | 极低 |
三条结论:
- 音效优先做,你的判断是对的。 它是"投入产出比"最高的品类:体量小、检索容易、参数化空间大、跨品类通用(一个关门声能用在 50 个游戏里)。
- 代码要按"特性包"而不是"文件"复用(见 §5)。
- 关卡和角色不要指望复用。 这两块的价值在"独特性",用库反而会拖累品质。架构上不要为它们设计复用机制,那是浪费。
3. 资产库的架构——它不是一个搜索引擎
┌──────────────────────────────────────────────────────────┐
│ Asset Vault(资产仓) │
│ 内容寻址存储 · 版本 · 引用计数 · 增量同步 │
├──────────────────────────────────────────────────────────┤
│ Semantic Index(双索引)★ │
│ ① 向量索引:语义相近("美式乡村木屋" ≈ "木结构农舍") │
│ ② 结构索引:规格关系(三角面/骨骼数/UV/材质槽/API 签名) │
├──────────────────────────────────────────────────────────┤
│ Adapter Layer(适配器)★ │
│ 规格转换 · 接口适配 · 风格迁移 · LOD 重生成 │
├──────────────────────────────────────────────────────────┤
│ Provenance Ledger(授权与溯源账本) │
│ 来源 · 许可 · 生成链路 · 可商用性 │
└──────────────────────────────────────────────────────────┘
3.1 为什么必须是双索引
单靠向量检索必然失败。 因为美术资产的典型失败模式是:
"这个椅子看起来完全对,但它有 12 万面、UV 是一团乱、朝向是 Y-up、单位是英寸。"
向量索引只会说"很像"。它不知道这资产用不了。
所以必须有第二套索引,索引的是结构属性:
| 资产类型 | 结构索引字段 |
|---|---|
| 静态网格 | 三角面数、UV 通道、LOD 层级、包围盒、朝向/单位、材质槽数量 |
| 骨骼动画 | 骨骼数量、命名规范(Mixamo/UE/自定义)、帧率、根运动有无、循环性 |
| 材质 | 分层结构、贴图分辨率、PBR 通道完整性、tiling 模式 |
| 音频 | 时长、采样率、声道、循环点、BPM、情绪标签、事件类型 |
| 代码特性包 | 依赖列表、接口签名、平台要求、MIR 依赖、测试覆盖 |
检索时两个索引同时用:向量负责"像不像",结构负责"能不能用"。两个都过,才进候选。
3.2 检索不是一次,是一个循环
query
→ 粗排(向量 top-100)
→ 精排(结构兼容性打分)
→ 适配(Adapter 改规格)
→ 硬校验(Guardian:预算/引用/许可)
→ 接受 / 退回(带诊断,重新检索)
关键设计:适配器是必备的,不是可选的。 世界上的资产永远不会恰好匹配你的项目规格。
4. 代码复用——靠契约,不靠相似度
代码和美术的复用机制完全不同:
- 美术:靠"看起来对 + 规格能用"
- 代码:靠"接口契约 + 依赖声明 + 测试"
用向量检索找代码是错的("这段代码和那段代码很像"——像不代表能替换)。
4.1 Feature Package(特性包):复用的最小单位
packages/
third-person-camera/
manifest.mir-pkg # 依赖、平台要求、MIR 版本约束
src/ # 实现(Rust/WASM)
mir/ # 配套的 MIR 片段(预制体、配置)
api.md # 接口契约(输入/输出/生命周期)
tests/ # ★ 必须自带测试
budget.json # 预算画像(CPU/内存/包体)
硬规则:没有测试的特性包不允许入库。 因为 AI 会大量组合这些包,一个没有测试的包被组合 50 次,就有 50 个隐患。
4.2 哪些代码适合做特性包
| 类型 | 适合度 | 例子 |
|---|---|---|
| 基础设施 | ★★★ | 输入映射、存档、设置、本地化、相机、音频管理 |
| 系统机制 | ★★★ | 寻路、对话系统、任务系统、背包、属性系统 |
| 品类模板 | ★★ | 模拟经营的"时间/经济/建造"三件套;RPG 的"战斗/成长/掉落" |
| 具体玩法 | ★ | 玩法是设计,复用会毁掉差异 |
你说的"模拟游戏、物理游戏、RPG 不用每次从 0 写",正确的落地方式是:
不是复用"一个 RPG",而是复用 20 个 RPG 特性包(对话、任务、战斗回合、掉落表、装备、属性、存档、商店……),再用 MIR 装配成你的游戏。
装配层才是差异化,包本身是公共设施。
5. 音效为什么最值得做(展开)
音效的可复用性来自四个物理属性:
- 事件级:不依赖上下文。"门关上"这个事件在任何游戏里都一样
- 可参数化:一个 0.8 秒的木门声,通过 pitch/低通/音量/叠加,能产生 10+ 个变体
- 体量小:一个音效 50KB,一个模型 50MB——库的成本差 1000 倍
- 描述性强:音频可以用语言精确描述("潮湿的、木头、短促、带金属扣声"),生成模型天然适配
架构:Sound Palette(音景包)
一个"场景的声音配方",而不是一堆散装音频:
palette: chinese-village-dawn
ambience: 远处鸡鸣 + 溪流 + 微风树叶
one_shots:
木门开: [door_wood_01, door_wood_02(pitch+2)]
脚步_石板: [step_stone_01..04]
水桶落井: bucket_well_01
reverb: small_outdoor_rural
这样"中国乡村"这个世界自带一套声音,换场景 = 换 palette,不是逐个挑音频。
AI 介入点
- 从参考音频检索相似(音频也有 embedding)
- 生成变体(同一事件需要 4–6 个变体避免"复读机感")
- 自动匹配时长与循环点(这是人工最费时的部分)
6. 世界构建:世界语法(World Grammar)★
这是我认为整个资产体系里最有价值、也最容易被忽略的东西。
6.1 核心判断
一个"世界"不是资产集合,是"词典 + 语法"。
- 词典 = 构件库(房子、树、路、道具、材质)
- 语法 = 布局规则(拓扑、密度、动线、风格约束)
你说"一个美国小镇",真正的知识不是"有房子",而是:
美式小镇(1950s 中西部)=
主街(Main Street):宽 12m,两侧连续商铺,雨棚统一,停车斜列
+ 十字支路:每 200m 一条,尽端是住宅区
+ 地标:教堂(尖顶,社区最高点)+ 加油站 + 杂货店 + diner
+ 住宅区:独栋,前院 8m,车库朝街,白色栅栏
+ 基础设施:电线杆沿街(这是美式小镇最强的视觉符号)
+ 声音:远处火车 + 蟋蟀 + 教堂钟
+ 色彩:褪色红砖 / 米白木板 / 铬黄招牌
这段"语法"比 100 个模型更值钱,因为它能生成无限多个不重复的"美国小镇"。
6.2 对比:中国乡村 / 高端别墅内景
| 世界类型 | 语法要点(决定"像不像"的其实是这些) |
|---|---|
| 中国乡村 | 院落围合(不是开放草坪);晒谷场/水塘是公共空间核心;田埂分割;屋脊方向;柴垛/农具/腌菜缸;声音是鸡犬+广播 |
| 高端别墅内景 | 动线(玄关→客厅→餐厅→厨房);层高与开间比例;材质档次梯度(大理石/木饰面/金属);灯光层次(基础/重点/氛围三层);家具品牌风格一致性 |
注意:让场景"像中国乡村"的,不是房子的模型,而是"院落围合 + 晒谷场 + 柴垛"这些语法元素。 这也是为什么"从库里随便拿几个中式房子拼一拼"一定不像。
6.3 语法在架构里怎么表达
World Grammar 是 MIR 的一种特殊图(不是代码,是声明式的规则图):
world_grammar "us-town-1950" {
requires: [street_grid, building_shop, building_house, prop_pole, soundscape_rural]
layout {
grid: main_street(width=12m) + cross_street(every=200m)
zone: commercial(front=main_street, depth=30m) | residential(behind)
landmark: church{placement: highest_point, max_height: 25m}
density: {commercial: 0.7, residential: 0.4}
}
style_lock: "us-midwest-faded@v1"
budget: { tri: 180k, tex: 24MB, draw: 220, ms: 8 }
}
编译器(或 Agent)执行它 → 产出一个具体的 SceneGraph。
关键:语法是可积累的知识资产,且可以版本化、可以 A/B 测试(哪种布局玩家更喜欢)。
7. "我要一个房子" —— 构件化生成,不是整体生成
你说:"我需要一个房子的模型,先生成 2D 效果图,然后创建 3D 模型。"
这个流程方向对,但有一个关键岔路要说清:
7.1 不要生成"一个完整的房子模型"
| 做法 | 结果 |
|---|---|
| ❌ 整体生成一个房子 mesh | 面数不可控、无法换风格、无法 LOD、只能用在一次 |
| ✅ 生成"墙体/屋顶/门窗/材质 + 装配规则" | 面数可控、换材质即换风格、可 LOD、同套构件做 N 栋房 |
这是建筑业的"构件化"(prefab)思路,也必须是游戏场景生产的正确姿势。
7.2 正确的三段流水线
① 2D 概念图(人 review 这一层)
文生图 + ControlNet/LoRA 锁定风格与构图
→ 得到"这个房子长什么样"的共识
→ 人工确认:比例、风格、光照意图
↓
② 构件生成(AI)
图 → 构件级 3D(墙体/屋顶/门窗/装饰)
不是"图 → 整栋房子"
↓
③ 规范化闸门(Asset Normalizer)★
重拓扑 → UV 展开 → 单位/朝向统一 → LOD 生成
→ 材质烘焙 → 碰撞体生成 → 入库
② 之所以按构件生成,是因为 ③ 必须可控。 整体生成的模型过不了 ③。
7.3 关于"自研图生 3D"
必须给一个诚实的现状判断:
| 环节 | 现状(2026) | 能否直接进管线 |
|---|---|---|
| 文生图 | 成熟,可控性可接受(配合 ControlNet) | ✓ 可直接用(当概念图/贴图源) |
| 图生 3D(整体) | 质量参差,"看起来像" | ✗ 拓扑/UV/布线不可用 |
| 图生 3D(构件/规整物体) | 较好,尤其硬表面 | △ 仍需规范化 |
| 图生 3D(角色/有机体) | 差,无法蒙皮 | ✗ 不可用 |
所以"自研图生 3D"的正确目标不是"生成能直接用的模型",而是"生成能被 Normalizer 收编的毛坯"。
这个定位差别很大: - 前者要求模型能力突破(不可控) - 后者要求"生成规整的、可修复的几何"(可通过约束/后处理达成,可控)
架构建议:把力气花在 Normalizer 和"生成约束"上,而不是追求生成质量的天花板。 因为生成质量由基础模型决定(你无法控制别人的迭代),而 Normalizer 是你的资产。
8. Asset Normalizer(规范化闸门)—— 不做这个,库会被垃圾填满
AI 生成的 3D 资产有四个原罪:
| 原罪 | 后果 | Normalizer 的处理 |
|---|---|---|
| 面数爆炸 | 一把椅子 12 万面 | 重拓扑 / 减面到目标预算 |
| UV 混乱 | 无法贴图、无法烘焙 | 自动 UV 展开(含 seam 优化) |
| 拓扑差 | 无法蒙皮、无法变形 | 四边面重拓扑(角色);硬表面可放宽 |
| 单位/朝向不统一 | 拼装时全乱 | 单位与轴向规范化 + 包围盒对齐 |
额外必须做的:
- LOD 链生成(否则进不了渲染管线)
- 材质烘焙(把生成的高精度材质烘成 PBR 通道)
- 碰撞体生成(凸包/简化网格,物理要用)
- 预算标注(把实际 tri/tex/mem 写进 MIR 的 budget 字段)
这条闸门是资产库的生命线。 没有它,库的价值会随规模增长而下降(垃圾越多,检索越差)。
9. 流程编排:引擎管什么,平台管什么
你提到版本管理、自动化测试、发布、宣传片、线上运营。这些不能全塞进引擎,否则引擎会变成四不像。
| 能力 | 位置 | 理由 |
|---|---|---|
| 构建 / 打包 | 引擎内 | 需要 MIR 与平台工具链 |
| 单元/集成测试 | 引擎内(headless 模式) | 需要运行时 |
| 自动化真机测试 | 引擎内产出 + 外部农场 | 设备矩阵在外部 |
| 版本管理 | 外部(git + Vault) | MIR 是文本,天然适配 git |
| 发布 | 外部 | 平台政策变化快,不该进内核 |
| 宣传片/截图 | 外部 Agent(调用引擎的离线渲染/录制) | 需要视频剪辑能力 |
| 运营/数据分析 | 外部 | 与引擎无关 |
原则
引擎是"生产工具",不是"运营平台"。
分界线:凡是需要"MIR + 运行时"的,进引擎;凡是需要"外部服务"的,进平台。
me-ci(进程内 CLI)负责前者,Orchestrator(外部服务)负责编排后者。两者通过一套稳定的 API 通信。
宣传片这条特别值得说:不要做在引擎里。引擎只需要提供"离线高质量渲染 + 固定时间步录制 + 相机路径脚本",剩下的剪辑/配乐/字幕交给外部 Agent。否则你会花两年做一个半吊子的剪辑软件。
10. 冷启动——资产库的鸡生蛋问题
这是整个方向最真实的风险,比技术难得多。
资产库价值 = f(资产数量 × 元数据质量 × 检索准确率)
- 资产 < 500 个:库几乎无价值(检索不到东西,AI 只能硬生成)
- 元数据差:库越大越糟(全是搜不出来的垃圾)
- 检索不准:用户失去信任,回到手动找
四个破局手段
-
垂直包先行,不做通用库 只做一个包(比如"中式乡村"),做到 300+ 构件、做深做透。 一个能用的垂直包 > 十个半成品通用库。
-
生成即入库 每次 AI 生成的产物,经过 Normalizer 后自动入库(带 provenance)。 这样"用"的过程就是"攒"的过程,库随项目自然增长。
-
配方优先于模型 World Grammar 和 Sound Palette 这类"配方",几十条就能产生巨大价值, 而模型要几百个才够用。先攒配方,后攒资产。
-
合法填充 开源资产(CC0/CC-BY)+ 商用授权库(需记账,Ledger 里标许可)。 注意:这一步必须做许可合规,否则是定时炸弹。
11. 跑满 Xbox——这不是渲染技巧,是"零 CPU 瓶颈"
"跑满主机"常被理解成"画质拉满",其实核心是让 CPU 不再是瓶颈。
Xbox Series X 的关键能力:RDNA2 12 TFLOPS、16GB GDDR6(约 13.5GB 可用)、硬件光追、Mesh Shader、VRS。
要跑满它,必须做到:
| 要求 | 含义 | 对架构的约束(现在就要留) |
|---|---|---|
| GPU Driven | 场景数据整批提交,CPU 每帧只写少量常量 | RHI 必须支持 indirect draw / dispatch |
| Mesh Shader 路径 | 几何在 GPU 上生成,不走传统 IA | RHI 接口要能表达 mesh pipeline |
| 硬件光追 GI | 次级光线在硬件上跑 | RHI 要有 RT 抽象(Tier 检查) |
| 异步 Compute | 后处理/裁剪与渲染并行 | 命令队列要支持多队列 + 同步原语 |
| 描述符堆 | 避免每 draw 绑定开销 | RHI 要有 bindless/descriptor heap 概念 |
最关键的一条:如果 P0 的 RHI 只实现了"立即模式 draw call"(WebGL2 就是这样),那么后期要补 GPU driven 会重写整个提交层。
所以架构上必须从现在开始:RHI 接口里保留 draw_indirect / dispatch_indirect / multi_queue / descriptor_set 这些方法,即使 WebGL2 后端把它们实现成"退化版"(内部循环调用普通 draw)。
接口现在留,实现后补。 这是唯一能避免后期重写的办法。
12. Cocos 开源的借鉴方式
Cocos 的引擎代码是公开的,值得读。但要有边界:
| 借鉴(读设计,自己写) | 不要借鉴 |
|---|---|
| 组件模型的取舍(为什么它这样设计) | 直接拷贝代码(许可 + 架构耦合) |
| 资源管线的依赖图与增量构建 | TS/JS 运行时(我们的架构不同) |
| 平台适配层的抽象方式 | 编辑器 UI 架构(我们的编辑器不同) |
| 分包策略与小游戏发布流程 | 它的渲染管线(我们要更高上限) |
正确做法:读它的 ADR/设计文档/issue 讨论,理解"为什么这样",然后用自己的架构重写。
代码拷贝会带来两个问题:许可风险(各文件许可可能不同)+ 架构污染(它的假设和我们的 MIR 冲突)。
13. 对你这个问题的直接回答
"对你是一个挑战吧?"
是,但挑战不在你想的地方。
| 你以为的挑战 | 实际难度 | 说明 |
|---|---|---|
| 生成能力(文生图/图生 3D) | 低 | 接模型即可,且外部在快速进步 |
| 检索 | 低 | 向量 + 结构双索引,成熟技术 |
| 版本管理 | 低 | MIR 是文本,git 天然适配 |
| 跑满 Xbox | 中 | 纯工程量,但要现在就留接口 |
真正的挑战是三个:
① 冷启动(最大) 库空的时候,整套"智能复用"是空转的。而填库需要时间,时间需要钱。 → 破局:垂直包 + 生成即入库 + 配方优先(§10)
② 适配成本可能高于重做 尤其是代码。一个"看起来能复用"的包,接入时发现接口不匹配、依赖冲突、性能画像不符,改它的时间可能超过重写。 → 破局:特性包必须带契约和测试(§4),否则不允许入库
③ 产品线摊薄 引擎 + 资产库 + 流程编排 + 运营 = 4 个产品。一个小团队做 4 个产品,每个都做不深。 → 破局:明确不同时做。顺序是:引擎(P0)→ 资产库(P1)→ 编排(P2)→ 运营(P3,或永远不做,用现成工具)
14. 对已有架构的增量
这篇文档引入了 4 个新组件,都不改原有分层,而是挂在旁边:
| 新组件 | 位置 | 与既有架构的关系 |
|---|---|---|
me-vault |
L6 侧(资产服务,不在运行时) | 产出的资产以 MIR 形式被引用 |
World Grammar |
MIR 的一种新图类型 | 编译期被 lower 成 SceneGraph |
Asset Normalizer |
L8 工具链的后端服务 | 输出带 budget 的 MIR 节点 |
Orchestrator |
引擎外服务 | 通过 API 调用 me-ci,不进内核 |
架构宪法不变:MIR 仍是唯一真相源;AI 仍只操作 MIR;硬校验仍是确定性代码。
新增一条宪法:
第 6 条:任何进入资产库的东西,必须经过 Normalizer 并携带 provenance 与 budget。 例外需要 ADR。
这条是为了防止"库被垃圾填满"——那比库空着更糟。
AI 生成配方而非波形、七项硬校验、声学体检、场景音景闭环、运行时合成
音频与音效架构
状态:P0 已验证(SfxForge 可运行,32 个资产 / 12 个配方 / 5 个场景) 相关:
tools/sfx/(实现)、library/sfx/audition.html(试听台)
一、先回答一个前置问题:AI 该生成什么
WorkBuddy 现在没有音频生成模型(只有音频转写)。但这不构成阻碍,因为——
在游戏里,AI 生成音频的正确形态是"生成配方",不是"生成波形"。
理由不是"现在还没有模型",而是三个结构性的:
| 问题 | 生成波形(text-to-audio) | 生成配方(参数化合成) |
|---|---|---|
| 可复现 | 同 prompt 两次不同,无法回滚、无法 diff | seed + 参数 = 逐字节一致(已验证) |
| 变体成本 | 每多一个变体就是一次生成 | 换 seed,边际成本为 0 |
| 包体 | 每个变体几十~几百 KB | 参数 JSON 几百字节,可运行时合成 |
| 版权 | 训练数据来源难自证 | 纯数学生成,无第三方权利 |
| 可编辑 | "再亮一点"只能重新生成 | 改一个 cutoff 数值 |
这与引擎的核心原则一致:资产必须可复现、可 diff、可回滚。一段不可复现的音频,进不了这条流水线。
所以结构是:
AI(或人)→ 写/改配方(JSON/Python,可 diff)
→ SfxForge 渲染(确定性)
→ 硬校验(7 项,代码判定)
→ 入库(双索引)
→ 试听 / 预烘 / 运行时合成
AI 的产出物是几十行参数,不是 720KB 的 wav。这是本期最重要的一个判断。
二、为什么不用采样库
不是"合成音质更好"(通常不如录音),而是采样库解不了游戏音频的三个硬问题:
- 重复感:玩家每秒走两步,一小时 7200 次。同一个采样必被听出来。需要 8~12 个变体,采样方案的成本线性增长,程序化方案为零。
- 包体:4MB 主包里,一套能用的采样库(脚步 8 材质 × 8 变体 + 环境 10 条)轻松超过 2MB。程序化配方不到 10KB。
- 参数化:同一段风声,白天要亮、夜里要闷、进洞要加混响。采样只能换文件,配方改一个 cutoff 曲线就行。
采样库仍有不可替代的场景:人声、方言、音乐、真实乐器。边界要划清楚(见第八节)。
三、SfxForge 的分层
recipes.py 配方层 "这个声音的物理机制是什么" ← AI 主要在这层工作
│
dsp.py 基元层 噪声 / 包络 / 时变滤波 / 颗粒 / 混响 / LUFS
│
forge.py 管线层 render → dc_block → seamless → normalize → 校验 → 导出
│
verify.py 门禁层 声学体检(谱质心 / 衰减比 / 带宽)
│
scene.py 编排层 场景 → 图层 → 检索 → 缺口 → 补齐 → 清单
│
index.json 索引层 双索引(语义标签 + 结构规格)
3.1 配方:合成发声机理,不是模仿某段音频
这是配方能不能被 AI 改对的关键。写"一段沙沙声"AI 改不动;写清楚机理,AI 就知道该动哪个参数。
| 声音 | 机理(写进配方的东西) | 只写"效果"会怎样 |
|---|---|---|
| 碎石脚步 | 低频体重撞击(52~78Hz)+ 5~8 个带通噪声颗粒(450~1100 / 1800~3800Hz) | 听起来像踩薯片袋 |
| 木门吱呀 | stick-slip 摩擦脉冲,音高缓慢上漂 1.4~1.9 倍,中段必有卡顿 | 像电子音效,不像门 |
| 金币 | 非谐波泛音 1 : 2.76 : 5.40 : 8.93(金属棒振型) |
用谐波列会变成玩具笛子 |
| 风 | 粉噪 + 双 LFO(0.11Hz / 0.047Hz)驱动截至 260~1040Hz 扫动 + 树叶层 | 静态噪声,没有"阵风" |
| 蟋蟀 | 3~5 个个体,频率 3.6~5.2kHz、节奏互不相同,双层门控(组 × 脉冲) | 一只蟋蟀就是电子表 |
真实感来自"不齐":蟋蟀从不同步、火里的爆裂大小呈对数分布、碎石颗粒的间隔是随机的。配方的价值就在于把这些"不齐"参数化。
3.2 时变滤波为什么必须走 STFT
游戏音效绝大多数是"截止频率随时间扫动"的(风、门轴、水)。二阶 IIR 系数随时间变化会产生相位漂移,扫动时能听见"水泡声"。
实现上踩过两个坑,都记在代码注释里:
- 反射填充:不做的话边界帧覆盖不足,触发 NOLA(STFT 不可逆)告警。
- 参数顺序:
tv_filter(x, fc, sr, kind, order)里sr在kind前,导致我两次把"low"传给了sr。改成tv_filter(x, fc, kind, order, sr)——让错误无法通过语法,比靠记忆可靠。
3.3 处理顺序不能乱
render → dc_block → make_seamless → normalize
dc_block 是一阶 IIR,带历史状态。我一开始把 seamless 放在 render 内部(即 dc_block 之前),结果交叉淡化做好的接缝被后面的滤波重新撕开,三个循环资产因此挂掉 C4。
教训:管线里只要有一个有状态的环节,前后顺序就是契约,不是风格。
四、七项硬校验
硬校验的原则:代码判定、不通过就不入库、判据本身必须可测量。
| # | 项 | 判据 | 为什么 |
|---|---|---|---|
| C1 | 时长 | 偏离目标 < 8% | 配方声明的时长是设计意图 |
| C2 | 峰值 | ≤ -1.0 dBFS | 防削波(编码后会更糟) |
| C3 | 直流偏移 | < 0.002 | 布朗噪声(火、风)天然堆在直流附近,不去会浪费动态余量 |
| C4 | 循环接缝 | 接缝跳变 < 该信号相邻采样典型差的 3 倍 | 见下 |
| C5 | 声道数 | 一过性=单声道 / 环境=立体声 | 见下 |
| C6 | 响度 | 落在配方声明窗口 | 环境按 LUFS,短音效按峰值 |
| C7 | 体积 | ogg ≤ 配方预算 | 4MB 主包的硬约束 |
C4 的判据我第一版写错了
第一版是"首尾样本差 < 0.02"。这是错的:对宽带噪声,相邻采样点本身就差得很大,绝对阈值毫无意义(0.02 在峰值 -12dBFS 的信号里只相当于 8% 相对误差,正常噪声轻松超过)。
正确的判据必须是相对的:
seam = |x[-1] - x[0]|
typical = percentile(|diff(x)|, 90)
fail if seam / typical > 3.0
含义是:"接缝处的跳变远超这个信号自己的正常跳变"——这才是可听的咔哒。改完立刻通过,而且这个判据对任何信号类型都成立。
这说明一件事:硬校验的价值不在"有校验",在"判据对"。 一个错的判据比没有更糟,因为它会让你以为已经验过。
C5:位置音源必须单声道
这条容易被忽略但必须写死。一过性音效(脚步、狗吠、金币)如果在制作阶段就做成立体声,引擎再也无法做 3D 定位——立体声文件没有相位关系可供 HRTF 使用。
- 位置音源:单声道,由引擎在运行时 pan / HRTF
- 环境床:立体声(左右去相关),因为环境音通常是"区域音"而非"点音"
配方层用 decorrelate(fn) 生成真立体声:左右各跑一次不同随机流。这比"单声道 + 延迟"好在单声道兼容——手机外放时不会相位抵消掉音量。
五、双索引(与资产库里是同一套)
{
"id": "sfx.footstep_gravel.v01",
"semantic": { "tags": ["footstep","碎石","土路","户外"], "mood": ["dry","gritty"] },
"structural": {
"duration_ms": 300, "channels": 1, "loop": false,
"peak_dbfs": -3.0, "loop_seam": null,
"bytes_ogg": 1200,
"provenance": { "recipe": "footstep_gravel", "seed": 20261048917, "runtime": "baked" }
}
}
- 语义侧管"像不像":
碎石、傍晚、乡村 - 结构侧管"能不能用":单声道吗?能循环吗?多大?3 秒内能加载吗?
- 溯源侧管"怎么来的":配方 id + seed + 是否确定性。有这三样,任何资产都能重算出来。
检索时两侧都必须过。这是第六篇讲的"复用的瓶颈在适配不在检索"在音频上的具体落地:库里有一条"风声",标签命中 100%,但如果它是单声道立体声混错的、或者 40 秒长 2MB,它就不能用作 bed。
六、场景音景的闭环
实测跑通(python scene.py):
场景「北方乡村的傍晚」
├ [bed] 需要「乡村/傍晚」 → wind_village_dusk (1.00) → v01, 49KB
├ [bed] 需要「虫鸣」 → crickets_dusk (1.00) → v01, 94KB
├ [spot] 需要「狗/远处」 → dog_bark_far (1.00) → v01, 5KB
├ [spot] 需要「麻雀」 → bird_sparrow_chirp (1.00) → v01, 2KB
└ [player] 需要「碎石」 → footstep_gravel 需 4 变体 → 现场补 v04(校验通过)
图层不是装饰,是必需。 真实游戏音景永远是 3~6 层:底床(给空间)、点缀(给生命)、玩家(给反馈)。少一层就"空"——只剩风声的场景听起来像素材,五层叠起来才像"傍晚的村子"。
缺口分两种,处理方式完全不同
| 类型 | 含义 | 处理 |
|---|---|---|
MISSING |
库里没有这个物种 | 不能生成,必须新建配方。如实报告 |
SHORT |
有配方但变体不够 | 换 seed 现场补,走同一套校验 |
实测中"白天的集市"场景故意留了两个 MISSING(人群嘈杂、叫卖),系统如实报告"2 处缺口"而不是硬凑一个不像的声音。
这个诚实的失败比虚假的成功有用:它直接告诉你"下一步该建哪个配方"。
幂等
第二次运行 scene.py:32 → 32,不重复补齐。缺口判定基于"现有变体数 ≥ 需求数",不是"每次生成一批"。
七、声学体检:不靠耳朵发现问题
我听不到生成的声音。所以做了一层可量化的门禁(verify.py),对每类硬编码物理期望:
| 类别 | 谱质心期望 | 依据 |
|---|---|---|
| 脚步 | 120~3800Hz | 低频体重 + 中频砾石 |
| 麻雀 | > 2000Hz | 鸟的声带就这么高 |
| 环境床 | 80~5600Hz | 高频被空气与植被吸收 |
| 环境床衰减比 | 0.45~2.2 | 循环床必须平稳(首段/末段能量比) |
| 打击型衰减比 | < 0.3 | 末段必须明显低于首段 |
它抓到了 4 个用耳朵很难定位的错误:
| 资产 | 首版质心 | 问题 | 修正后 |
|---|---|---|---|
| 小雨 | 8289Hz | 高频过多,是白噪嘶声不是雨 | 4562Hz |
| 柴火 | 7496Hz | 爆裂跑到 11kHz,像电子炸点 | 3442Hz |
| 风 | 4029Hz | 树叶层压过低频,"呼呼"变成"沙沙" | 3385Hz |
| 碎石脚步 | 4267Hz | 颗粒频段太高,像踩薯片袋 | 3400Hz 区间内 |
人耳会说"有点儿刺",但说不出是哪不对。谱质心直接指向"高频能量占比过高",改哪个参数就明确了。
八、运行时策略:这是体积问题的唯一解
实测:32 个资产 ogg 合计 747KB,其中环境床 690KB。4MB 主包里这是 17%——不可接受。
三条路,只有一条对:
| 方案 | 主包占用 | 代价 |
|---|---|---|
| 全部预烘进包 | 690KB | 吃掉 17% 包体 |
| 全部放 CDN 下载 | 0(首包) | 首次进场景卡顿,弱网致命 |
| 环境床运行时合成 | 0 | 需要 wasm DSP,CPU 有成本 |
所以 index.json 里标了 provenance.runtime:
baked:一过性音效,预烘进包(脚步 1KB、金币 3KB,本来就小,没必要省)procedural:环境床,运行时用配方现场合成,0 字节
环境音的合成成本实测:8.25s 立体声 / 48kHz,Python 侧约 0.4s。wasm 上会更快,且可以分帧增量生成——每帧生成 1024 个样本,占用的 CPU 摊到整个场景生命周期,几乎不可感知。
这还有一个副作用好处:环境音可以随游戏状态连续变化。玩家从屋外走到屋内,风的高频逐渐衰减、混响逐渐增加——用配方改参数就行,用采样就得做交叉淡化,还容易听到切换痕迹。
九、引擎侧音频中间件的接口
音效资产只是原料。引擎需要的是(P0 最小集):
Bus 结构
Master ─┬─ SFX(一过性)
├─ Ambience(环境床,受 ducking)
├─ UI(不受 3D 影响)
├─ Voice(对话,触发 ducking)
└─ Music
四个必须 P0 就设计的接口:
- 虚拟声部(voice virtualization):小游戏并发源上限约 32。超了要按"优先级 × 距离 × 音量"淘汰,而不是静默丢最老的。淘汰策略错误的表现是"打着打着音效没了"。
- Ducking:对话/关键提示时,把 Ambience 自动压 6~10dB。必须做成总线的属性,不能靠每个音效自己调音量。
- 3D 衰减模型:距离衰减 + 空气吸收(高频先衰减,这就是"远处"的听感来源,见
dog_bark_far的 220~2200Hz 带通)。 - 流式 vs 常驻:环境床常驻内存(合成后 ~1.6MB PCM/条,需要按需释放),一过性全量预载。
这些都在 ADR-004 的 RHI 之外的独立子系统里,不依赖图形后端。P0 只做 Bus + 简单 pan + 距离衰减,HRTF 留到 P1。
十、做不了的边界(要说清楚)
| 类型 | 程序化合成 | 结论 |
|---|---|---|
| 脚步声/门/金属/UI | ✅ 很好 | 继续扩配方 |
| 环境床(风/雨/火/水/虫) | ✅ 很好 | 继续扩配方 |
| 人声 / 方言 / 叫卖 | ❌ 做不到 | 必须录音或等 TTS/voice-clone |
| 音乐 / BGM | ❌ 不该做 | 用授权曲库或外部生成 |
| 大型生物(怪兽/马/人群) | ⚠️ 有限 | 拟音(foley)或采样 |
上面表格里的 ❌ 不是"暂时没做",是"路线不同"。集市场景缺的"人群嘈杂"和"叫卖"永远不会被程序化合成解决——那是录音和 TTS 的领域。系统应该如实报告这个缺口,而不是硬凑。
十一、实测记录(可复现)
$ cd tools/sfx && python forge.py
共 32 项 / 12 个配方 / {ui:3, footstep:7, door:3, pickup:3, creature:6, ambience:10}
ogg 合计 747KB(其中环境循环 690KB),wav 合计 16.6MB
全部通过 7 项硬校验。
$ python verify.py
声学体检通过:32 项全部落在各类别物理期望区间内。
$ python scene.py
5 个场景合计 539KB ogg
· 北方乡村的傍晚 5 层 151.2KB 全部满足
· 北方老屋的炉火 4 层 86.2KB 全部满足
· 雨夜的村口 4 层 115.2KB 全部满足
· 白天的村口 5 层 121.3KB 全部满足
· 白天的集市 2 层 65.6KB 2 处缺口(故意)
确定性验证:连续两次构建,31 个 wav 的 MD5 完全一致。
布局审计:0 横向溢出 / 0 处 < 11px 的字 / 0 JS 报错 / 32 个 ogg 全部可加载。
十二、下一步
按性价比排序:
- wasm 版 DSP(把
dsp.py移植到 Rust→wasm32)——这是环境音 0 字节的前提,也是"引擎内实时合成"的唯一路径 - 配方扩展:先做 20 个"室内"配方(家具、布料、玻璃、陶器、金属),室内场景的复用率最高
- AI 写配方:给 AI 一份机理模板 + 参数区间 + 校验反馈,让它产出配方而不是音频。这是本期"AI 生成、确定性产出"的完整闭环
- 音频中间件(Bus / ducking / virtualization)——等 P0 引擎骨架起来再对接
本文档的所有数字均来自实跑,脚本在 tools/sfx/,试听台在 library/sfx/audition.html。
11 个后端许可核验、三处会否决商用的错误、可调用性实测、三档选型、三分区架构
08 · 生成式音频:选型核验与接入架构
状态:已核验(2026-10-22),许可证与端点均为当日实测/实读 适用:SfxForge 音频子系统的生成式后端选型
0. 三句话结论
- 清单里有 3 处会直接否决商用的错误:AudioGen 权重是 CC-BY-NC(禁商用)、Stable Audio 是"营收 <100 万美元"条件许可且叠加 Gemma 条款、Dasheng 输出只有 16kHz。
- 真正被漏掉的好东西是 MOSS-SoundEffect v2.0——清单里唯一真 Apache-2.0、48kHz、支持中文提示的音效模型。但本机 8GB 内存跑不动,且 HuggingFace 与 hf-mirror 均不可达(实测超时)。
- WorkBuddy 自身没有任何音频生成能力(只有图像/视频/3D 生成和音频转写)。可调用的三个云端后端我已全部接通并实测到鉴权层,只差你的 key。
1. 先划约束
选型不能脱离这四条,否则就是抄参数表:
| 约束 | 数值 | 为什么是硬约束 |
|---|---|---|
| 小游戏主包 | 4MB(首包) | 一段 20 秒 48kHz 立体声 = 3.8MB,一条就吃掉整个包 |
| 本机算力 | M1 / 8GB 统一内存 | MOSS v2 权重 11.2GB,MLX 版 30 秒峰值 14.2GB。差一个数量级 |
| 网络 | HF ❌ / hf-mirror ❌ / pypi ✅ / 国内 API ✅ / fal·replicate·stability ✅ | 开源权重下载不下来,这条路今天不成立 |
| 许可链 | 引擎要交付给第三方客户 | 有营收阈值的许可,你无法替客户保证 |
第四条的杀伤力最容易被低估:稳定音频那类"营收 <100 万美元免费商用"的条款,作为自用工具没问题,作为引擎内置能力就是定时炸弹——你交付给一个年营收 200 万的客户,他就在违约。
2. 清单逐条核验
2.1 开源模型
| 模型 | 宣称 | 核验结果 | 裁决 |
|---|---|---|---|
| Meta AudioGen | 文生音效 | 代码 MIT,但权重 CC-BY-NC 4.0;16kHz 单声道;10 秒上限 | ⛔ 商用出局。且 16kHz 意味着 8kHz 以上频段全无——金属、玻璃、水花这些靠高频质感的声音会变闷响 |
| Stable Audio Open | 47 秒 / 44.1kHz 立体声 | 47 秒是 1.1B 那个;Small 版是 341M / 11 秒。许可是 Stability 社区许可:营收 <$1M 免费商用;2026 年 5 月的 SA3 Small-SFX 还叠加 Gemma 条款(文本编码器是 T5Gemma) | ⚠️ 自用可,交付不可。质量确实好,留作"编辑器内试听/临时素材" |
| MOSS-SoundEffect v2.0 | 国内开源 | ✅ 确认为真:1.3B DiT + DAC VAE + Qwen3-1.7B,48kHz、中文提示、30 秒、Apache-2.0(模型/代码/组件全是) | ✅ 许可与规格都是最优解,但本机跑不动,且权重下不来。列长期兜底 |
| 小米 Dasheng-AudioGen | 文生音效 | Apache-2.0 干净,但输出 16kHz;定位是"结构化混合场景"(对白+音效+音乐一条出) | ⚠️ 用它出单个开门声是错配;出一整段带台词剧情音频才是它的用法 |
2.2 线上产品
| 产品 | 核验结果 | 裁决 |
|---|---|---|
| ElevenLabs SFX v2 | 最长 30 秒;全清单唯一服务端支持 loop=true;48kHz WAV(非循环);$0.12/分钟;免费层仅限个人非商用,付费层才商用 |
✅ 音质基准。缺点是英文提示词,中文场景会掉 |
| Adobe Firefly SFX | 版权友好,但主要落在 Premiere / Firefly 应用内;独立 API 不明确 | ⛔ 不作为引擎后端(无法程序化接入) |
| 可灵 / 即梦 | 有文生音效,但没有公开的开放 API | ⚠️ 只能走"网页生成 → 下载 → ingest 导入"这条手工路径 |
| 字节 Seed-Audio 1.0 | ✅ 确认开放 API:POST https://openspeech.bytedance.com/api/v3/tts/create,model: seed-audio-1.0,最长 120 秒,48kHz,支持 wav/ogg_opus,中文提示词原生 |
✅ 国内首选,理由见下 |
2.3 清单漏掉的一整类:视频 → 音效
这类东西确实存在且很值钱(画面里的动作自动配对声音),但商用全军覆没:
| 模型 | 许可真相 |
|---|---|
| HunyuanVideo-Foley(腾讯) | 混元社区许可,EU / UK / 韩国不适用,48kHz,帧级同步 |
| MMAudio(Sony/UIUC) | 代码 MIT,权重 CC-BY-NC |
| ThinkSound / PrismAudio(阿里) | 仓库挂 Apache-2.0,README 却写明仅研究教育用途 |
| Woosh(Sony) | 代码 MIT,权重非商用 |
ThinkSound 这一类最危险:法务看仓库标签会放过它,实际条款禁止商用。许可必须读 README 和 NOTICE,不能读仓库 badge。
而且它解决的是另一类问题——不是"我要一个开门的声音",而是"我已经有画面,让声音踩在动作上"。这属于流程差异,不是模型强弱。
3. 我实际能调用什么(实测)
我在本机跑了一遍真实探测,结果如下(python genai.py probe):
| 后端 | 端点 | 实测 | 状态 |
|---|---|---|---|
volc-seedaudio |
openspeech.bytedance.com | HTTP 400 fail to convert model to resource_id |
✅ 链路通,缺 VOLC_AUDIO_API_KEY |
eleven-sfx |
api.elevenlabs.io | HTTP 401 Neither authorization header nor xi-api-key received |
✅ 链路通,缺 ELEVENLABS_API_KEY |
fal-eleven |
fal.run | HTTP 401 Cannot access application "fal-ai/elevenlabs" |
✅ 链路通,缺 FAL_KEY |
401 / 400 说明端点活着,只缺匙;000 才是真不通。 同期实测 huggingface.co 与 hf-mirror.com 都是 000(超时),api.openai.com 也是 000。
所以直接回答你的问题:
- 我(WorkBuddy)内置能力:0 个音频生成。 有多模态生成工具(图像
ImageGen、视频VideoGen、3D),有音频转写,没有 text-to-audio。 - 配一个 key 就能调用的:3 个(上面三个)。你走注册拿试用积分,把 key 写进环境变量,我这边立刻能出声音。
- 不能调用的:所有开源权重模型(本机内存 + 网络双重不可行)。
4. 选型决定
分三档,不要只选一个——用途不同:
主用:火山 Seed-Audio 1.0
四个理由,都不是参数表能看出来的:
- 中文提示词原生。写"北方乡村集市,人群嘈杂,远处有人叫卖"直接可用;英文模型要先翻译,而"叫卖"这种词翻译完味道就变了。
- 一个模型覆盖人声+音效+音乐+环境。这正是配方永远做不到的三类(真人声/方言、人群嘈杂、独特奇观音)——它精准补上 SfxForge 已知的能力空洞。
- 48kHz + 120 秒 + 直接出 ogg_opus。省一次转码,环境床长度也够。
- 国内节点:支付、发票、合规、延迟都在同一侧。
个人有 30 分钟免费额度,够验证 60~100 条。
质量基准:ElevenLabs SFX v2(直连或经 fal)
只在两种情况下用:需要 loop=true 的服务端无缝循环(这是它独有的),或英文场景要一版最高音质的参照。
长期兜底:MOSS-SoundEffect v2.0
唯一能同时满足"Apache-2.0 + 48kHz + 中文 + 可微调 + 可私有化"的。等换 32GB 以上机器或租卡时切换。它的战略价值不是现在能用,而是引擎产品化时的许可安全底线。
明确不选
- AudioGen / MMAudio:权重 NC,商用出局。
- ThinkSound:标签与条款冲突,法务风险。
- Stable Audio:营收阈值 + Gemma 条款,作为交付给客户的引擎能力不可接受。可用作编辑器内临时试听。
5. 接入架构:生成只做"补缺口"
5.1 三个分区(物理隔离,不是逻辑标记)
library/sfx/
wav/ ogg/ index.json 可重建区:配方渲染,逐字节可复现,进 CI
generated/ 冻结区:生成/导入,不可复现,靠 sha256 保真
scenes/ 场景音景清单
为什么必须物理隔离:可重建区的资产可以随时 rm -rf 再跑一遍;冻结区不能——删了就永远回不来(同 prompt 再生成是另一条音频)。混在一起,一次误清理就把花了钱和时间的东西全毁掉。
5.2 三条规则(写进代码,不是写进文档)
R1 生成资产不进可重建区。 generated/ 的 provenance.zone = "frozen"。
R2 不可复现的资产必须冻结字节。 deterministic: false + sha256_wav。而且禁止在 CI 里重新生成来"验证"——那是测不出来的,只能测字节有没有被换。
R3 生成不是资产库主力。 配方负责常规音效(可复现、0 KB、换种子出新变体),生成只补三类配方永远做不到的。
R2 的执行不是口号:python genai.py verify 会重算 sha256。我实测过——改一个字节即报错、退出码 1,并拒绝保留旧 provenance。
5.3 生成必须过同一道闸门
生成产物和配方产物走完全相同的七项硬校验(时长/峰值/直流/循环接缝/声道/响度/体积)。不通过就进 generated/rejected/,入库标准不因"这是花钱生成的"而降级。
实测细节:单声道环境床要变立体声,我没有用"延迟 1ms"那种相位把戏(手机外放会相位抵消掉 3dB),而是干声完全相同 + 湿声两路独立扩散,干声占 82% 保单声道兼容。位置音源则强制单声道——立体声文件做不了 3D 定位,这是硬约束。
5.4 为什么生成不能进运行期
一段 20 秒 48kHz 立体声未压缩约 3.8MB,超过小游戏主包 4MB 的全部预算。所以生成只能在工具期烘焙(bake-time),产出物再经 opus 压缩进包。想"运行时生成变体"在小游戏上是不成立的;那是主机/PC 才有的奢侈。
6. 成本:不是决策变量
| 方案 | 单价 | 100 条一过性(1.5s) + 10 条环境床(20s) |
|---|---|---|
| 火山 Seed-Audio | ≈¥0.05/秒* | ≈ ¥18 |
| ElevenLabs(直连/fal) | $0.002/秒 | ≈ ¥5 |
| MOSS 自建 | 仅算力 | ≈ 0 |
* 火山单价来自第三方整理,官方单价未完整公开,以控制台为准。
一百多块钱以内的差别,不构成选型理由。真正的决策变量是:许可、中文、包体、可复现性。这也是我建议不要在这上面做比价优化的原因。
7. 需要你做的两件事
第一件(决定能不能动手):选一个后端注册拿试用额度,把 key 给我。
export VOLC_AUDIO_API_KEY=... # 火山方舟控制台 → 豆包语音 → Seed-Audio
# 或
export ELEVENLABS_API_KEY=... # elevenlabs.io → Profile → API Keys
# 或
export FAL_KEY=... # fal.ai → Keys
拿到后一条命令就能出声音并自动过闸门:
python genai.py gen --provider volc-seedaudio \
--prompt "北方乡村集市,人群嘈杂,远处有人在叫卖" \
--seconds 8 --kind ambience --id market_crowd
第二件(不需要 key 就能做):核对请求体。--dry-run 会打印将要发出的完整请求,你把这个和火山控制台的文档对一遍——契约字段错一个字符,调用就是失败的。
python genai.py gen --provider volc-seedaudio --prompt "test" --dry-run
8. 命令速查
python genai.py list # 11 个后端总览(含不能商用的,作为决策记录)
python genai.py probe # 实测可达性 + 密钥状态
python genai.py gen --provider X --prompt "..." --seconds 3 --kind one-shot --id foo
python genai.py gen --provider X --prompt "..." --dry-run # 只打印请求
python genai.py ingest --file ~/Downloads/xx.mp3 --id foo \
--kind ambience --source "即梦" --license "..." --commercial unknown
python genai.py verify # 校验冻结区字节未被替换
ingest 这条路径比看起来重要:可灵、即梦只有网页端没有开放 API,手动下载再导入是目前唯一合法的接入方式,而且导入后与 API 产物结构完全同构——后续检索、装配、打包不受影响。
三层计划粒度、P0-P3 路线、六个里程碑 DoD、WBS 与 88 人周、关键路径、降级方案
09 · 开发总计划:路线图、里程碑与执行
05 号文档回答"分几个阶段、什么时候该停"。 这一份回答"下周谁做什么、做到什么算完成"。
基准日:2026-11-02(周一)。P0 起算,26 周,2027-04-30 结项。
配套:可交互甘特图见 plan/gantt.html(数据源
plan/p0-plan.json); 逐迭代任务与人日见 10-p0-sprint-plan.md。
1. 计划的四个前提(不成立则计划作废)
| 前提 | 具体值 | 不成立会怎样 |
|---|---|---|
| 有一个真实项目 | 悦居自有产品,能上线、有真实用户、有留存指标 | 变成"为验证引擎而造需求",Gate 0 数据全是自欺 |
| 人力 | 基准 4 人(3 全职 + 1 半职 TA/美术协调) | 3 人 → 砍范围(见 §11);2 人 → 只做 §12 的单人 MVP |
| 周期 | 26 周硬上限,不可延期 | 延期等于把 P1 的预算烧在 P0,且证明不了假设 |
| AI 可用性 | 编码 Agent + 一个强模型 API,成本可忽略 | 退回传统开发,工期 ×1.8,P0 目标作废 |
第一条是一票否决项。 没有真实项目,就不该启动这个计划——应该先做一个 Cocos 项目,再回来。
2. 计划分三层,每层回答不同问题
| 层 | 粒度 | 回答什么 | 谁看 | 变更频率 |
|---|---|---|---|---|
| 路线图 | 阶段(P0–P3) | 值不值得投、什么时候停 | 决策者 | 一年一次 |
| 里程碑 | 6–8 周 | 这一段的产出是否成立 | 全员 | 双周对齐 |
| 迭代计划 | 2 周 | 本周谁做什么、卡在哪 | 执行者 | 每周 |
三层之间是承诺关系:迭代承诺给里程碑,里程碑承诺给路线图。任何一层失守,必须向上报告,不允许在迭代层偷偷吸收。
3. 全景路线图
2026 2027 2028 2029+
────────────────────────────────────────────────────────────────────────────────────────────
P0 ├── 2D/2.5D 小游戏上线(26 周)──┤
P1 ├──── 3D 移动双端(12 个月)────┤
P2 ├──── PC/主机预研(18 个月)────┤
P3 └─ 3A/商业化
▲Gate0 ▲Gate1 ▲Gate2
| 阶段 | 时间 | 一句话目标 | 唯一决策门 |
|---|---|---|---|
| P0 | 2026-11 → 2027-04 | 一款微信小游戏真实上线,主包 ≤4MB,低端安卓 30fps,零手写 shader | Gate 0:六项指标 ≥4 项通过 |
| P1 | 2027-05 → 2028-04 | 同一款游戏 3D 版上线微信+抖音,AI 生成关卡占比 >50% | Gate 1:双端上线 + Perf Agent 采纳率 ≥30% |
| P2 | 2028-05 → 2029-10 | 同一份 MIR 编出 PC 包,主机移植冒烟通过 | Gate 2:PC 上线 + 外部团队独立使用 |
| P3 | 2029-11 → | 主机正式支持、大世界、第 3 方付费 | 商业验证 |
每个阶段只有一个门,门的判据在启动时就写死。 中途改判据等于没有门。
4. P0 的 13 个迭代(一览)
双周迭代,S0 起。每个迭代有唯一主题——一个迭代里塞两个主题,通常两个都做不完。
| 迭代 | 周 | 主题 | 关键产出 | 门 |
|---|---|---|---|---|
| S0 | W1–2 | 工程基线与语言决策 | wasm 三角形上屏;Rust→C ABI→C++ 冒烟 | D0 |
| S1 | W3–4 | MIR v0 落地 | 解析/序列化/格式化/往返校验 | |
| S2 | W5–6 | 编译链骨架 + 首个 RHI | .mir → 屏幕上带纹理四边形 |
M0.2 |
| S3 | W7–8 | ECS + 2D 渲染 | 1000 sprite 60fps | |
| S4 | W9–10 | 音频与资源流式 | 场景切换无白屏;混音 CPU <1.5ms | |
| S5 | W11–12 | Agent 闭环 v0 | 一句话 → 可编译 MIR + diff review | |
| S6 | W13–14 | 编辑器 v0 | 场景树/Inspector/视口/构建发布 | |
| S7 | W15–16 | 垂直切片 | 可玩关卡,真机 30fps,trace 回传 | M0.3 |
| S8 | W17–18 | 包体攻坚 | 主包 ≤4MB,首帧 <3s | M0.5 |
| S9 | W19–20 | 内容量产 | 10 关卡,AI 占比 ≥50% | |
| S10 | W21–22 | 稳定性与兼容 | 30 分钟无崩溃、无内存增长 | M0.6 |
| S11 | W23–24 | 上线准备 | 提审通过、灰度 5% | |
| S12 | W25–26 | 上线与复盘 | Gate 0 评审报告 | Gate 0 |
S8 是最危险的一个迭代。 4MB 是一个物理约束,不是"再挤一挤"。它的解法必须在 S3–S7 就埋好(纹理压缩、wasm 体积、分包划分),S8 只是验收。如果 S8 才开始想包体,一定失败。
5. 里程碑与门禁(DoD 逐条可判)
里程碑不是"做完某个模块",而是"某个能力被真实证明"。每个里程碑都必须有可运行产物 + 可核对的数字。
D0 · 语言与基建决策门(W2)
| 判据 | 通过线 |
|---|---|
| wasm 产物 | 在微信开发者工具里出画面,.wasm ≤ 800KB(release + wasm-opt) |
| FFI 冒烟 | Rust → 手写 C ABI → C++ 后端,跑通一个三角形;调用开销 <2μs/次 |
| 构建耗时 | 全量构建 ≤10 分钟,增量 ≤60 秒 |
| CI | 三个目标(host / wasm32 / iOS-arm64)全部绿 |
| 决策 | Rust + C++ 方案维持,或回退纯 C++(ADR-001 转 Accepted/Rejected) |
这一关最重要的不是"技术可行",而是在写下 5 万行代码之前,确认语言选对了。改语言的成本在这里是 2 周,在 S6 之后是 4 个月。
M0.2 · 第一个真实渲染闭环(W6)
| 判据 | 通过线 |
|---|---|
| 端到端 | .mir 文本 → 编译 → 屏幕出现带纹理的四边形,全程无手写 GLSL |
| 预算门禁 | 故意把 tri 预算改成 10,编译失败,报错精确到节点 id |
| 往返 | 1000 节点场景 mir→mirb→mir 字节级等义 |
| 确定性 | 同输入两次构建,产物 hash 一致 |
M0.3 · 可玩切片(W16)
| 判据 | 通过线 |
|---|---|
| 可玩性 | 3 分钟完整玩法循环(开始→关卡→结算→重开) |
| 真机 | 中端安卓(骁龙 7 系)稳定 30fps,帧时间 p95 ≤33ms |
| 内存 | 峰值 <600MB |
| 可观测 | 真机 trace 能回传到编辑器,帧时间可归因到 MIR 节点 |
M0.5 · 包体与冷启动(W18)
| 判据 | 通过线 |
|---|---|
| 主包 | ≤4MB(微信真机安装验证,不是构建产物估算) |
| 首帧 | 冷启动首帧 <3s(中端安卓,4G 网络) |
| 分包 | 总包 ≤30MB,分包加载成功率 ≥99.5% |
| 纹理 | 全部走 ASTC/ETC2,无未压缩贴图进包 |
M0.6 · 内容与稳定性(W22)
| 判据 | 通过线 |
|---|---|
| 内容量 | 10 个关卡,AI 生成占比 ≥50%(按 MIR 节点数计,非人工估算) |
| 质量 | AI 生成内容人工 review 一次通过率 ≥80% |
| 稳定 | 连续 30 分钟游玩无崩溃、无内存单调增长 |
| 覆盖 | 5 台真机(3 安卓低中端 + 2 iOS)全绿 |
Gate 0 · 六项指标(W26)
沿用 05 号文档判据,≥4 项通过即 Go。额外要求:一份工时台账——记录每个关卡的人工小时,与 Cocos 路线同内容量的估算对比。没有台账,"效率提升"就是感觉。
6. 工作分解与工时估算(WBS)
基准容量:4 人 × 26 周 = 104 人周。但要区分两本账:
| 账 | 人周 | 说明 |
|---|---|---|
| 可承诺工作量 | 88 | 下表 14 个工作包的净开发量 |
| 过程开销 | 12 | 站会、评审、联调、Code Review、文档 |
| 缓冲 | 4 | 预留给 §7.1 的中间窗口 |
| 合计 | 104 | = 4 人 × 26 周 |
88 / 104 = 85% 利用率,这是可持续的上限。不要按 100% 排计划——按 100% 排的计划,第一个意外就会全盘崩。
| 工作包 | 人周 | 主责 | 迭代跨度 |
|---|---|---|---|
| 工程基线(仓库/CI/构建/发布脚本) | 3 | A | S0–S1 |
me-core(数学/句柄/错误/Arena/日志) |
4 | B | S0–S2 |
me-mir(schema/解析/序列化/校验/格式化) |
8 | A | S1–S4 |
me-compiler(6 个 pass + 分包 link) |
11 | A | S2–S6 |
me-rhi WebGL2 后端 |
8 | B | S2–S5 |
me-runtime(ECS/调度/内存/输入) |
11 | B | S3–S6 |
| 2D/2.5D 渲染器(批处理/图集/文本/后处理) | 9 | B | S3–S7 |
me-pal 微信(FS/网络/账号/广告/支付) |
3 | C | S3–S6 |
| 音频子系统接入(混音器 + 配方运行时) | 3 | C | S4–S5 |
| 资源流式与分包加载 | 4 | B | S5–S8 |
| Agent 闭环 v0(Planner/Builder/Guardian) | 6 | A | S5–S8 |
| Magic Studio v0(Tauri + React) | 6 | C | S6–S9 |
| 内容与关卡(10 关,含世界语法) | 7 | D | S7–S11 |
| 真机测试/兼容/上线运营 | 5 | D | S7–S12 |
| 合计 | 88 |
6.1 工时是怎么估的
按"A/B 类代码"分开估,这是本计划与常规估算最大的差别:
| 代码类型 | 占比 | 估算方式 | 系数 |
|---|---|---|---|
| A 类:样板清晰、接口明确、可测试 | ~60% | 按接口数 × 单接口耗时 | ×0.6(AI 生成 + 人工 review) |
| B 类:性能关键、平台贴边、架构决策 | ~40% | 按经验值,不打折 | ×1.0(人写) |
不要对 B 类打折。 渲染提交层、RHI 后端、ECS 内存布局、Agent 的校验器——这些地方的错误会在 S8 以"包体超 800KB""帧时间抖 8ms"的形式回来收利息。
6.2 B 类代码清单(禁止 AI 生成,必须人写)
这份清单要贴在仓库里,作为 Code Review 的第一条检查项:
me-rhi的 C ABI 头文件与后端实现- 内存分配器、Arena、句柄表
- 序列化/反序列化的格式定义(不是它的样板代码)
- ECS 的 archetype 存储布局与系统调度器
- 渲染提交层(draw call 排序、状态缓存、command buffer 录制)
- 编译链的 pass 顺序与 legalize 规则
- 平台适配(PAL)各平台实现
- 构建系统与 CI 脚本
判断标准很简单:如果这段代码错了,会让性能或包体变成"说不清原因的问题",它就属于 B 类。
7. 关键路径与不可压缩项
S0 基建 ──▶ S1 MIR ──▶ S2 编译器 ──▶ S3 ECS/渲染 ──▶ S7 切片 ──▶ S8 包体 ──▶ S11 审核 ──▶ S12 上线
│ │ │
└─▶ S5 Agent ┴──────────────┘(旁路,可晚 2 周但不能晚 6 周)
关键路径上的三个不可压缩项(这几件事无法靠加人缩短):
| 项 | 耗时 | 为什么不可压缩 |
|---|---|---|
| 4MB 攻坚 | 3–4 周 | 每一次纹理压缩/分包调整都要走真机安装验证,一轮 2–3 天 |
| 真机兼容 | 3 周 | 设备到货、驱动怪癖、厂商 ROM 差异,只能一台台过 |
| 平台审核 | 1–3 周 | 外部黑盒,被拒一次就是 3–5 天,且无法加速 |
这三个加起来约 8 周,必须显式排在 S8–S11,不能藏在"开发时间"里。
7.1 缓冲放在哪里
不放在最后,而是分布在 S8–S11:每个迭代预留 1 人周未承诺容量,共 4 人周。
两个理由:
- 不单独占日历周。 插一整周会让 26 周的日期全部后移,和"结项日期不动"的硬约束冲突;而且一旦写进日历,"保留的一周"就会自动被填满。
- 落在中后段,而不是末尾。 如果放在最后(S12 前两天),它一定会被当成"再多做点功能"的时间烧掉;放在 S8–S11,它是"补关键路径的债"的专用窗口——包体、真机兼容、Agent 质量恰好都在这一段收尾。
缓冲是"哪条线告急就给谁",不是平均分。 默认分配顺序:包体(S8 溢出)→ 真机兼容 → Agent 质量。三者都没告急,则用于 S9 的关卡扩量(10 → 12)。
这也是"承诺 88 / 容量 104"这组数字的实际含义:那 15% 的缺口不是浪费,它就是缓冲。 排到 100% 的计划没有缓冲,第一个意外就会全盘崩。
8. 人力配置与分工
8.1 角色
| 代号 | 角色 | 核心职责 | 不可替代性 |
|---|---|---|---|
| A | 架构 / 编译链 | MIR、编译器、Agent 编排、技术决策 | 极高——全项目只有一个人能改 schema |
| B | 引擎 / 渲染 | RHI、渲染器、ECS、性能 | 极高——性能问题只有他能定位 |
| C | 工具 / 平台 | 编译器 CLI、Studio、微信 PAL、CI | 中——可外包部分 |
| D | TA / 内容 | 关卡、资产、真机测试、运营 | 中——可用外包 + AI 补 |
8.2 RACI(关键交付物)
| 交付物 | A | B | C | D | 飞哥 |
|---|---|---|---|---|---|
| MIR schema | R | C | C | I | A |
| 编译链 | R | C | I | I | A |
| RHI / 渲染器 | C | R | I | I | A |
| 运行时 / ECS | C | R | I | I | A |
| Studio | C | I | R | C | A |
| Agent 闭环 | R | C | C | I | A |
| 内容与关卡 | I | I | C | R | A |
| 上线发布 | I | I | C | R | R |
R=负责执行 A=最终问责 C=需咨询 I=需知会
A 是单点。 这不是好设计,是小团队的必然。缓解手段:MIR schema 与编译链 pass 的每一处决策都写 ADR,保证换人时能接上。这是本计划里唯一"为离职做设计"的部分,值得。
8.3 每周节奏(固定,不可协商)
| 时间 | 事项 | 时长 | 产出 |
|---|---|---|---|
| 周一 10:00 | 迭代规划 | 30 min | 本周承诺清单(≤5 项) |
| 每日 | 站会(异步文字即可) | 5 min | 阻塞项上墙 |
| 周五 17:00 | Demo + 指标更新 | 30 min | 可运行产物 + 8 个指标数字 |
| 双周五 | 迭代评审 + 回顾 | 60 min | 完成率、下迭代范围调整 |
周五 Demo 的硬规则:只演示能跑的,不演示 PPT。 演示不了的,就是没做完。
9. 工程规范(决定 AI 能否真的提效)
9.1 仓库与分支
main ← 永远可构建、可发布
└─ feat/<sprint>-<topic> ← 双周迭代分支
└─ ai/<agent>-<task> ← Agent 产出分支,自动开 PR
main受保护:必须过 CI + 1 人 review- Agent 的提交一律走 PR,不允许直接推
main - 每个 PR 必须能回答三个问题:改了什么 MIR / 为什么 / 怎么验证的
9.2 AI 生成代码的三条准入规则
| 规则 | 内容 | 违反后果 |
|---|---|---|
| R-AI-1 | 所有 AI 生成的代码在 commit message 里标 ai: 前缀 + 模型名 + prompt hash |
无法评估 AI 的真实贡献,§12 的止损判据失效 |
| R-AI-2 | A 类代码可 AI 生成;B 类(§6.2 清单)禁止 AI 生成,只能 AI 建议 | 性能问题变成玄学 |
| R-AI-3 | AI 生成的功能代码必须有测试,覆盖率不低于人工代码(≥70% 行 / ≥60% 分支) | 组合 50 个特性包后留下 50 个隐患 |
R-AI-1 看起来只是规范,实际是整个项目的度量基础。 如果分不清哪行是 AI 写的,Gate 0 的"AI 生成占比"就只能靠感觉——那这一整年的核心假设就没被验证。
9.3 确定性三连(每个 PR 必过)
- MIR 往返:
mir → mirb → mir字节级等义 - 可复现构建:同 commit 两次构建,产物 hash 一致
- golden 对比:渲染产物与基准图的 SSIM ≥0.98(容差按场景配置)
第 2 条最容易破:时间戳、路径、HashMap 迭代顺序、并行任务完成顺序都会破坏它。在 S0 就要把 SOURCE_DATE_EPOCH、稳定排序、确定性 RNG 全部处理掉,否则后期补是灾难。
10. 测试与度量
10.1 六类测试,各管一件事
| 类型 | 频率 | 管什么 | 失败处理 |
|---|---|---|---|
| 单元测试 | 每次提交 | 纯逻辑正确性 | 阻断合并 |
| MIR golden | 每次提交 | 编译产物不漂移 | 阻断合并 |
| 视觉金图 | 每次提交 | 渲染不回归 | 阻断合并(人工确认后更新基准) |
| 真机自动化 | 每日 | 真实设备能跑 | 阻断发布 |
| 性能回归 | 每日 | 帧时间/内存/包体 | 超阈值 5% 报警,超 10% 阻断 |
| 确定性 | 每次发布 | 可复现 | 阻断发布 |
10.2 看板指标(每周五更新,只有 8 个)
| 指标 | 目标 | 说明 |
|---|---|---|
| 主包体积 | ≤4096KB | 从 S2 就开始画曲线,不要等到 S8 |
| 首帧时间 | <3s | 中端安卓真机 |
| 帧时间 p95 | ≤33ms | 目标场景,不是空场景 |
| 峰值内存 | <600MB | 真机 |
| AI 生成占比 | ≥50% | 按 MIR 节点数,脚本统计 |
| AI patch 采纳率 | ≥80% | 一次通过率,不是"改改能用" |
| CI 全绿率 | ≥90% | 低于 80% 说明测试不可信 |
| 迭代完成率 | ≥85% | 连续两迭代 <70% → 触发范围裁剪 |
指标超过 8 个就没人看了。 想看细节去 dashboard,看板只放这 8 个。
11. 预算(P0,基准 4 人)
| 项 | 金额 | 说明 |
|---|---|---|
| 人力(4 人 × 6.5 月,含社保) | 95–130 万 | 按 3.5–5 万/月全成本 |
| 真机与设备 | 8–12 万 | 安卓低中高端 8 台 + iOS 3 台 + 测试机 |
| 外包美术/音效 | 15–30 万 | 10 关卡的场景、UI、音效 |
| 模型与 API | 2–5 万 | 编码 Agent + 生图 + 生成式音频 |
| 软件与云服务 | 3–6 万 | CI runner、CDN、云真机、设计工具 |
| 备用金(15%) | 18–27 万 | 不解释用途 |
| 合计 | 140–210 万 | 落在 05 号文档"150–300 万"区间下沿 |
这个数字的意义是判断可行性,不是预算书。 如果只能拿出 50 万,正确做法是把 P0 缩到 §12 的单人 MVP,而不是砍架构——砍架构省不下钱,只会让后面重做。
12. 三种降级方案(计划失效时用)
计划的价值有一半在"提前想好怎么缩"。以下三个方案预先定义,触发即执行,不再讨论。
| 方案 | 触发条件 | 范围 | 周期 | 产出 |
|---|---|---|---|---|
| A. 基准 | 默认 | 全部 13 个迭代 | 26 周 | 上线游戏 + 完整引擎 |
| B. 单人 MVP | 人力 <2 人 或 预算 <60 万 | 砍 Studio(用文本编辑 MIR)、砍 Agent 自动生成(人工写 MIR)、只做 1 个关卡 | 12–16 周 | 能上线的小游戏 + 引擎核心 |
| C. 只做工具 | AI 生成采纳率连续 2 迭代 <40% | 停止引擎开发,把 Agent + MIR 做成"跑在 Cocos 上的内容生成工具" | 8 周 | 保住 AI 生产效率,放弃自研运行时 |
方案 C 听起来像失败,其实是最优止损。 如果 AI 的价值在内容生产而不在运行时,那么把 AI 挂到成熟引擎上,是投入产出比最高的形态——而且这条路今天就能走。
13. P1/P2/P3 的季度级计划
P1(2027-05 → 2028-04,12 个月)
| 季度 | 主题 | 里程碑 |
|---|---|---|
| Q1 | 3D 渲染基础:Clustered Forward+、PBR、级联阴影 | 3D 场景跑通,中端机 30fps |
| Q2 | Metal 后端(Tier1)+ compute 路径 + 蒙皮动画 | M6:GPU 粒子跑通 |
| Q3 | 物理集成、材质图编辑器、Perf Agent | M9:Perf Agent 首个被采纳的优化 diff |
| Q4 | 抖音端 + MIR schema v1 冻结 + 双端上线 | M12 上线,M15 schema 冻结 |
P2(2028-05 → 2029-10,18 个月)
| 季度 | 主题 |
|---|---|
| Q1–Q2 | Virtual Geometry(Tier1 软件光栅版)+ SDF-DDGI |
| Q3 | Character Stack(皮肤/毛发/眼球)+ D3D12 后端 |
| Q4 | PC 端上线 + 构建农场 + 多人协作 |
| Q5–Q6 | 主机移植冒烟(Xbox ID@Xbox 先行)+ HWRT-DDGI |
P3(2029-11 起)
主机正式认证、大世界流式、运行时 AI(NPC/叙事)、商业化三选一(授权 / 云烘焙 / 垂直方案)。
P1 之后不再做详细计划。 12 个月后的细节一定会错,写细了只会让人误以为可控。P1 只做季度级,每季度末重排下季度。
14. 这版计划里最可能错的三处
诚实地标出不确定性,比假装精确有用:
- S8 包体攻坚的 2 周可能不够。 若 S7 结束主包已 >6MB,S8 需要扩到 4 周,直接从缓冲里扣,并砍 S9 的关卡数(10 → 6)。
- Agent 闭环(S5)可能拖到 S8。 这是全项目最不确定的部分。缓解:把 Guardian 校验器先做完(它是确定性的,好做),生成质量靠 prompt + 模板迭代,不追求一次到位。
- Studio(S6–S9)的 8 人周偏乐观。 Tauri 的离屏纹理共享在 macOS/Windows 上都有坑。若 S6 结束视口仍不通,降级为"弹出独立渲染窗口",不阻塞主线。
15. 一句话
这份计划的骨架是"三不可压缩 + 一个中间缓冲":包体、真机、审核三件事没法加速,所以它们必须早开始、占满 S8–S11;缓冲放在 S9–S10 之间而不是最后,因为最后的时间一定会被功能吃掉。
计划的成功不看"是否按期完成全部功能",而看Gate 0 的六项指标能不能用数字回答。
13 个双周迭代的逐条任务与人日、DoD、前 10 个工作日排班
10 · P0 迭代执行计划(26 周 / 13 个双周迭代)
09 号是总计划,回答"分几段、门在哪"。 这一份是排班表:每个迭代做什么、多少人日、什么算做完。
容量口径:每迭代 4 人 × 10 工作日 = 40 人日,按 85% 利用率 → 可承诺 34 人日。 表内人日合计 ≈ 34/迭代,超出即代表该迭代已经排满,不能再加需求。
0. 怎么用这份文档
- 周一看这张表:把本迭代的"任务"栏拆成这周的 5 件事,写进承诺清单。
- 周五看 DoD:DoD 的每一条都必须是能演示或能跑脚本验证的,不能是"基本完成"。
- DoD 不达标不要进下一个迭代——宁可砍本迭代范围,也不要带着半成品往前走。带病前进的债,在 S8 会以"包体超 900KB""帧时间抖 10ms"的形式一次性还回来。
人日口径:1 人日 = 1 个有效工作日(不含会议)。A=架构/编译链,B=引擎/渲染,C=工具/平台,D=TA/内容。
1. 迭代总表
| 迭代 | 周 | 主题 | 人日 | 里程碑 |
|---|---|---|---|---|
| S0 | W1–2 | 工程基线与语言决策 | 33 | D0 |
| S1 | W3–4 | MIR v0 | 35 | |
| S2 | W5–6 | 编译链骨架 + 首个 RHI | 35 | M0.2 |
| S3 | W7–8 | ECS + 2D 渲染 | 34 | |
| S4 | W9–10 | 音频与资源流式 | 34 | |
| S5 | W11–12 | Agent 闭环 v0 | 33 | |
| S6 | W13–14 | Magic Studio v0 | 34 | |
| S7 | W15–16 | 垂直切片 | 33 | M0.3 |
| S8 | W17–18 | 包体攻坚 | 33 | M0.5 |
| S9 | W19–20 | 内容量产与资产库 | 33 | |
| S10 | W21–22 | 稳定性与兼容 | 34 | M0.6 |
| S11 | W23–24 | 上线准备 | 30 | |
| S12 | W25–26 | 上线与 Gate 0 | 32 | Gate 0 |
缓冲(20 人日)不占日历周:它是每个迭代"40 人日容量 − 34 人日承诺 = 6 人日"里的一部分,分布在 S8–S11 优先使用。哪条线告急给谁,分配顺序固定为 包体 → 真机兼容 → Agent 质量。详见 09 号文档 §7.1。
工时账对得上:上表 13 个迭代合计 433 人日(≈86.6 人周);每迭代承诺 34 人日 × 13 = 442,余 9 人日。每迭代另有 6 人日未承诺(共约 78 人日 ≈ 15.6 人周),用于会议、联调、Code Review 与缓冲,正好对应 09 号文档的"过程开销 12 + 缓冲 4 = 16 人周"。 如果某个迭代排入超过 34 人日,说明它已经超载——砍范围,不要借钱。
2. S0(W1–2)工程基线与语言决策
目标:在写下 5 万行代码之前,确认三件事——wasm 能进微信、Rust 能调 C++、构建是确定性的。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | 仓库骨架 + Rust workspace(9 个 crate)+ 目录约定 | 2 | A | — |
| 2 | CI 三目标构建:host / wasm32-unknown-unknown / aarch64-apple-ios | 3 | A | 1 |
| 3 | wasm 最小运行时:WebGL2 context + 三角形 | 4 | B | 2 |
| 4 | 微信开发者工具最小工程:加载 wasm 并上屏 | 3 | C | 3 |
| 5 | me-rhi/ffi.h 设计 + C++ dummy 后端(模拟 GNM 的对象式接口) |
5 | A | — |
| 6 | Rust → C ABI → C++ 冒烟:三角形 + 调用开销测量 | 4 | A+B | 5 |
| 7 | 确定性构建基座:SOURCE_DATE_EPOCH、稳定排序、确定性 RNG |
3 | B | 2 |
| 8 | me CLI 骨架(build / fmt / validate 占位) |
3 | C | 1 |
| 9 | MIR schema v0 草案(SceneGraph 子集,约 40 个节点类型) | 4 | A | — |
| 10 | 工程规范 + PR 模板 + AI commit 规则(§R-AI-1~3) | 2 | C | — |
交付物:可构建的仓库、三目标 CI、FFI 冒烟测试报告、MIR schema v0 草案、规范文档。
DoD
- [ ] 微信开发者工具出画面(附截图,不是"能编译")
- [ ] release +
wasm-opt后.wasm≤ 800KB - [ ] FFI 单次调用开销 <2μs(含参数编组,10 万次平均)
- [ ] CI 三目标全绿,全量构建 ≤10 分钟,增量 ≤60 秒
- [ ] 同一 commit 两次构建产物 hash 一致
- [ ] D0 评审记录已归档:维持 Rust+C++,或回退纯 C++(ADR-001 状态更新)
风险:微信开发者工具对 wasm 的限制(体积、SIMD、内存上限)。若 800KB 达不到,先记录数字继续走,包体优化放 S8——但不能因此放弃"包体从 S2 就画曲线"这条纪律。
3. S1(W3–4)MIR v0
目标:让"场景"成为一种可读、可 diff、可往返验证的文本程序。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | 文本语法定稿(块语法 + 稳定属性顺序,不用 S-expression) | 3 | A | S0-9 |
| 2 | parser:手写递归下降,错误带行列号与修复建议 | 5 | A | 1 |
| 3 | AST → 强类型 IR(schema 驱动代码生成) | 4 | A | 1 |
| 4 | 规范化序列化器(排序规则、浮点格式、转义规则) | 4 | A | 3 |
| 5 | 二进制 mirb(版本头 + 零拷贝 + 可 mmap) |
4 | A | 4 |
| 6 | 往返校验 + 属性测试(proptest ≥1000 用例) | 3 | B | 5 |
| 7 | me fmt / me validate 实装 |
4 | C | 4 |
| 8 | 校验规则首批:引用完整性、类型、循环依赖、预算字段合法性 | 3 | A | 3 |
| 9 | 示例场景 12 个(覆盖 2D 全要素:精灵/文本/九宫/层级/九层遮罩) | 3 | D | 7 |
| 10 | MIR 语法手册(20 个节点类型 + 10 个反例) | 2 | C | 1 |
DoD
- [ ] 1000 节点场景
mir → mirb → mir字节级等义 - [ ] parser 报错含行号、列号、期望 token、最可能修复(抽查 10 个错误样例)
- [ ] 12 个示例全部通过
me validate - [ ] proptest 1000 用例零失败,模糊测试跑 10 万次无 panic
- [ ]
me fmt幂等(format 两次结果相同)
为什么不用 S-expression:MIR 要被人和 LLM 同时读写。S-expr 对机器友好、对人可读性差,且 diff 时嵌套层级难对齐。块语法 + 稳定排序,diff 噪声最小——这条直接影响 AI 改动的可 review 性。
4. S2(W5–6)编译链骨架与第一个真实渲染闭环
目标:打通 .mir → 屏幕,并让"预算"第一次真正拦住东西。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | pass 框架:注册、依赖、单独运行、逐 pass 日志 | 4 | A | S1 |
| 2 | legalize:Tier0/Tier1 能力表 + 首批降级规则 | 5 | A | 1 |
| 3 | budget:树形分配、父子借贷、超限定位到节点 | 5 | A | 1 |
| 4 | lower:2D 场景 → 绘制列表 + 图集分配 | 4 | B | 1 |
| 5 | RHI WebGL2:buffer / texture / pipeline / draw / command buffer | 6 | B | S0-6 |
| 6 | 着色器生成器 v0:材质节点 → GLSL(全程无手写) | 4 | B | 5 |
| 7 | link/pack:单包最小产物(本迭代不做分包) | 3 | C | 4 |
| 8 | 预算错误 UX:文案 + 建议("删 3 个装饰物可满足") | 2 | C | 3 |
| 9 | 基准场景 3 个(10 / 100 / 1000 sprite) | 2 | D | 7 |
DoD
- [ ]
.mir文本 → 编译 → 屏幕出现带纹理的四边形,无一行手写 GLSL - [ ] 把某节点
tri预算改成 10,编译失败,报错精确到节点 id + 超出比例 - [ ] 三个基准场景都能出图,绘制列表正确(抽查 20 帧)
- [ ] 编译链可
--dump-pass=budget输出中间结果 - [ ] 编译一个 100 节点场景 <200ms
风险:着色器生成器容易滑向"半个 Shader Graph"。本迭代只支持 5 种材质节点(常量、贴图、乘法、lerp、UV 变换),够用即可。
5. S3(W7–8)ECS 与 2D 渲染器
目标:运行时骨架成型,且性能数字是可测的。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | archetype 存储 + 组件注册 + 句柄表 | 5 | B | S0 |
| 2 | HierarchyIndex(父子稀疏数据)+ 变换传播 | 4 | B | 1 |
| 3 | 系统调度器:静态依赖图 + 单线程时间切片(2ms 预算) | 4 | B | 1 |
| 4 | 内存模型:FrameArena / AssetHeap / Persistent | 3 | B | 1 |
| 5 | 2D 批处理渲染器:排序、合批、状态缓存 | 5 | B | S2-4 |
| 6 | 图集打包工具 + 运行时图集切换 | 4 | C | S2-7 |
| 7 | 文本渲染:位图字体 + 动态字形缓存 | 3 | C | 5 |
| 8 | 输入系统 + 微信触摸适配 | 3 | C | — |
| 9 | 性能基准 + 自动回归阈值(±5% 报警) | 3 | B | 5 |
DoD
- [ ] 1000 sprite @60fps(Mac Chrome,帧时间 p95 ≤16ms)
- [ ] 1000 sprite 批次数 ≤8(批处理有效性的直接证据)
- [ ] 微信开发者工具内 ≥30fps
- [ ] 时间切片生效:单个系统超 2ms 时跨帧续跑,不阻塞渲染
- [ ] 性能基准已进 CI,超阈值自动报警
6. S4(W9–10)音频与资源流式
目标:让场景"有声音、能切换、不白屏",并把 SfxForge 接到运行时。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | 轻量混音器:16 通道、总线、距离衰减、单声道定位 | 4 | C | S3 |
| 2 | wasm DSP 最小集:噪声/滤波/包络/混响(复用 SfxForge 配方) | 5 | C | 1 |
| 3 | 音频管线:配方 → 烘焙 ogg 或运行时合成(按包体预算自动选) | 3 | C | 2 |
| 4 | 场景音景:分层音床 + 随机变体调度 | 3 | C | 2 |
| 5 | 资源系统:AssetHandle、引用计数、按分包粒度整体卸载 | 5 | B | S3-4 |
| 6 | 流式加载:占位资产 → 渐进精化 + 相邻分包预下载 | 5 | B | 5 |
| 7 | 微信 PAL:FS、网络、分包加载 API | 4 | C | — |
| 8 | 场景切换:无白屏(<300ms,含资源预热) | 3 | B | 6 |
| 9 | 音频 CPU 预算测量(16 通道同时播放) | 2 | B | 1 |
DoD
- [ ] 场景切换 <300ms,无白屏、无掉帧尖峰(附 trace 截图)
- [ ] 16 通道同时播放,混音 CPU <1.5ms/帧
- [ ] 分包卸载后内存回落 ≥90%(heap 快照对比)
- [ ] 音景正确叠加:单听 vs 整场景叠加,主观差异明显(必须人耳确认)
- [ ] 位置音源全部单声道(脚本扫描断言)
音频这块有一条纪律:生成式音频(08 号文档)只做工具期烘焙,运行期零推理。 一段 20s/48kHz 立体声未压缩 3.8MB,超过整个主包预算。
7. S5(W11–12)Agent 闭环 v0
目标:AI 第一次能产出可编译、可 review、可回滚的 MIR。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | Agent 运行时骨架:Intent → Task DAG → 执行器(可中断/重试) | 4 | A | S2 |
| 2 | Planner:意图 → 任务分解(prompt 模板带预算约束) | 4 | A | 1 |
| 3 | Builder:MIR patch 生成(schema 约束解码,不允许自由文本) | 5 | A | S1-3 |
| 4 | Guardian:确定性校验器(不调模型),20 条规则 | 4 | A | 3 |
| 5 | Patch 应用器:三方合并 + 冲突检测 + 单步回滚 | 3 | A | 3 |
| 6 | Provenance:seed / model_version / recipe_hash + 一条复现命令 |
3 | A | 5 |
| 7 | AI Workbench 最小界面:意图输入、MIR diff、渲染前后对比 | 5 | C | — |
| 8 | 评测集:20 个生成任务 + 人工评分标准(可接受/需修改/不可用) | 3 | D | — |
| 9 | --no-ai 复现开关 + 回归测试 |
2 | B | 6 |
DoD
- [ ] "生成一个 3 关的跳跃玩法关卡" → 产出可直接编译的 MIR
- [ ] 20 任务评测:一次通过率 ≥70%(基线,S9 要提到 80%)
- [ ] 故意注入 20 个坏 patch,Guardian 拦截 ≥19 个
- [ ]
--no-ai重跑与原始产物逐字节一致 - [ ] 每一步 patch 可单独撤销(不是"重新生成")
这是全项目技术风险最高的一环。 缓解办法是顺序:先做 Guardian(确定性,好做),再做 Builder(难)。如果 Builder 质量不达标,Guardian 仍然有用——它能拦住其他所有来源的错误。
8. S6(W13–14)Magic Studio v0
目标:编辑器能替代文本编辑,且"看到的 = 玩家看到的"。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | Tauri 壳 + React 布局 + IPC(MessagePack) | 5 | C | — |
| 2 | me-studio 服务:文档模型 + 命令栈(undo/redo) |
5 | C | 1 |
| 3 | 视口:引擎离屏渲染 → 共享纹理(macOS 优先) | 6 | B | S3-5 |
| 4 | 场景树 + 拖拽 + 多选 | 4 | C | 2 |
| 5 | Inspector:MIR 节点属性编辑(所见即 MIR) | 4 | C | 2 |
| 6 | 热重载 L1:材质/光照参数 <50ms | 3 | B | 3 |
| 7 | 热重载 L2:结构变更 <300ms,实体按 id 映射 | 4 | B | 6 |
| 8 | 构建发布面板:一键构建 + 包体分析 | 3 | C | S2-7 |
DoD
- [ ] 编辑器改属性 <50ms 生效,游戏不中断
- [ ] 拖拽摆 50 个物件,视口 ≥55fps
- [ ] 进场景/出场景/删节点,undo 20 步全部正确
- [ ] 点"构建"出微信包,并在开发者工具里跑起来
- [ ] 视口画面与真机截图一致(同机位 diff)
已知风险(预先定好降级):Tauri 的跨进程共享纹理在 macOS/Windows 都有坑。若 S6 结束视口仍不通,降级为"弹出独立渲染窗口"——牺牲一体化体验,保住主线,S9 再回来补。
9. S7(W15–16)垂直切片与 M0.3
目标:第一次完整的"能玩",并让真机性能可归因。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | 玩法框架:状态机、关卡流程、结算 | 5 | D | S6 |
| 2 | 关卡 1–3:手工 + AI 混合生产 | 6 | D | 1 |
| 3 | UI 层:HUD、菜单、弹窗 | 4 | C | 1 |
| 4 | 真机 profile 隧道:WebSocket trace → 编辑器 | 5 | B | — |
| 5 | 归因:draw call → MIR 节点链 | 4 | B | 4 |
| 6 | 存档 / 设置(本地存储 + 跨端) | 3 | C | 1 |
| 7 | 首次真机全流程测试 + 问题清单 | 3 | D | 2 |
| 8 | 性能预算热力图 v0(编辑器内高亮超预算节点) | 3 | C | 4 |
DoD
- [ ] 3 分钟完整玩法循环(开始 → 3 关 → 结算 → 重开)
- [ ] 中端安卓真机稳定 30fps,帧时间 p95 ≤33ms
- [ ] 峰值内存 <600MB
- [ ] trace 能从真机回传到编辑器并归因到 MIR 节点
- [ ] 一份 ≥10 条的真机问题清单(这份清单是 S8 的输入)
10. S8(W17–18)包体攻坚与 M0.5
目标:把主包压进 4MB,把首帧压进 3 秒。这是全项目最不可压缩的一环。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | 纹理压缩管线:ASTC / ETC2 + 质量档 + 按用途自动选 | 5 | B | S7 |
| 2 | wasm 体积:wasm-opt -Oz、panic=abort、no_std 子集、去 fmt |
5 | B | S0-3 |
| 3 | 音频:opus 低码率 + 大文件改运行时合成 | 3 | C | S4 |
| 4 | 分包图划分:按场景可达性做图划分(最小割近似) | 5 | A | S4-6 |
| 5 | 包体分析器:按模块/资产类型归因,输出可执行建议 | 4 | C | — |
| 6 | 首帧优化:首场景最小化 + 占位资产 + 并行下载 | 5 | B | 4 |
| 7 | 真机安装验证 5 台(含最差机型) | 4 | D | 1 |
| 8 | 冷启动埋点(分段计时:wasm 加载/初始化/首场景) | 2 | C | 6 |
DoD
- [ ] 主包 ≤4096KB,微信真机安装验证(不是构建产物估算)
- [ ] 冷启动首帧 <3s(中端安卓 + 4G)
- [ ] 总包 ≤30MB,分包加载成功率 ≥99.5%
- [ ] 包内无未压缩贴图(脚本断言)
- [ ] 包体归因表:每 100KB 都能说清属于谁
纪律:包体曲线从 S2 就要画。如果 S7 结束时主包已经 >6MB,S8 需要 4 周 → 从缓冲扣 1 周 + S9 关卡数 10→6。
11. S9(W19–20)内容量产与资产库
目标:把"AI 能生成一个关卡"变成"AI 能生成十个关卡且质量稳定"。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | 世界语法 v0:8 条布局规则(乡村 / 小镇 / 室内各一组) | 5 | D | S6 |
| 2 | 构件库 v0:120 个构件 + 元数据(结构规格 + 语义标签) | 5 | D | — |
| 3 | 资产库双索引:语义向量 + 结构规格过滤 | 5 | A | 2 |
| 4 | 规范化闸门:重拓扑/UV/单位朝向/LOD/材质烘焙 + 拒绝原因 | 5 | B | 2 |
| 5 | 关卡生成器:语法 + 构件 + 预算约束 → MIR | 5 | A | 1,3 |
| 6 | 关卡 4–10 生产(AI 生成 + 人工 review) | 5 | D | 5 |
| 7 | 采纳率统计脚本(按 MIR 节点数,非人工估算) | 3 | C | — |
DoD
- [ ] 10 个关卡全部可玩,无阻塞性 bug
- [ ] AI 生成占比 ≥50%(脚本统计,附报告)
- [ ] review 一次通过率 ≥80%
- [ ] 库内 ≥120 构件,100% 带结构元数据(缺元数据的拒绝入库)
- [ ] 生成一个新关卡 <10 分钟(含 review 前的人工确认)
冷启动现实:库 <500 个资产时,智能复用会空转。所以这一迭代的重点不是"库大",而是"配方优先"——8 条世界语法带来的变化量,远大于 120 个孤立的模型。
12. S10(W21–22)稳定性与兼容
目标:把"能玩"变成"能上线"。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | 5 台真机兼容矩阵(3 安卓低中端 + 2 iOS) | 6 | D | S8 |
| 2 | 崩溃收集 + 符号化(wasm 栈还原) | 4 | C | — |
| 3 | 内存排查:30 分钟循环 + heap 快照 diff | 5 | B | — |
| 4 | 低端机降级:动态分辨率 + 画质档位下发 | 4 | B | 1 |
| 5 | 弱网 / 断网 / 后台切换 / 来电中断 | 3 | C | — |
| 6 | 留存埋点 + 漏斗(进入/过关/失败/退出) | 3 | C | — |
| 7 | 压力测试:连续 100 次场景切换 | 3 | B | 3 |
| 8 | Bug 修复缓冲 | 6 | 全员 | 1–7 |
DoD
- [ ] 连续 30 分钟游玩,无崩溃,内存增长 <5%
- [ ] 5 台真机全绿(附机型 + 帧时间 + 内存)
- [ ] 断网可恢复,后台切回不崩、不黑屏
- [ ] 100 次场景切换后内存回到基线 ±3%
- [ ] 崩溃栈可符号化到 MIR 节点/函数名
13. S11(W23–24)上线准备
目标:过审、灰度、可回滚。这一迭代的外部依赖最多,必须留足时间。
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | 隐私协议、用户协议、权限最小化 | 3 | D | — |
| 2 | 审核自查(微信审核要点逐条)+ 整改 | 5 | D | — |
| 3 | 广告 SDK + 激励视频(如采用 IAA 模式) | 4 | C | S10 |
| 4 | 埋点校验 + 后台看板 | 4 | C | S10-6 |
| 5 | 灰度方案:5% → 20% → 100% + 回滚预案 | 3 | A | — |
| 6 | 提审 + 应对驳回(预留 2 轮) | 6 | D | 2 |
| 7 | 运营素材:icon / 截图 / 简介 / 分类 | 3 | D | — |
| 8 | 客服与反馈通道 | 2 | D | — |
DoD
- [ ] 提审通过(或明确知道被拒原因与修复路径)
- [ ] 灰度 5% 运行 48 小时无 P0 故障
- [ ] 回滚演练成功(一次真实回滚,不是纸面预案)
- [ ] 埋点数据在后台可查、口径正确
审核是黑盒。唯一有效的应对是"提前一轮"——不要等 S11 才第一次提审。
14. S12(W25–26)上线与 Gate 0
| # | 任务 | 人日 | 主责 | 依赖 |
|---|---|---|---|---|
| 1 | 全量发布 + 监控值守(前 72 小时) | 5 | D | S11 |
| 2 | 留存与漏斗分析(7 日) | 4 | D | 1 |
| 3 | 工时台账汇总 + 与 Cocos 路线对比测算 | 5 | A | — |
| 4 | 六项指标核对表(每项给出数字与采集方式) | 4 | A | 1–3 |
| 5 | Gate 0 评审报告 + P1 范围建议 | 6 | A | 4 |
| 6 | 技术债清单 + 架构复盘(哪条 ADR 要改) | 4 | B | — |
| 7 | 文档收尾:MIR 手册、构建手册、FAQ | 4 | C | — |
DoD
- [ ] 游戏真实上线,有真实用户与真实留存数据
- [ ] Gate 0 报告:六项指标每一项都有数字,不接受"感觉良好"
- [ ] 工时台账可用:每关卡人工小时 + AI 占比 + 与 Cocos 估算的对比
- [ ] P1 范围建议明确(做 3D / 只做工具 / 暂停)
- [ ] 技术债清单按"是否阻塞 P1"分级
15. 每个迭代的固定检查点
| 时点 | 检查 | 不合格的动作 |
|---|---|---|
| 周一 | 承诺清单 ≤5 项,每项有验收方式 | 超过 5 项 → 砍到 5 项 |
| 周三 | 中期检查:是否出现"预计延期" | 出现即上报,不等到周五 |
| 周五 | Demo(只演示能跑的)+ 8 指标更新 | 演示不了 = 没做完 |
| 迭代末 | DoD 逐条勾选 | 有未勾选 → 不带入下迭代,砍范围 |
15.1 连续两个迭代完成率 <70% 时的强制动作
按顺序执行,不再讨论:
- 砍掉本阶段"可以后补"清单里最上面的一项(见 04 §9 右列)
- 若仍不足,砍内容量(10 关 → 6 关)
- 若仍不足,触发 09 §12 方案 B(单人 MVP)或方案 C(只做工具)
不允许的应对方式:延期、加人、降低 DoD 标准。前两个在这个阶段无效,第三个会让 Gate 0 失去意义。
16. 前 10 个工作日(S0 逐日)
这一段最值得写细,因为它是唯一"下周就要执行"的部分。
| 日 | A(架构/编译链) | B(引擎/渲染) | C(工具/平台) | D(内容) |
|---|---|---|---|---|
| D1 | 仓库骨架、workspace、目录约定 | 读架构文档,列 RHI 接口草案 | 微信开发者工具环境搭好 | 确认首个项目的玩法方向 |
| D2 | CI 三目标构建 | wasm + WebGL2 三角形 | 微信最小工程跑通 | 玩法原型纸面推演 |
| D3 | ffi.h 第一版 |
三角形上屏调通 | wasm 加载进微信 | 关卡结构草案 |
| D4 | ffi.h 评审 + C++ dummy 后端 |
调用开销测量脚手架 | me CLI 骨架 |
美术需求清单 |
| D5 | C++ dummy 后端完成 | 确定性构建基座 | CLI 接入 CI | 资产采购/外包询价 |
| D6 | FFI 冒烟:Rust→C++ 三角形 | 修确定性(时间戳/路径/顺序) | PR 模板 + 规范文档 | 12 个示例场景规划 |
| D7 | 冒烟通过 + 开销数据 | 二次构建 hash 对比 | 微信包体基线测量 | 示例场景 1–3 |
| D8 | MIR schema v0(节点类型 1–20) | wasm 体积基线 + 优化试验 | me fmt 设计 |
示例场景 4–6 |
| D9 | MIR schema v0(21–40) | 体积报告 | me validate 设计 |
示例场景 7–9 |
| D10 | D0 评审材料 | D0 评审材料 | D0 评审材料 | 示例场景 10–12 |
D10 的评审只需要回答一个问题:要不要按这个语言组合往下写 5 万行代码。
17. 一句话
这份排班表唯一的硬规则是"DoD 不达标不许进下个迭代"。 13 个迭代里有 3 个(S8 包体、S10 兼容、S11 审核)受外部约束、无法靠加班压缩,所以它们必须准点开始;其余 10 个都可以通过砍范围来保时间。 砍范围不丢人,带病前进才丢人。
四类冲突、扇区模型、crate 内分权、共享面设计、并发度曲线、同步协议、守卫脚本实测
11 · 四 Agent 并行开发:扇区模型
09 号是总计划,10 号是排班表,这一份解决「四路并行怎么不互相踩」。
一句话结论:撞车的主因不是模块没拆好,是"拆到目录级"停手了。 目录拆完,每个目录内部还有共享文件;而共享文件才是撞车的唯一物理来源。
1. 先说清楚:撞车有四种,只有一种是 git 会报警的
| 类型 | 现象 | git 会报冲突吗 | 为什么"按模块拆"治不了 |
|---|---|---|---|
| ① 同文件双写 | 两人同时改 Cargo.toml / mod.rs / 索引文件 |
会 | 模块拆分不改变"这个文件只能有一个作者"的事实 |
| ② 契约漂移 | 没人冲突,但 A 改了 schema,B 编译不过 | 不会 | 最阴的一类。等编译报错时,B 已经写了 200 行"临时适配" |
| ③ 产物污染 | target/、生成物、site/ 互相覆盖 |
不会 | 谁都不认为自己在改产物,但清理动作会 |
| ④ 语义重复 | 两个人各实现了一份 ResourceLoader |
不会 | 目录不同、名字不同、功能一样,合并时才发现 |
你过去遇到的"那个模块没拆分好",绝大多数是 ② 和 ④。 它们的共同点是:git 全程安静,直到某一天突然全炸。
所以真正的解法不是"把模块拆得更细",而是四件事:
- 文件所有权(治 ①③)——每个路径只有一个 owner,机读、可断言
- 契约冻结(治 ②)——接口文件带版本号和哈希,改了必 bump
- 构建隔离(治 ③)——每个 Agent 一个 worktree、独立
target/ - 越界自动检测(治全部)——脚本判定,不靠自觉
2. 扇区,而不是模块
模块(crate)不等于扇区。 扇区是"写权边界",一个扇区可以跨多个 crate,一个 crate 也可以被两个扇区按文件切开。
最典型的例子是 me-rhi:
crates/me-rhi/src/ffi.h → A1(C ABI 定义)
crates/me-rhi/src/abi.rs → A1
crates/me-rhi/src/lib.rs → A2(实现)
crates/me-rhi/backends/** → A2(各后端)
crates/me-rhi/Cargo.toml → A2
为什么必须这样切:C ABI 一旦由实现方自己定,主机 SDK 那一层(GNM / GDK,全 C++ 且受 NDA)就再也换不动了。这不是洁癖,是 ADR-001 里那条"语言可换"的前提。
按 crate 拆的话,这条约束根本无法表达——这就是"模块拆了还是会撞"的具体机制。
3. 四个扇区
| Agent | 代号 | 职能 | 核心产出 | 谁依赖他 |
|---|---|---|---|---|
| A1 | kernel |
内核与编译链 | interface/**、MIR、编译器、me-core |
全部 |
| A2 | runtime |
运行时与渲染 | RHI 后端、ECS、2D 渲染、音频 | A4 |
| A3 | tools |
平台与工具链 | PAL、CLI、Studio、构建与 CI | 全部 |
| A4 | content |
内容与验证 | 世界、资产库、测试、发布验证 | 无(他是消费者) |
依赖是单向的:A1 定义 → A2/A3 实现与消费 → A4 使用。A4 在链条末端,全靠反馈说话。
A4 有一条硬规则:一行引擎代码都不许改。 他改不动的地方,就是接口设计有缺陷的证据。如果他能随手改引擎让它跑通,这个缺陷会一直留到第三方开发者手里。
这条规则把"撞车"转成了"信号"——这也是四个角色里唯一"越被动越有价值"的设计。
四个 Agent 的完整扇区定义(含可写 glob、只读区、禁区、每迭代 FTE)在 plan/agents.json,是机读的唯一事实源。
4. 把"必然共享"设计掉——减少 90% 冲突的真正手段
大部分共享文件是可以被设计掉的,只是没人愿意在第一天做。这六条做完,冲突从"每次提交都可能撞"降到"少数真冲突":
| # | 手法 | 消灭哪一类冲突 |
|---|---|---|
| 1 | 根 Cargo.toml 用 members = ["crates/*"] |
"新增 crate 必改根文件" |
| 2 | mod 声明由 build.rs 生成到 src/generated/ |
"两个人都要往 lib.rs 加一行 mod" |
| 3 | 注册表分片:一目录一文件,启动时扫目录 | "三个人的组件都注册在同一个数组里" |
| 4 | 索引与产物一律脚本产出(进 forbidden) |
手改生成物后又被脚本覆盖 |
| 5 | CHANGELOG.md 按日期分区 append |
整文件重写导致的整段冲突 |
| 6 | trait 与 impl 分文件 | 接口定义和实现抢同一个文件 |
第 1 条最容易被忽略,但它消灭的是最高频的那类冲突。 目录结构永远比文件内容稳定——把"变化的东西"放进目录,把"稳定的东西"放进文件。
5. 并发度:四个 Agent 不等于四个同时在跑
关键路径本身是串行的。S0 阶段 A3、A4 如果硬要开工,只能做无效功——因为骨架还没出来。
按 plan/agents.json 的 FTE 分配实测(每迭代 10 个工作日,预算 = 人日/10):
| 迭代 | 预算 FTE | A1 | A2 | A3 | A4 | 合计 | 同时活跃 |
|---|---|---|---|---|---|---|---|
| S0 | 3.3 | 1.3 | 1.0 | 0.8 | 0.2 | 3.3 | 3 |
| S1 | 3.5 | 1.5 | 0.9 | 0.8 | 0.3 | 3.5 | 3 |
| S2 | 3.5 | 1.0 | 1.5 | 0.7 | 0.3 | 3.5 | 3 |
| S3 | 3.4 | 0.6 | 1.4 | 1.0 | 0.4 | 3.4 | 4 |
| S4 | 3.4 | 0.3 | 1.4 | 1.0 | 0.7 | 3.4 | 3 |
| S5 | 3.3 | 1.1 | 1.0 | 0.7 | 0.5 | 3.3 | 4 |
| S6 | 3.4 | 0.5 | 1.0 | 1.3 | 0.6 | 3.4 | 4 |
| S7 | 3.3 | 0.2 | 1.1 | 0.7 | 1.3 | 3.3 | 3 |
| S8 | 3.3 | 0.2 | 1.3 | 0.5 | 1.3 | 3.3 | 3 |
| S9 | 3.3 | 0.2 | 0.8 | 0.8 | 1.5 | 3.3 | 3 |
| S10 | 3.4 | 0.5 | 1.2 | 0.5 | 1.2 | 3.4 | 4 |
| S11 | 3.0 | 0.3 | 0.7 | 1.0 | 1.0 | 3.0 | 3 |
| S12 | 3.2 | 0.3 | 0.5 | 1.1 | 1.2 | 3.1 | 3 |
("活跃" = FTE ≥ 0.4。全部 13 个迭代合计 43.2 FTE·迭代,预算合计 43.5,无超支。)
结论:四个 Agent 同时有实质任务的窗口只有 S3、S5、S6、S10 四个迭代,共 8 周。 其余 18 周都是 2–3 个。
这不是排期偷懒,是盲目四并发反而更慢:多出来的那个 Agent 要么在等接口,要么在写后来要推翻的东西。开四个对话之前先看这张表——多数时候你只需要开三个。
6. 同步协议:四个节奏
| 节奏 | 动作 | 谁做 | 不做的后果 |
|---|---|---|---|
| 每次提交 | check_ownership.py files --as <id> |
各 Agent 自己 | 越界累积到合并时才爆 |
| 每天开始 | git rebase 到主干,跑一次构建 |
各 Agent | 分支活过 3 天,合并成本翻倍 |
| 契约变更时 | 改 interface/ + bump 版本 + 更新 MANIFEST + 通知消费方 |
契约 owner | 静默漂移,消费方编译期踩雷 |
| 每周一次 | 集成窗口:全员 rebase + 全量构建 + 跑 tests/ |
A3 主持 | 只在最后集成 = 一次炸四个 |
契约提案是唯一允许的跨扇区通道。 流程固定四步:
- 提出方写清:要改哪个文件、加什么、为什么本扇区解决不了、影响多少处调用点
- owner 决定:接受(bump 版本)/ 拒绝(给替代方案)/ 延后(进 backlog)
- 接受了:owner 改
interface/+MANIFEST.json的version与sha256 - 消费方同步升级,编译器断言版本号
没有第 5 步"我先在自己这边临时改一下"。 那一步之后两份接口就永久分叉了。
7. 开工:四个对话怎么开
# 1. 每个 Agent 一个独立 worktree(共用 .git,互不干扰)
cd /path/to/magic-engine
git worktree add ../me-A1 -b agent/A1
git worktree add ../me-A2 -b agent/A2
git worktree add ../me-A3 -b agent/A3
git worktree add ../me-A4 -b agent/A4
# 2. 每个 worktree 独立构建目录,不抢 target/ 锁
export CARGO_TARGET_DIR=/tmp/me-target-A1 # A2/A3/A4 同理
为什么必须 worktree 而不是同一个目录切分支:同一个工作目录下切分支,会把别人未提交的改动一起带走——这是最隐蔽也最致命的一种"撞车"。
然后四个对话分别粘 plan/agent-briefs/ 下对应的那份。简报是自包含的,因为新对话没有上下文。
开对话的顺序:先 A1、A3(S0 关键路径在 A1,共享设施在 A3),S2 再加 A2,S3 再加 A4。不要第一周就开四个。
8. 守卫脚本:tools/ci/check_ownership.py
三个检查,全是确定性判定,plan/agents.json 是唯一事实源。
python tools/ci/check_ownership.py files --as A2 --git # 越界断言
python tools/ci/check_ownership.py contracts # 契约静默漂移
python tools/ci/check_ownership.py dupes # 跨 crate 同名符号
匹配规则:取"最具体"(非通配字符最多)的那条 glob,所以 crates/me-rhi/src/ffi.h(A1)能压过 crates/me-rhi/src/**(A2)。判定顺序是 forbidden → generated → shared → owned → unowned。
实测结果(真跑,非设计意图)
| 场景 | 输入 | 结果 |
|---|---|---|
| A2 顺手改了 C ABI | interface/rhi.ffi.h + 自己的后端 |
FAIL 属于 A1,退出码 1 |
| A4 改了引擎代码 | crates/me-runtime/src/ecs.rs |
FAIL 属于 A2,退出码 1 |
| 同一 crate 内分权 | me-rhi 的 5 个文件 | ffi.h/abi.rs → A1,其余 → A2,全部 ok |
| 生成物 | library/sfx/index.json、ogg/** |
note 生成物:须由脚本产出(不 FAIL,见局限) |
| 纯本地产物 | target/**、library/sfx/wav/** |
FAIL 纯本地产物,永不进版本控制 |
| 新目录未登记 | crates/me-net/src/lib.rs |
warn 未注册扇区 |
| 契约静默漂移 | 往 rhi.ffi.h 追加注释、不 bump |
DRIFT,退出码 1,sha256 从 ff2eec… 变为 e8d503… |
| 还原后 | git checkout -- interface/rhi.ffi.h |
sha256 回到 ff2eec…,通过:无静默漂移 |
真实仓库的覆盖验证
假的路径测一百遍,不如拿真仓库跑一次。首次提交 80 个文件跑下来,当场暴露两个设计错误:
错误一:把"进版本控制的生成物"放进了 forbidden。 初始提交合法地新增了 31 个 ogg 和 index.json,却被判 33 处 FAIL。
根因是把"不许手改"和"不许提交"混成了一件事。修正后语义分开:
forbidden= 纯本地产物,永不进版本控制(target/、wav 中间产物、build.rs生成物)generated= 由脚本产出但会提交(ogg、index.json、scenes、site、gantt)
错误二:仓库根的基础设施文件没登记。 .gitignore、.gitattributes、build_site.py 全部 warn 未注册扇区。
修正后归 A3(构建设施)。
修正后:80 个文件 → 42 ok + 38 note,0 FAIL,0 warn。
过程中的真实缺陷修复:--base main 原本被实现成 git diff main HEAD,比较的是两次提交而不是"工作区 vs 基线",所以提交前自检永远返回空集。改成 git diff main 后才真正可用——这类 bug 只有真跑才会出现,纯看代码看不出来。
过程中修掉一个真实缺陷:library/sfx/index.json 最初被判给了 A4 的 library/**,因为生成物判定排在归属判定之后。顺序错了,生成物就会被当成"可手改"——这是最隐蔽的一类污染源。修正后 generated 提前到 owned 之前。
已知局限(不掩饰)
dupes只能抓同名符号,抓不到"名字不同、功能重复"。语义重复最终仍要靠人 review。- 不能阻止两个人同时改不同文件但破坏同一个不变量(比如"资源释放顺序")。这类只能靠测试。
--as依赖提交者如实声明身份。真正的强制点在 CI:由 CI 从分支名agent/A2推出身份,不接受自报。- 手改生成物不会 FAIL,只会
note。这不是漏了,是想清楚了:判"文件是否手改"要靠内容比对,而生成物的兜底本来就该是生成器的确定性——forge.py两次构建 MD5 一致、genai.py verify的 sha256 校验,比文件归属检查更可靠。归属检查管"谁写的",确定性检查管"是不是算出来的",两件事不要混。
9. 什么时候说明拆分错了
四个可观测信号。出现任意一个,先改扇区划分,不要再加 Agent:
| 信号 | 具体阈值 | 说明什么 |
|---|---|---|
| 契约提案频繁 | 一周超过 2 次 | 扇区切在了不该切的地方,把该归一个人的东西拆开了 |
| 越界反复 | 同一文件连续 2 周被 2 个 Agent 越界 | agents.json 写错了,不是纪律问题 |
| 集成持续失败 | 连续 2 个集成窗口构建不过 | 共享面还没设计掉,第 4 节那六条没做完 |
| 等待时间上升 | 任一 Agent 每周 > 1.5 天在等上游 | 依赖方向错了,或有扇区应该合并 |
"多开一个 Agent"从来不是解药。 四个扇区对应四条真实依赖链,第五条链不存在——多开的那个只会制造冲突。
一条补充规则:架构角色不计入扇区
扇区定义本身(plan/agents.json、docs/、共享骨架、CI 守卫)由架构角色写,它不参与四路并行。
但这条必须收紧使用:一旦进入日常开发,架构角色的改动也要交回对应扇区 owner。 否则守卫会退化成"有例外的人"——那比没有守卫更糟,因为它给越界提供了一个永远可用的借口。
写这份文档时守卫就抓到了我自己一次:tools/ci/check_ownership.py 按扇区属于 A3(构建设施),我以 A1 身份写它被判定越界。这恰好说明规则是有效的——它没有因为作者是"制定规则的人"就放过。
Rust 内核 + C++ RHI 后端
ADR-001:内核语言选择 —— Rust 内核 + C++ RHI 后端
Status
Proposed(有条件接受,需 P0 第 8 周完成 FFI 冒烟测试后转 Accepted)
Context
引擎需要同时覆盖:WASM(小游戏)、移动原生、PC、主机。
- 主机 SDK(PS5 GNM/GNMX、Xbox GDK、Switch NVN)只提供 C++ 接口,且受 NDA 保护
- 小游戏要求主包 ≤4MB,WASM 体积是硬指标
- 工具链(编辑器、资产管线、编译器)的开发效率直接决定产品迭代速度
- 团队规模小,无法承担"两套语言、两套构建系统"的维护成本
Options
| 方案 | 优势 | 劣势 |
|---|---|---|
| A. 纯 C++(UE 路线) | 主机最顺、性能天花板最高、招人容易、生态最全 | 内存安全问题长期累积;编译慢;工具链开发效率低;WASM 产物体积大;无内置包管理 |
| B. 纯 Rust | 内存安全、并发友好、cargo 生态、工具链开发极快、WASM 一流支持 | 主机 SDK 无法直接调用;渲染后端仍需 C++;引擎人才稀缺;学习曲线陡 |
| C. Rust 内核 + C++ RHI 后端(C ABI 边界) | 兼顾:工具链/编译器/ECS/Agent 用 Rust(迭代快、安全);渲染后端贴 SDK 用 C++(唯一可行) | 跨语言调试成本;需自建 C ABI 绑定层;构建系统双轨 |
Decision
采用 C:Rust 承担 L5–L8(运行时、渲染器上层、编译器、Agent、编辑器后端),C++ 承担 L3 的 RHI 后端实现(Metal/Vulkan/D3D12/GNM/GNMX/GDK),边界是手写 C ABI(me-rhi/ffi.h)。
约束条件:
- C ABI 边界必须极窄:只暴露 command buffer 录制、资源创建、PSO 创建、sync 原语。不做对象级高频调用(避免 FFI 开销)。
- 不允许 C++ 侧回调到 Rust 的业务逻辑(除 allocator 与 log)。控制流单向:Rust 驱动,C++ 执行。
- P0 第 2 周(S0 的 D0 门)必须完成冒烟测试:在主机 SDK 环境(或伪造的 C++ 后端)上,验证 Rust → C ABI → C++ 后端能跑通一个三角形。
原定第 8 周,编排 P0 计划时提前到第 2 周。理由:改语言的成本在写第一行业务代码前是 2 周,在 S6 之后是 4 个月——决策越早越便宜,没有理由等。
- 若冒烟测试失败或成本超预期,回退到 A(纯 C++),此时 L3 以上的架构文档与 C ABI 设计不变,只替换实现语言。
Consequences
变容易: - 编译链、MIR 解析、Agent 编排这类"逻辑密集"代码的开发速度提升显著(Rust + cargo 的迭代速度约为 C++ 的 2–3 倍) - WASM 产物体积与启动性能更优(无 libstdc++ 拖累) - 内存安全在长期(5 年+)显著降低崩溃率 - 编译器(Rust)能强制模块边界(workspace + crate 可见性)
变困难: - 跨语言调试需要专门基建(gdb/lldb 混合栈、tracing 关联) - 招募:懂 Rust + 图形学的工程师国内稀缺且贵 - 主机平台的构建流程需双轨(Rust 交叉编译 + MSVC/Clang 后端) - 一旦 C 边界设计错了(例如暴露了太细的对象),后期重构成本高
可逆性:中高。 因为边界在 C ABI,语言替换是"重写 L5–L8",不是"重写整个项目"。这是选 C 而非 B 的关键理由——它保留了回退到 A 的路。
附:为什么不用 C# / TypeScript 做内核
- C#(Unity 路线)需要 IL2CPP 转换到 C++,转换层本身是巨大的不确定性来源,且小游戏包体不可控
- TS(Cocos 路线)在小游戏端表现好,但缺乏对内存与 SIMD 的控制,做不了高端渲染
- 两者都可以做脚本层,这也是 ME 的选择(L5 Script 支持 C#/TS 之一),但不是内核
wasm32 是一等编译目标
ADR-002:WASM 是一等编译目标(WASM-first)
Status
Proposed
Context
小游戏是 ME 的第一战场(微信 5 亿 MAU,抖音增长快)。而小游戏的运行环境是 WASM + WebGL2 / 原生 Metal(iOS),不是原生二进制。
现有引擎的路径是"AOT 转换":
| 引擎 | 小游戏方案 | 问题 |
|---|---|---|
| Unity | unity-webgl-transform:原生引擎 → WebGL 转换 | 转换层引入额外开销;il2cpp 产物大;需要大量裁剪;同步 IO 要改异步 |
| Unreal | 无官方支持 / 第三方 | 基本不可行 |
| Cocos | TS 原生,天然在 Web 上跑 | 优势明显,但缺高端渲染能力 |
| Godot | Web export,非小游戏定制 | 社区反馈适配困难 |
ME 若走"原生优先、再转 Web"的路,会重复 Unity 的所有坑,且无法在 4MB 主包里做出优势。
Decision
从第一天起,wasm32-unknown-unknown(或 wasi 子集)就是引擎的正式编译目标,不是附属产物。
具体约束:
- 禁止任何依赖"原生假设"的设计:
- 无阻塞 IO(所有资源加载必须异步,哪怕原生平台也一样)
- 不依赖 OS 线程做正确性(Job 系统必须支持"零线程退化",用时间切片)
- 不使用 C++ 异常做控制流(Rust 侧
panic=abort) - 文件系统抽象为"包 + 虚拟路径",不假设真实目录树 - 体积是设计约束而非优化项:主包预算 ≤1.2MB(gzip 后),每次 PR 检查体积回归
- 能力分级以"后端能力"为准(compute 有无),不以"平台名"为准,因为微信 iOS 侧已有原生 Metal
- 原生平台是另一个编译目标,允许有更多能力,但不允许有不同的语义
Consequences
变容易: - 小游戏端零转换损耗,启动快、包体可控 - 一套代码,多目标编译,无"平台特化分支"技术债 - 天然获得"沙箱友好"的设计(无全局可变状态、无隐式 IO)
变困难:
- 原生平台的性能会被"最保守假设"拖累(例如不能随意用线程池)——需要"渐进增强"设计:能力探测 + 可选路径
- 异步化贯穿全引擎,API 复杂度上升(回调/Future)
- panic 处理、错误传播需要统一设计(不能靠异常)
不可逆性:高。 这是全项目最不可逆的决策之一,必须在 P0 之前定死。
附:为什么不用 WebGPU 作为统一后端
WebGPU 很强(compute、显式管线),但:
- 微信小游戏当前主力仍是 WebGL2,WebGPU 覆盖不足
- 小游戏 iOS 侧的原生 Metal 后端已经提供了 compute/indirect,且性能更好
- WebGPU 在桌面浏览器可用,但 PC 游戏不走浏览器
结论:WebGPU 作为 Tier1.5 的可选后端(P2 再评估),不作为主力。
附:为什么不基于 Emscripten + C++(UE 风格)
技术上可行,但: 1. 产物体积难压(4MB 主包下没有余量) 2. GL 胶水层开销(微信已推 Emscripten GLX 模式缓解,但仍不如直接产 WASM + 精简绑定) 3. Rust 的 wasm32 产物更精简、无异常开销
如果 ADR-001 回退到纯 C++,此 ADR 需要重写。
MIR 是唯一真相源,AI 只操作 MIR
ADR-003:MIR 是唯一真相源,AI 只操作 MIR
Status
Accepted(架构基石,除非项目方向改变否则不复议)
Context
"从 0 融入 AI"有多种实现路径,架构不同,天花板也不同:
| 方案 | AI 的操作对象 | 可 review | 可复现 | 可回滚 | 跨端 |
|---|---|---|---|---|---|
| A. 命令式 API | 引擎运行时对象(createObject()) |
✗ | ✗ | ✗ | 差 |
| B. 直接改资产文件 | 二进制/blob 资产 | ✗ | 部分 | 难 | 中 |
| C. 操作结构化 IR | MIR(文本、带类型、带预算) | ✓ | ✓ | ✓ | 好 |
Decision
- MIR 是引擎的唯一真相源。 运行时不接受绕过 MIR 的资产直读。凡是不能表达为 MIR 的东西,AI 就碰不到,也就无法优化——这是刻意的取舍。
- Agent 与引擎之间只有三个 API:
mir.query/mir.patch/mir.measure。Agent 不得直接调用引擎 API,不得直接写文件。 - 所有 AI 产出必须是可 review 的 MIR diff,像 code review 一样过。
- Guardian(硬约束校验)必须是确定性代码,不是 LLM。
- MIR 节点必须携带三样元数据:
budget(预算)、provenance(生成溯源)、id(稳定标识)。
Rationale
为什么可复现如此重要: 如果 AI 生成不可复现,那么三年后没人能维护这个工程——改一个参数,整个场景"重新长一遍",且长出来的不一样。这会让项目变成一次性黑盒。
为什么预算必须在 MIR 里: 如果预算只存在于"目标平台配置"里,AI 就无法在生成时做正确选择,只能生成后被裁剪。局部化预算是"AI 能做对"的前提。
为什么硬约束不能交给 LLM: LLM 有幻觉,且在"看起来对"和"确实对"之间没有可靠界限。一个放过去的坏包就是线上事故。硬约束(引用完整性、预算、变体数、碰撞体闭合)必须是代码。
Consequences
变容易: - 所有 AI 改动可 review / 可回滚 / 可合并 / 可审计 - 多人协作回归正常 git 流程(对比 Unity prefab / UE uasset 的痛苦) - 跨端编译成为可能(MIR 是中间语言,天然多目标) - 测试与 CI 可以做全量静态校验
变困难: - 需要设计一套 MIR schema 及其演进机制(成本高,P1 末必须冻结) - MIR 文本序列化的性能与体积需要优化(不能成为运行期负担 → 所以有二进制形态) - 团队学习成本:开发者要理解"资产即程序" - 所有第三方格式都需要写导入器(不能直接吃 FBX 当运行时资产)
不可逆性:极高。 这是全项目最核心的决策。
双管线共享 Render Graph
ADR-004:渲染管线 —— 双管线共享 Render Graph
Status
Proposed
Context
渲染目标区间跨越 4 个数量级:每帧 5 万三角面(WebGL2 小游戏)到 3000 万+(主机)。
同时,硬件架构有根本差异:
- 移动 GPU 几乎全是 TBDR(Apple / Mali / Adreno / PowerVR):G-Buffer 的每个额外 RT 都意味着 tile memory 往返与带宽开销
- PC / 主机是 IMR:带宽充裕,deferred 更合适
如果用单一管线硬套全平台,结果必然是"两端都不够好"。
Options
| 方案 | 说明 | 问题 |
|---|---|---|
| A. 统一 Deferred | 一套 GBuffer 全平台 | 移动端带宽爆炸,TBDR 上严重掉帧 |
| B. 统一 Forward+ | 一套前向,高端靠叠加 | 主机上做不了 3000 万面的场景,光照复杂度受限 |
| C. 双管线 + 共享描述层 | 移动 Forward+,高端 Visibility Buffer;共享 Render Graph | 需要两套实现,测试矩阵翻倍 |
Decision
采用 C,并加三条约束来控制成本:
- 两条管线共享同一个 Render Graph 描述接口。pass 声明"读/写哪个虚拟资源 + ms 预算",具体实现由 pass 内部的 backend 分支决定,不在 Graph 层分支。
- 管线选择由 MIR 的 target 在编译期决定,不是运行期判断。同一份 MIR,
me build --target=wechat-android与--target=ps5产出的 pass 图不同。 - 能力分级以"后端能力"为准(compute / indirect / RT 有无),不以平台名为准。
具体分配
| Tier | 管线 | 关键 pass |
|---|---|---|
| Tier0(WebGL2) | Forward + 烘焙光照 | depth prepass、forward opaque、透明、bloom、无 TAA(或简易 FXAA) |
| Tier1(小游戏+Metal) | Clustered Forward+ | + compute light culling、+ GPU 粒子、+ TAA |
| Tier2(移动原生) | Clustered Forward+ | + 稀疏纹理、+ 更好阴影 |
| Tier3/4 | Visibility Buffer → Deferred Material | + 虚拟几何、+ HWRT GI、+ 体积雾 |
Consequences
变容易: - 每个平台都能用最适合自己的管线,不互相拖累 - Render Graph 的预算注解让"性能契约"可落地到 pass 级 - 新增平台只需实现一个 backend,不动上层
变困难: - 两套 pass 实现 → 视觉一致性难保证(同一场景在手机上与 PC 上"看起来不是同一个游戏") - 测试矩阵:管线 × 平台 × 画质档位,组合爆炸 - 需要"参考渲染器"(reference renderer,慢但正确)用于对比验证两条管线的视觉偏差
不可逆性:中。 管线选型可以换,但换的成本是重写渲染器。
附:为什么不在 Tier0 上追求 TAA
WebGL2 无 compute,TAA 需要的 velocity buffer + neighborhood clamp 在 fragment 里做代价高,且小游戏端 GPU 弱。Tier0 用 MSAA + 高质量边缘处理,Tier1 起才上 TAA。
但这个取舍有代价:Tier0 的画面会有闪烁。 缓解手段是美术规范(避免高频细节、避免细长几何),这也意味着Tier0 的内容不能直接复用到 Tier3,需要不同的美术策略。这是必须接受的现实。
附:Virtual Geometry 的分层实现
不追求"一次做到 Nanite"。分三步:
- P2 初:编译期生成 cluster LOD 链 + CPU 选择(Tier1/2 可用)
- P2 中:compute 软件光栅到 vis-buffer(Tier2/3)
- P2 末/P3:mesh shader 路径 + 完整 DAG(Tier3/4)
每步都可独立交付价值,不需要"全做完才能用"。
自研渲染与 MIR,集成物理与音频
ADR-005:自研 vs 集成 —— 明确的买/建边界
Status
Accepted
Context
引擎项目最容易死法之一是"什么都自己写"。判断标准只有一个:
这块东西是不是我们的差异化?如果不是,且市场有成熟方案,就集成。
Decision 矩阵
| 领域 | 决策 | 选型 | 理由 |
|---|---|---|---|
| 渲染器 | 自研 | — | 核心差异化(AI 生成 + 预算编译),必须自研 |
| MIR / 编译链 | 自研 | — | 核心差异化,无替代品 |
| AI Agent 层 | 自研 | — | 核心差异化 |
| ECS | 自研 | — | 与 MIR/预算深度耦合,且代码量可控 |
| 编辑器 UI 框架 | 集成 | Tauri + React | 非差异化,生态成熟 |
| 物理(3D) | 集成 | Jolt Physics(MIT) | 30 年成熟领域;Jolt 性能优秀、支持多平台、许可宽松 |
| 物理(2D) | 自研轻量 | — | 小游戏端要极致小,Jolt 太重 |
| 角色控制器 | 自研 | — | 手感是游戏性核心,通用方案都不够 |
| 音频(中高端) | 集成 | Wwise / FMOD | 主机认证必需,自研无法通过认证 |
| 音频(小游戏) | 自研轻量 | WebAudio 封装 | 包体约束,Wwise 塞不进 4MB |
| 导航网格 | 集成 | Recast & Detour | 成熟、可裁剪、MIT |
| 纹理压缩编码 | 集成 | ISPC Texture Compressor / Basis | 编码质量与速度是专门领域,自研不划算 |
| 超分 | 抽象 + 集成 | FSR / XeSS / MetalFX | 见下 |
| 光照烘焙 | 自研外层 + 集成求解器 | 自研管线,求解器可接第三方 | 烘焙管线必须与 MIR/预算打通 |
| 序列化 | 自研 | — | 与 MIR 同源,用集成库反而绕 |
| 动画压缩 | 自研 | — | 与 MIR/预算耦合,且算法不复杂 |
| 网络同步 | P3 再定 | — | 视 P3 项目需求,不预先设计 |
| 视频解码 | 集成平台原生 | — | 无差异化 |
关于超分的特别说明
三个选择:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 自研 TAAU | 完全可控、无授权问题、可深度定制 | 需要 2–3 年积累才能达到 FSR 水平 |
| 集成 FSR(AMD,开源 MIT) | 免费、跨平台、质量好 | 主机认证需确认;对特定硬件优化不足 |
| 集成 DLSS / XeSS | 质量最好(AI 超分) | 需要特定硬件;授权与主机认证复杂 |
决策:做抽象层(me-upscaler),默认集成 FSR,硬件专有方案作为可选插件。
理由:超分不是我们的差异化,但它对"性能+画质"的贡献极大(等效 30–50% 性能)。花 2 年自研是浪费。
风险:主机认证(TRC/TCR)对超分实现有要求,需在 P2 提前确认。
关于物理的特别说明
选 Jolt 而非 PhysX/Bullet/Havok:
| 引擎 | 评价 |
|---|---|
| Jolt | MIT、C++、性能优秀(比 Bullet 快 2–4 倍)、活跃维护、已被 Horizon Forbidden West 等商业项目使用 |
| PhysX | NVIDIA 主导,主机支持需确认,近年重心转向 GPU |
| Bullet | 老、性能一般、维护弱 |
| Havok | 商业授权贵,但主机最成熟 |
若 P3 需要主机级品质与官方支持,可换 Havok——因为物理被封装在 me-physics 接口后,替换成本可控。
Consequences
变容易: - 团队精力集中在渲染 + MIR + AI 三条差异化线上 - 减少 60% 以上的"重复造轮子"工作量 - 成熟组件自带稳定性与工具链
变困难: - 依赖管理与许可合规(MIT/Apache 与商业授权混杂) - 第三方库的 WASM 兼容性需要逐个验证(这是 P0 的真实风险:Jolt 能编到 wasm32,Wwise 不能) - 集成层需要写得足够薄,否则"集成"变成"深度改造",失去集成的意义
关键约束:所有集成组件必须藏在接口后,且接口由我们定义。 否则替换成本会失控。
补缺口而非替换、三分区、冻结字节、限于工具期烘焙
ADR-006: 生成式音频只作为"补缺口",且限于工具期烘焙
Status
Accepted(2026-10-22)
Context
MagicEngine 的音频子系统 SfxForge 已有 12 个程序化配方、31 个资产,能覆盖常规音效 (UI、脚步、门、雨、火、风、虫鸣、爆炸)。但它有三个结构性空洞,配方永远填不上:
- 真人声 / 方言 / 叫卖——需要真实录音或语音生成,程序化合成做不到
- 人群嘈杂——是几十个弱源的统计叠加,不是几个振荡器能凑出的"密度感"
- 独特奇观音——"这一声"必须专家级音效师或生成模型才做得出来
2026 年可用的生成式音频后端已经成熟到可用(Seed-Audio 1.0 出 120 秒 48kHz、 ElevenLabs SFX v2 支持服务端 loop、MOSS-SoundEffect v2.0 是 Apache-2.0)。 于是问题变成:接进来之后,它应该在架构里占什么位置?
有两条路:
- A(替换):生成模型取代配方,成为音效生产的主路径
- B(补缺):配方仍是主路径,生成只填三个空洞
Decision
选 B。生成式后端接入为工具期(bake-time)的补缺口通道,并受三条规则约束:
R1 物理隔离。 配方产物在 library/sfx/wav|ogg/(可重建区,逐字节可复现,进 CI);
生成产物在 library/sfx/generated/(冻结区,不可复现)。两者不混放。
R2 冻结字节。 生成资产 deterministic: false + sha256_wav,并提供 verify
命令校验字节未被替换。禁止在 CI 里重新生成来"验证"——同 prompt 两次结果不同,
这条路测不出任何东西。
R3 过同一道闸门。 生成产物必须通过七项硬校验(时长/峰值/直流/循环接缝/声道/
响度/体积),不通过进 rejected/。入库标准不因"这是花钱生成的"而降级。
同时确定:生成不在运行期发生。
Consequences
变得容易
- 三个能力空洞被补上,而且补得准(中文提示词直接写"叫卖",不用先翻译)。
- 生成物的可追溯性达到配方同级:
provider / license / commercial / prompt / sha256全部落库,法务问起来能逐条回答。 - 换后端是一行命令。三个可调用后端(火山 / ElevenLabs / fal)共用同一个
finalize(),协议差异被build_request()吸收。 - 手工路径同样可入库:可灵、即梦没有开放 API,
ingest让"网页下载 → 导入" 与 API 产物结构完全同构。
变得困难
- 包里放不下多少生成音效。一段 20 秒 48kHz 立体声未压缩 3.8MB,超过小游戏 4MB 主包的全部预算。所以生成音效必须"少而精",环境床还得靠运行时合成。 这直接限制了这套能力的价值上限——它补的是空缺,不是产能。
- 构建不再完全可复现。只要
generated/里有一条资产,rm -rf library && rebuild就不再能重建整个库。这是永久性的代价,只能靠"冻结区单独备份"来管理。 - 多了一层外部依赖。后端改契约、涨价、下架,都会影响流水线。 缓解方式是三个后端互备,但协议适配代码会长期存在。
- 短期的许可不确定性。火山 Seed-Audio 的官方单价与商用条款细则未完全公开, 目前只有第三方整理的 ¥0.05/秒。在拿到书面条款前,不应把它写进对外产品承诺。
被否掉的选项及理由
A(替换) 被否掉的核心不是质量,是包体。生成音频的体积下限由采样率决定, 配方音频的体积下限由参数决定(可以 0 字节,运行时算)。在小游戏这个首要目标平台上, 这条差距无法用质量弥补。
选 AudioGen / MMAudio 被否掉:权重 CC-BY-NC,商用直接出局。 选 ThinkSound 被否掉:仓库挂 Apache-2.0 但 README 禁止商用,标签与条款冲突, 是最危险的一类。 选 Stable Audio 被否掉:营收 <$1M 的条件许可 + Gemma 条款。自用无妨, 但引擎要交付给第三方客户时,你无法替客户保证营收。 把生成接到运行期 被否掉:见上,包体数学不成立。
备注
许可状态是易腐信息,本 ADR 的结论有效期按季度复核。复核清单: AudioGen 是否改许可、Stable Audio 是否去掉营收阈值、MOSS-SoundEffect 是否上 可用推理端点、火山是否公开书面条款。
判断许可的正确读法是读
LICENSE与NOTICE原文,不是读仓库 badge, 也不是读第三方评测页的摘要。