🦀

Rust & Wasm でミニゲームを作る時のサンプル(Rust2021)

2025/02/16に公開

はじめに

WebAssemblyを使いミニゲームを作成する際、WASMの作法に、Rustの借用や所有権、クロージャ、ジェネリクスなどのモダン仕様が重なると、自作アプリのビルドを通すのに苦労します。そこで、'web_bindgen'や、'web-sys'処理をPoC(試行錯誤※)するための、シンプル(1ファイルでなるべくコーディング量が少ない)なミニゲーム用の"Rust & Waws"サンプルを作成しました。また、ブラウザーからのコールバックを受け取る方法は、たくさんの情報が公開されていますが、それでも、自由に使えるようになるまで理解するには、時間がかかりました。今回のサンプルと合わせて、私が理解したイメージを掲載します。

※実際に思考錯誤した内容

  • MouseやTouchイベントから座標を、画面サイズが異なる場合に取得する方法
  • 画像やCSSの操作をjavascript/rustのどっちで実装する!?かの確認
  • ブラウザーからのコールバック処理の受け取り方

対象のバージョン

  • Rust 2021 Edition
  • rustc 1.83.0
  • cargo 1.80.1
  • rustup 1.27.1
  • pnpm 9.12.3

環境構築例:Rust & WebAssembly 構築メモ

Rust&Wasm サンプル

Source
Play in browser

サンプルを通して理解した内容

  1. Gameオブジェクトを "Mutable Static Variables"として用意する必要があります。理由は、Closure::wrap()に渡たすオブジェクトは全て'static'でないといけないためです。また、渡すオブジェクトを変更可能にしたいので、'Mutable Static'にする必要があります。Closureは、Strcutとほぼ同等とあり、Structが内部的に作られるイメージをもつと扱いやすくなります。👍

    A closure expression produces a closure value with a unique, anonymous type that cannot be written out. A closure type is approximately equivalent to a struct which contains the captured variables.
    参照:The Rust Referencehttps

  2. 再描画時に呼び出す関数(Closure)を用意します。

    • unsafe{}は、static mute のオブジェクトの値を変えるため必要になります。
    • 引数として呼び出しを開始した時点の時刻が返されますが、サンプルでは参照していません。
    • RC<T>を使って実装する理由は、callbackの処理中に、再帰用のrequestAnimatinoFrameを呼び出す必要があるためです。👍

      Rc<T> enables multiple owners of the same data; Box<T> and RefCell<T> have single owners.
      参照:RefCell<T> and the Interior Mutability Pattern

  3. 再描画時に呼び出される関数(Closure)を指定してrequetsAnimationFrameを呼び出します。

  4. ブラウザーが、再描画のタイミングで 3.で指定した関数(Closure)を呼び出します。

  5. 自前の内部カウントをインクリメントし、各内部変数を更新します。

  6. 各内部変数を参照し、再描画します。

  7. "requestAnimationFrame()"を呼び出し、次の再描画に備えます

  8. クリック時にに呼び出される関数(Closure)を用意します。

  9. "canvas"に、click時に呼び出されるイベント・リスナーをセットします。

  10. forget()を呼び出し、関数のスコープを抜けた後でも、イベントを受け取れるように、Function(closure)のポインタ用情報を維持します。👍

  11. ブラウザーが、クリックを検知し、 8.で指定した関数(Closure)を呼び出します。

  12. 受け取ったクリック座標を、内部変数に反映します。

Discussion