@graviton-game/bevy-minigame-kit
v0.1.0
Published
Bevy Adapter for Wechat Mini Game: build pipeline, wx-env mock and simulator verification for Rust->wasm games
Readme
bevy-minigame-kit
Bevy Adapter for Wechat Minigame(微信小游戏)——把 Bevy(Rust → wasm-bindgen → wgpu WebGL2)跑进微信小游戏运行时的适配层。当前处于可行性验证 spike 阶段。
- 可行性研究报告与参考实现(bevy_gamekit)深挖笔记:docs/reference-bevy-gamekit.md
- 总体结论:条件性可行。Android(微信 8.0.24+/基础库 2.24+)走 WebGL2;
iOS 需高性能/高性能+模式(
game.jsoniOSHighPerformance: true+ 平台开通) - 完整游戏示例:bevy-minigame-example——三消 (bevy-match-3 上游移植,含 bevy_state/picking/ui 新验证面与 wx 音频桥), 已在 Node mock / 浏览器基线 / 开发者工具模拟器三环境验证通过
里程碑状态
| 阶段 | 内容 | 状态 | |---|---|---| | S1 | 纯 wasm 链路(glue patch + WXWebAssembly + 环境垫片) | ✅ Node mock 双场景通过 | | S2 | Bevy 0.20 第一帧(完整垫片层 + wgpu WebGL2) | ✅ Node mock 通过 + 浏览器基线 + 开发者工具模拟器验证通过 | | S3 | 触摸输入 + 体积数据 | ✅ 浏览器基线鼠标跟随像素级验证 + mock 触摸探针 + 模拟器拖拽跟随验证 | | S4 | 微信开发者工具 / 真机验证 | ✅ 模拟器全项通过(启动/渲染/拖拽/切后台/内存曲线,见 docs/devtools-verification.md);真机扫码待人工 | | S5 | bevy_asset 资产管线(真实纹理 + 字体) | ✅ 构建/体积/mock 回归 + 浏览器基线渲染验证通过;模拟器验证因工具链环境回归挂起(§9.5,与本项目无关,含复验指引) |
实测数据(S5)
| 项 | 数值 | |---|---| | wasm 原始(release, lto=thin, strip) | 30.07 MB | | wasm-opt -O4 后 | 30.07 MB(28.68 MiB) | | brotli -q11(.wasm.br,分包实际交付物) | 4.52 MB | | glue js(no-modules, patched) | 122 KB | | adapter.js | 49 KB | | assets(128x128 PNG + OFL ttf 字体) | 118.8 KB | | 主包(game.js + adapter.js + glue + 配置) | 174 KB(上限 4 MB)✓ | | bevy 分包(.wasm.br + assets) | 4.98 MB(主+分包合计 5.15 MB,上限 30 MB)✓ |
体积说明:S5 加了真实纹理 + 字体加载,wasm 只涨 6.3 KB(br 后)——bevy 的
2d特性链本就内含 bevy_asset/bevy_text/png(见 devtools-verification.md §9.1),增量只有实际用到的加载/渲染路径。
构建与测试
mise install # 装 mise.toml 钉住的 node / wasm-bindgen-cli / binaryen(wasm-opt)
mise run build # 构建并组装 minigame/ 与 web/
mise run test # 微信环境 Node mock(boot 链 + 触摸探针)
mise run serve # 浏览器基线(本机即可验证真实渲染)
# 不经 mise 的等价命令(工具自备,版本要求同下):
# node tools/build.mjs / node tools/test-node-mock.mjs / node tools/serve.mjs依赖口径与工具链分工(mise):mise.toml 钉 node ≥18、
wasm-bindgen-cli 0.2.129、wasm-opt(binaryen 130);rust 仍交给 rustup——
rust-toolchain.toml 钉 stable + wasm32-unknown-unknown target(target/component
管理是 rustup 的活,mise 官方亦建议;也避免与本机 scoop rustup 重复装整套工具链)。
python3 选配,仅重新生成演示纹理用。
tools/build.mjs(纯 Node 内置模块,零 npm 依赖):cargo 构建(RUSTFLAGS=-C target-feature=-reference-types)→
wasm-bindgen --target no-modules → wasm-opt → patch glue(path-only 守卫 +
module.exports 导出,失配显式报错)→ brotli → 组装主包 + bevy/ 分包。
管线本体在 tools/lib/minigame-build.mjs,本文件只是 demo 的构建配置。
作为 npm 包使用(下游游戏)
kit 按可直接发布的 npm 包组织;下游游戏把工具链当依赖消费,不复制任何模板代码:
npm install @graviton-game/bevy-minigame-kit # npm 包;开发期可用 file:../bevy-minigame-kit 链接本地
# .npmrc 写 install-links=false → 符号链接安装,kit 改动即时生效// tools/build.mjs —— 下游的全部构建配置(完整示例见 bevy-minigame-example)
import { buildMinigame } from "@graviton-game/bevy-minigame-kit/build";
buildMinigame({
root: new URL("..", import.meta.url).pathname,
crateName: "my_game",
projectName: "my-game",
extraRootScripts: ["adapter/audio-bridge.js"], // 额外主包 JS(可选)
extraBinAliasExts: [".wav"], // 追加 FSM 敏感扩展(可选)
});| 子路径 | 用途 |
|---|---|
| @graviton-game/bevy-minigame-kit/build | buildMinigame(opts):cargo→bindgen→glue patch→wasm-opt→brotli→组装主包+数据分包+web 基线,含体积限额守卫 |
| @graviton-game/bevy-minigame-kit/wx-mock | createWxMock 等:vm 沙箱微信环境(path-only WXWebAssembly、无 TextEncoder、wx API + InnerAudioContext 记录),CI 里验启动链 |
| @graviton-game/bevy-minigame-kit/automator | findTarget/attachCdp/focusIde/canvasTapExpression + restart.mjs:开发者工具 CDP 验证驱动(上下文自动发现、失焦聚焦) |
适配器(adapter/minigame.js)由 buildMinigame 从包内自动取用,下游无需复制。
demo 内容
红/绿/蓝三个方块绕中心公转+自转,背景色缓慢渐变,黄色方块跟随最后一个指针
(触摸与鼠标双通道)。S5 起加入资产管线:AssetServer 经垫片 fetch 加载
真实 PNG 纹理(画面左上四象限纹理 sprite)+ OFL 像素字体(底部一行
ASSET OK 0123,bevy_text/parley 渲染),加载/失败均有 console 日志。
资产源在 assets/(build.mjs 拷入分包与 web 基线),纹理可用
python tools/make-texture.py 重新生成(纯标准库,无第三方依赖)。
微信开发者工具验证步骤(S2/S3,需 Mac/Windows)
- 安装微信开发者工具(Linux 可用社区移植版)
- 导入项目 → 目录选本仓库
minigame/→ AppID 选「测试号」 (已配touristappid+compileType: game+bevy分包) - 编译,Console 预期输出:
[bevy-minigame-kit] 已安装微信 WebGL shader-link workaround(真机桥缺陷补丁)
[bevy-minigame-kit] canvas 375x667 @2 (backing 750x1334)
[boot] bevy-minigame-kit adapter s2 pathOnly: true
[S2] wasm 初始化完成,启动 Bevy…
...Creating new window bevy-minigame-kit...
[S2] Bevy 已启动(第一帧应在 canvas 上渲染)
INFO bevy heartbeat: t = 2.0s(每 2 秒一条,证明调度器活着)- 画面:三色方块公转 + 背景渐变;模拟器里按住拖动,黄色方块应跟随手指
- 性能面板记录:运行内存(对比浏览器基线)——iOS 高性能模式上限 1~1.5GB, 官方建议关注 wasm 编译峰值(团结引擎文档:≈ wasm 文件大小 × 10)
真机验证 checklist(S4)
- [ ] Android(微信 8.0.24+):扫码预览,确认第一帧 + 拖拽跟随 + 内存
- [ ] iOS:公众平台开通「高性能模式」(能力地图→研发能力→生产提效包),
game.json加"iOSHighPerformance": true,真机验证 WebGL2(iOS ≥ 15.5 建议) - [x] 长时间运行观察内存曲线(模拟器已验:10 分钟采样 JS 堆稳定 41.7–43.7 MB 无线性增长,单 setInterval 调度器泄漏 workaround 生效;真机待复核)
- [x]
wx.onShow/onHide切后台/回前台:模拟器已验(最小化/恢复等价路径: 心跳归零→恢复、无崩溃);真机待复核 - [ ] 弱网下分包加载失败的表现(game.js 已打错误日志)
- [x] shader 编译/链接异常防护:模拟器已验 console 输出「已安装微信 WebGL shader-link workaround」(基础库 3.17.4)
模拟器验证详情与数据:docs/devtools-verification.md
已验证的技术要点(S1-S3 汇总)
- wasm 加载:无标准
WebAssembly全局 → 别名WXWebAssembly(path-only); glue 两处 patch 后按包内路径加载.wasm.br(WXWebAssembly 原生解 brotli) - instanceof 陷阱:
Symbol.hasInstance注册Window/HTMLCanvasElement/WebGL(2)RenderingContext等类——web-sys/wgpu 的dyn_into全靠它(mock 验证 wgpu 确实走到了 surface 创建) - 环境缺口:TextEncoder/TextDecoder 兜底、
WXWebAssembly.Instance空壳 (glue 的 instanceof 会因 undefined 抛 TypeError)、crypto.getRandomValues 兜底 - winit DOM 表面:document/canvas/style/getComputedStyle("Npx")/matchMedia/ ResizeObserver/IntersectionObserver/MessageChannel 全套垫片,浏览器基线证明 同一 wasm 在真浏览器也照常工作(patch 不破坏标准路径)
- 微信 GL 桥缺陷(自 bevy_gamekit 先例移植):shader-link 时序 workaround、 LINK_STATUS 布尔归一化、ASTC getSupportedProfiles 补齐
- rAF 泄漏:微信宿主对每次 rAF 注册保留簿记 → 常驻单 setInterval + JS 队列
- 渲染验证:浏览器基线(真 WebGL2)第一帧渲染 + 动画 + 指针跟随全部通过 (截图存证),同一 wasm 走微信 mock 链路推进到 wgpu surface 创建
- 资产管线(S5):bevy_asset wasm 端发
fetch("assets/…")相对路径 + 精确 200/404 语义 + 缺省 meta 无害(源码级确认);垫片 fetch → FileSystemManager 加主包/分包候选回退;宿主 document 壳currentScript=undefined会让 wasm-bindgen glue 顶部崩溃(build.mjs patch 修复,详见 devtools-verification.md §9.3);浏览器基线纹理 + 字体渲染像素级验证通过(docs/screenshots/web-asset-pipeline.png)
工程结构
Cargo.toml # Bevy 0.20 精简特性集(2d/touch/webgl2/bevy_log/std);
# bevy_asset/bevy_text/png 已内含于 2d 特性链,零额外特性
mise.toml # mise 工具链钉版(node/wasm-bindgen-cli/binaryen)+ build/test/serve 任务
rust-toolchain.toml # rustup 钉 stable + wasm32 target(rust 交给 rustup,mise 不重复管)
src/lib.rs # demo:公转方块 + 指针跟随 + 心跳日志 + 纹理/字体资产加载;wasm 入口 start()
adapter/minigame.js # ★ 适配层(~1100 行):环境垫片 + GL workaround + rAF 调度器
# + fetch→FileSystemManager 垫片(主包/分包候选回退)
assets/ # 资产源(textures/ + fonts/),build.mjs 拷入分包与 web 基线
tools/build.mjs # 一键构建 + glue patch + wasm-opt + brotli + 资产拷贝 + 组装(纯 node 内置模块)
tools/make-texture.py # 程序生成演示纹理(纯标准库 PNG 编码)
tools/test-node-mock.mjs # 微信环境 Node mock(boot 链 + getContext 探针 + 触摸探针)
tools/serve.mjs # 浏览器基线静态服务器(纯 node 内置模块,替代 python http.server)
tools/automator/ # 开发者工具 CDP 验证工具(listen/eval/soak/截图/像素分析/
# 分包上下文探针 contexts.js/crossctx.js 等)
minigame/ # 可导入微信开发者工具的工程(主包 + bevy/ 分包,build 产物 gitignore)
web/ # 浏览器基线(同产物 + assets,验证 Rust 侧与 WebGL2 渲染)
docs/ # 参考实现研究笔记 + 验证报告后续路线(spike 之后)
- 模拟器资产管线复验:待工具链环境恢复(重启机器)后按 devtools-verification.md §9.6 五分钟走完
- 音频:
wx.createInnerAudioContext/wx.createWebAudioContext桥 + Bevy 自定义 audio plugin(bevy_audio 默认后端在小游戏不可用) - 体积压缩:feature 裁剪 → opt-level=z → 必要时 wasm-split 调用图分割
- Rust 侧插件化:把 wx API(storage/lifecycle/systemInfo/setPreferredFPS)封装成
MinigamePlugin(参照 bevy_gamekit 的 bridge 模式) - iOS 高性能模式实机验证与内存预算
