使用 Rust 与 WebAssembly 进行图形开发
项目初始化
要求需要先安装好 Node.js,和 Rust 环境。
- 建一个新的文件夹,将工作目录切换到该目录下
1 | npm init rust-webpack |
如果能看到下面的图案就是成功了:
1 | 🦀 Rust + 🕸 WebAssembly + Webpack = ❤ |
- 安装依赖
1 | npm install |
- 安装 wasm-pack
Linux 和 Mac OSX 的操作系统可以使用 cURL 进行安装:
1 | curl https://rustwasm.github.io/wasm-pack/installer/init . sh -sSf | sh |
Windows 可以下载单独的 exe,进行安装:下载地址
- 运行服务器
1 | npm run start |
会自动安装 Rust 所需的依赖包,如果成功的话,开发者工具中终端界面可以看到 Hello, World
- 更新 Rust 版本
目前模板的 Rust 版本为 2018
(2022年7月5日时),在 cargo.toml
将版本改成 2021
:
1 | edition = "2021" |
- 更新依赖的版本
cargo.toml
中的依赖也不是最新的,可以更新到新的版本。
Visusal Studio Code 中有 Crates 插件,可以获取依赖的版本信息。
1 | [dev-dependencies] |
- 更新
console_error_panic_hook
这是个非常有用的库,一看名字就知道是用来 Debug 的,目前最新是 0.1.7
。
1 | [target."cfg(debug_assertions)".dependencies] |
绘制图形到 Canvas
可以使用 <canvas>
将图形绘制到浏览器窗口中,在 static\index.html
中 <body>
后添加 <canvas>
:
1 | <body> |
终于到了写 Rust 代码的时候了!
在 lib.rs
引入依赖
1 | use wasm_bindgen::JsCast; |
原有代码里的 #[cfg(debug_assertions)]
可以删除。
1 | use wasm_bindgen::prelude::*; |
需要注意的是,web_sys
使用了 features
来减小大小。所以使用的时候,必须查看文档,看属于哪个 features
。
在 console
后添加所需要的 features
:
1 | [dependencies.web-sys] |
Rust 作为静态语言,确实要比 JavaScript 更加繁杂。如果这个图形纯粹使用 JavaScript 的话,看起来就就简单多了:
-
JavaScript 中,
window
有可能是null
或者undefined
,对于 Rust 来讲,就是Option<Window>
,可以使用unwrap
来获取window
。 -
使用
get_element_by_id
获取canvas
后,得到的是Option<Element>
,但Element
并没有函数关联到canvas
。JavaScript 中,可以使用get_context
去尝试获取,如果没有的话,会抛出异常。对于 Rust,则需要使用dyn_into
强制转换到HtmlCanvasElement
。 -
get_context("2d")
之后使用了两个unwrap
,这是因为它返回的是Result<Option<Object>, JsValue>
。
绘制谢尔宾斯基三角形
谢尔宾斯基(Sierpiński)三角形是分形中的经典图形之一,用递归来实现其实非常简单的。
先把绘制三角形的代码抽象为一个函数,便于多次调用:
1 | fn draw_triangle(context: &web_sys::CanvasRenderingContext2d, points: [(f64, f64); 3]) { |
那么就只需要调用函数进行三角形的绘制,
1 | draw_triangle(&context, [(300.0, 0.0), (0.0, 600.0), (600.0, 600.0)]); |
由于函数没有加上 context.fill()
,所以图案并没有填充成为黑色。
谢尔宾斯基三角形非常简单,只要 取边长中点 (两个坐标之和除以 2)继续进行相同的绘制过程。
1 | draw_triangle(&context, [(300.0, 0.0), (0.0, 600.0), (600.0, 600.0)]); |
我们将上述过程作为谢尔宾斯基三角形绘制的基础,使用递归绘制图案:
1 | fn midpoint(point_1: (f64, f64), point_2: (f64, f64)) -> (f64, f64) { |
调用这个函数,并且设置 depth
为 4:
1 | sierpinski(&context, [(300.0, 0.0), (0.0, 600.0), (600.0, 600.0)], 4); |
如果想要跟最开始图案一样填充黑色,只需要在最后一层进行填充颜色。可以考虑加入一个 bool
值作为是否填充的判断:
1 | fn draw_triangle(context: &web_sys::CanvasRenderingContext2d, points: [(f64, f64); 3], fill: bool) { |
之后判断是否是最后一层,
1 | let depth = depth - 1; |
小结
通过本文的铺垫,我们就可以考虑使用 Rust 开发 Web 游戏了(思考)。