[Rust] Rust开发Wasm运行到支付宝小程序
Rust开发Wasm运行到支付宝小程序
最近参加了支付宝小程序开发者大赛, 把我之前的RustNES项目(任天堂红白机游戏机模拟器, 可以玩小霸王上的马里奥, 冒险岛等)迁移到了支付宝小程序上,
发现相关内容网上的资料比较匮乏, 在此分享一下开发经历.
本项目两大核心点
- Rust编译成Wasm运行在小程序环境中
- 支付宝小程序支持Canvas+Wasm
Rust编译
Rust编译小程序Wasm与编译到Web环境大体相同, 但小程序的环境受限, dom api等不能直接通过js binding拿到, 尽量不要在小程序中用rust控制js,
我这里使用了feature来区分了web和小程序.
前置条件: Rust环境
- 添加wasm工具链
rustup target add wasm32-unknown-unknown
- Rust项目适配wasm, 这里简单列出我用到的依赖
// web only
[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2.8", features = ["js"] } # 注入缺失js api
js-sys = { version = "0.3.51" } # js 类型
wasm-bindgen = { version = "0.2.74" } # rust 导出 wasm
wasm-logger = { version = "0.2.0" } # console log 适配
wasm-rgame = { version = "0.0.1" } # 可选, 我用来做keycode定义
# w3c 标准对象定义
[dependencies.web-sys]
version = "0.3.4"
features = [
'Document',
'Element',
'HtmlCanvasElement',
'HtmlParagraphElement',
'KeyboardEvent',
'WebGl2RenderingContext',
'WebGlBuffer',
'WebGlVertexArrayObject',
'WebGlProgram',
'WebGlShader',
'WebGlUniformLocation',
'WebGlTexture',
'Window',
'FocusEvent',
'File',
"FileReader",
# ... 你需要的接口
]
optional = true
[features]
wasm = ["web-sys"]
具体如何适配可以直接搜文章, 也可以到我项目中看代码
- 编译 Wasm target
这里没有什么gap, 都是用 wasm-pack 命令(或者配置webpack config)
wasm-pack build --target web --release -d ../$Playground/pkg_miniapp --features wasm,wasm-miniapp --no-default-features
编译好后 pkg_miniapp 就是我们要的 wasm 包.
根据参考文章1, 我们还不能直接在小程序中用, 需要做一些适配.
导入TextEncoder,TextDecoder (文章1中方案是直接拼接文本内容, 我这里搞了个文件)
删除不支持api (new URL/fetch, 这里是用来拉取远程wasm文件的, 小程序不支持)
之后wasm包就可以给小程序用了, 为了方便我们之间把文件夹拷过去.
支付宝小程序适配
首先是wasm, 然后是canvas.
小程序初始化wasm
尽管支付宝官方文档没有写wasm的相关api,但经过实测模拟器,android环境是可以运行的(iOS不行).
import __wbg_init, * as wasm from "../../pkg/rust_nes";
// 1. 初始化wasm
__wbg_init(wasm_file_array_buf).then(() => {
// 2. 调用wasm的主函数(应用自己定义), 或在运行时调用wasm导出函数
wasm.main();
})
但由于网络请求的限制, 我们需要先把wasm文件读到arrayBuffer中, 那么现在的问题就在于如何读取这个文件
三个方案
- 本地文件打包: 这个是最合理的, 但没有找到小程序自定义打包配置, 没有走通
- 通过my.request下载: 这个上线时需要手动上传到oss上, 预览环境可用
- 下载文件: 每次启动游戏都要经过网络把源文件下载下来太浪费流量, 我们可以通过下载文件再读文件的方式获取, 但经过实测读取下载后的文件与源文件内容不一致, 导致加载失败
canvas环境
canvas获取context:
一定要在canvas
元素上添加type="webgl"
(或canvas2d), 否则拿不到节点
获取代码
my.createSelectorQuery().select('#canvas').node().exec((res) => {
const canvas = res[0].node;
const ctx = canvas.getContext('webgl');
// do something
})
实机运行效果
项目代码,playground代码 欢迎来看看
参考文章: