[Rust] Rust开发Wasm运行到支付宝小程序

Rust开发Wasm运行到支付宝小程序

最近参加了支付宝小程序开发者大赛, 把我之前的RustNES项目(任天堂红白机游戏机模拟器, 可以玩小霸王上的马里奥, 冒险岛等)迁移到了支付宝小程序上,
发现相关内容网上的资料比较匮乏, 在此分享一下开发经历.

本项目两大核心点

  1. Rust编译成Wasm运行在小程序环境中
  2. 支付宝小程序支持Canvas+Wasm

Rust编译

Rust编译小程序Wasm与编译到Web环境大体相同, 但小程序的环境受限, dom api等不能直接通过js binding拿到, 尽量不要在小程序中用rust控制js,
我这里使用了feature来区分了web和小程序.

前置条件: Rust环境

  1. 添加wasm工具链 rustup target add wasm32-unknown-unknown
  2. 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"]

具体如何适配可以直接搜文章, 也可以到我项目中看代码

  1. 编译 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中, 那么现在的问题就在于如何读取这个文件

三个方案

  1. 本地文件打包: 这个是最合理的, 但没有找到小程序自定义打包配置, 没有走通
  2. 通过my.request下载: 这个上线时需要手动上传到oss上, 预览环境可用
  3. 下载文件: 每次启动游戏都要经过网络把源文件下载下来太浪费流量, 我们可以通过下载文件再读文件的方式获取, 但经过实测读取下载后的文件与源文件内容不一致, 导致加载失败

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代码 欢迎来看看

参考文章:

  1. wasm-pack构建的wasm包如何用于微信小程序
posted @ 2024-01-15 10:53  新新人類  阅读(238)  评论(0编辑  收藏  举报