Godot + Rust + wasmによる3Dブラウザゲームの作り方またはRustはゲーム制作向き言語なのかの考察的な何か

:::note warn

最近修正したのでQiitaの警告が消えてしまいましたが古い記事です！Godotの一部リンクが機能しなくなっている点についてご容赦ください。
:::

この記事は [Rust Advent Calendar 2021 - Qiita](https://qiita.com/advent-calendar/2021/rust) 20日目の記事です！

ゲームエンジン Godot (ゴドー https://godotengine.org/ ) とRustを用いて3D[^1]シューティングゲームを作成し、さらにソースコード等をwasm化することで[WebGLゲームとして公開](https://alien-war.namnium.work/)することができたので、その手法の解説記事となります！(環境構築がメインです)

ここから遊べます!!!!! ↓ スマホも対応! (注意:音が出ます)

https://alien-war.namnium.work/

- 操作は十字キーとスペースを使います
- 一度画面をクリックするとキーボードで操作できます

完成したシューティングゲームのデモ(Windows版)↓

![AlienWarCaption.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205472/52ef837e-37de-ea92-fd98-2e4075550e1b.gif)

- GitHubリポジトリ: https://github.com/anotherhollow1125/godot_wasm_rust_shooting
- Rustで書いた部分: https://github.com/anotherhollow1125/godot_wasm_rust_shooting/blob/main/shooting_rst/src/lib.rs

### アピールポイント

- スマホのブラウザでも遊べる3Dゲーム! (UnityのWebGLも一応対応してるらしい...?)
- ゲームロジックの殆どが**Rustで書かれている**! (with gdnative クレート)

[^1]: 「3D」をタイトルに付けましたが「ほぼ2Dじゃない？」というようなゲームなのである意味タイトル詐欺のようにも見えますが、3Dモードで作ったので誰がなんと言おうと3Dゲームです...

# はじめに

筆者の背景を一応紹介します。筆者はプログラミングとゲーム制作が趣味の大学生ですが、Unity C#のNullReferenceExceptionやその他の言語のガベコレに辟易し、Rustをメイン言語としてゲームを作れないかとここ数年模索してきました。

[昨年のアドベントカレンダー記事](https://qiita.com/namn1125/items/1a5242f6e8bced9c0642)では、Rustで3DCGを行う手法を模索しました。このほか、nannouを試したりbevyについて調べたりなどもしてきました。しかしいくらRustが好きだからと言って制作のすべてをコードベースで行うのは大変です。要はRustでもUnityみたいな開発環境がほしいわけです。

そんな折、Rustで作った動的ライブラリを利用できるGodotというゲームエンジンがあることを知りました。

[Godot EngineからRustを呼ぶ](https://zenn.dev/kawaxumax/articles/e0dedf3f6d4219)

このGodot、ゲームをWebGLエクスポートすることも可能とのこと！制作物はすぐに遊んでもらいたく、Webで簡単に公開できることにもこだわっていた自分としては大変好都合です。しかしRustが使えてかつWebGL化もすぐできるなんて美味しい話があるのかと思ったらそうでもなく、その場合ちょっと工夫が必要になります。

[Support wasm32-unknown-emscripten target · Issue #647 · godot-rust/godot-rust](https://github.com/godot-rust/godot-rust/issues/647)

今回の記事では、このIssuesで紹介されている[orion78frさんの手法](https://github.com/orion78fr/godot_keepass_rust_totp/actions/runs/900998393/workflow#L255)を使った、Rustのソースコードをwasm化する開発環境の作成方法を前半で解説し、後半は今回作ったシューティングゲームの工夫した点などを語ります。

# Godot & Rust & WebAssembly 開発環境構築編

## Godot

いきなり[Godot](https://godotengine.org/)の存在だけ紹介されても困惑すると思うので、[Godot](https://godotengine.org/)について説明します。本気で入門したい方はまずはRustのことは一旦忘れ、普通に公式のチュートリアルをやりましょう[^2]！(Godotの環境構築に関しては割愛します、といっても本体のインストールはダウンロードしてzipを展開するだけなのでとても簡単です[^3]。)

[^2]: 日本語のドキュメントも存在したのですが、記事執筆中に404になってしまったので代替として英語のドキュメントのURLを載せています。
[^3]: アクティベーションが必要じゃないかって？そんなゲームエンジンがあるんですか？

- [Your first game — Godot Engine (stable) documentation in English](https://docs.godotengine.org/en/stable/getting_started/step_by_step/your_first_game.html)
- [Scenes and nodes — Godot Engine (stable) documentation in English](https://docs.godotengine.org/en/stable/getting_started/step_by_step/scenes_and_nodes.html)

クリープを避けろというゲームなのですが、結構短時間で作れます。Godotの特徴を一通りつかめると思います。以下のような感じでしょうか

- ゲームオブジェクトなどはすべてNodeという単位で管理され、Nodeはツリーを形成できる
- ツリーのルートであるルートノードが1つ存在するシーンという単位でファイルとして保存される。シーンもまたノードとしたり、インスタンス化できる
- 基本的には、GDScriptというPythonライクな言語でプログラミングを行う。イベントハンドラのようなものとしてシグナルという仕組みがある

オブジェクト指向で、コンポネント指向であるようです。Unityと似た部分も多く楽に開発ができそうです！以降、Rust向け環境構築の話題になります。GodotのためのプログラムをRustで書いて、Rustによるゲーム制作を実現しましょう！

## 環境構築に関してのTL;DR

環境構築というのは大変です。「そんな面倒で長ったらしい環境構築しなきゃ駄目なの？？ていうかここうまくいかないんですけど！本当に環境構築できるの？」ってなるのが普通です(コメント大歓迎です！)。そこで少しでも手助けするべく、今回の記事のオマケとして[Dockerfile](https://github.com/anotherhollow1125/godot_rust_wasm_dockerfile)を用意しました！使い方はGitHubのREADME.mdの方を参照してください。

- `Dockerfile`: wasmへのコンパイル用に用意したDockerfileになります。
- `all_in_one_win/Dockerfile`: wasmへのコンパイルに加え、Windows向けに`.dll`へもコンパイルできる環境です。VSCodeのRemote Containersとの併用を想定しています。

最初はwasmコンパイル用の環境のみ用意しようと思ったのですが、一応`.dll`有りの環境も後付で用意しました。そのためこのような配置になっています。

https://github.com/anotherhollow1125/godot_rust_wasm_dockerfile

(環境構築は遠慮しておくよという方は[シューティングゲーム制作編](#シューティングゲーム制作編)まで飛びましょう)

## Godot & Rustの開発環境整備

おそらくチュートリアルを終えた後と思われますので、次はRustの[gdnative](https://github.com/godot-rust/godot-rust)クレートを用いて、RustでGodotゲームをプログラミングできるように環境を整えましょう！

先程挙げた[Godot EngineからRustを呼ぶ](https://zenn.dev/kawaxumax/articles/e0dedf3f6d4219)の記事様がとてもわかりやすかったため、これに従って環境構築します。すべてlinux向けの場合はclangを入れるぐらいで難しいことは何もないのですが、Windows & WSL2環境の場合少しだけ設定が必要です[^4]。

[^4]: 一応PowerShellなどWindowsのみの環境での構築も図ってみたのですが、clangあたりで面倒になって結局wslで行うことにしました。

### 依存関係導入

Rustは導入済みとします。(一応リンク: https://www.rust-lang.org/tools/install )

Rustの環境:
- cargo 1.57.0 (b2e52d7ca 2021-10-21)

まずは依存関係をインストールしていきます。本記事ではコピペ簡易化のためプロンプトの `$` は省略します。

```bash:bash
sudo apt update
sudo apt install -y build-essential clang mingw-w64
```

`clang` と、linux環境でWindows向けにコンパイルするための `mingw-w64` をインストールします。 `build-essential` はRustをインストールする過程で入れているとは思われますが、一応記載しました。

そしてWindows向けのコンパイルターゲットをrustupで入れておきます。

```bash:bash
rustup target add x86_64-pc-windows-gnu
```

### 設定とコンパイル

ここからは早速プロジェクトフォルダを作成していきましょう！

```bash:bash
cargo new --lib hello_godot
cd hello_godot
```

先に面倒事を済ませましょう。 `cargo` に関する設定を行うため `.cargo/config.toml` ファイルを追加します。

```bash:bash
mkdir .cargo
touch .cargo/config.toml
```

`config.toml` の中身を以下のようにします。Windows向けにコンパイルするための設定です。

```toml:.cargo/config.toml
[build]
target = "x86_64-pc-windows-gnu"

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
```

あとはいつもどおり `Cargo.toml` と `src/lib.rs` を書いていくだけとなります！

内容はgdnativeクレートの公式( [godot-rust/godot-rust: Rust bindings for GDNative](https://github.com/godot-rust/godot-rust) )から拝借しました。

```toml:Cargo.toml
[package]
name = "hello_godot"
version = "0.1.0"
edition = "2021"

[dependencies]
gdnative = "0.9.3"

[lib]
crate-type = ["cdylib"]
```

```rust:src/lib.rs
use gdnative::prelude::*;

#[derive(NativeClass)]
#[inherit(Node)]
pub struct HelloWorld;

#[methods]
impl HelloWorld {
    fn new(_owner: &Node) -> Self {
        HelloWorld
    }

    #[export]
    fn _ready(&self, _owner: &Node) {
        godot_print!("Hello, world.");
    }
}

fn init(handle: InitHandle) {
    handle.add_class::<HelloWorld>();
}

godot_init!(init);
```

入力が終わったら`cargo build --release`等でコンパイルしましょう。 `target/x86_64-pc-windows-gnu/release/hello_godot.dll` が目的のライブラリになります。

### 動作確認

Godotに `hello_godot.dll` を読み込ませて、正しく動作することを確認します。

Godotのプロジェクトフォルダに `hello_godot.dll` をコピーし、以下の手順でシーンを用意していきます。(チュートリアルは行ったものと仮定して大雑把に書きます)

1. Godotのプロジェクトを用意し、`Node` 型のルートノードを含むシーンを用意します
2. 動的ライブラリを導入するためにファイルシステムタブで右クリック→ `新規リソース` → `GDNativeLibrary`から `.gdnlib` ファイルを用意( `.tres` から変更。 `.tres` との違いはあまり無いみたい...?)。名前は好きなものでよいのでここではデフォルトの `new_gdnativelibrary.gdnlib` とします。
3. 作成した動的ライブラリを対応するプラットフォームに指定します。指定後**必ずインスペクタにある保存ボタンでnew_gdnativelibrary.gdnlibファイルを保存**してください。 `.dll` なので `Windows 64` に入れます。
![save2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205472/e91b0903-3206-a098-9578-8af16e77e836.png)
4. ルートノードにスクリプトをアタッチします。言語を `NativeScript` 、クラス名を `HelloWorld` にしておきます。 `Node.gdns` のようなファイルが生成されます。
![script_attach.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205472/801b4171-b302-5965-c4d3-138c6c8750bc.png)
5. アタッチしたスクリプトのインスペクタにある `NativeScript` → `Library` に先程用意した `new_gdnativelibrary.gdnlib` を指定します。その後、 `Node.gdns` の方を保存します。(Ctrl + s で保存されます)
![inspector.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205472/613b107c-d58f-1100-5831-7f9a553f7bcd.png)
6. `Node.tscn` を保存し、メインシーンに指定します。

この状態で現在のシーンを実行し、コンソールに `Hello, world.` と出ていれば成功です！

## WebAssembly化するための開発環境整備

ここからが本題ですが私も[orion78frさんの手法](https://github.com/orion78fr/godot_keepass_rust_totp/actions/runs/900998393/workflow#L255)を真似ただけですので詳細がよくわからない部分が結構あります。あと本題とは言いましたがかなりニッチなパートであることは理解しています。誰かの手助けになれば幸いという気持ちで書いています。

本筋としては、Rustツールチェインの内emscriptenを使ったwasm化が行える`wasm32-unknown-emscripten`を使用します。emscriptenを使用しない`wasm32-unknown-unknown`の方でのやり方はまだないようです。

### 依存関係追加

まずは依存関係を追加していきます。emscriptenを使用するためにemsdkを入れていきますが、emsdk関連の知識が要求されることはないです。(というか僕もわかってない)

`apt` , `git clone` , `rustup` コマンドを実行していきましょう。以下ホームディレクトリに移ってから行っています。

:::note warn
ホームディレクトリ以外でも構いませんがその場合は間違えないように適宜読み替えてください。
:::

```bash:bash
cd ~
sudo apt install python3
git clone https://github.com/emscripten-core/emsdk.git
rustup target add wasm32-unknown-emscripten
```

`git clone` で取ってきた `emsdk` フォルダに移ります。最新のファイルが入るように `git pull` コマンドを打ち、 `emsdk` の必要なバージョンを取ってきます。

```bash:bash(@~)
cd emsdk
git pull
./emsdk install 2.0.17
./emsdk activate 2.0.17
```

`emsdk` のバージョンは `2.0.17` である必要があります。すべてのバージョンを試したわけではないですが、現在の最新バージョンでは条件が足りないのかコンパイルできませんでした。

`activate` を実行すると最後にこう書いてあります。

```
- Configure emsdk in your shell startup scripts by running:
    echo 'source "~/emsdk/emsdk_env.sh"' >> $HOME/.bash_profile
```

`emsdk_env.sh` は環境変数などをアクティブにするためのコマンドが含まれていますので、とりあえず `.wasm` をコンパイルする際には予め実行しておくようにしましょう。

```bash:bash
source "~/emsdk/emsdk_env.sh"
```

このコマンドを実行後に、emsdkフォルダの存在位置が `$EMSDK` 変数で参照できるようになります(本記事では `$EMSDK == ~/emsdk` )。そのため、以降本記事でも `$EMSDK` 変数を使用していきます。

ここからがちょっとというかかなり大変なところになります。頑張りましょう。

### `wasm-opt` への細工

まずは、wasmファイルを最適化するための`wasm-opt`というアプリケーションに対して、 `-all` というオプションを渡すための細工をします。

オリジナルのものを別なファイルに保存しておき、コマンドの中身を書き換えます。(強引...)

```bash:bash(@$EMSDK)
mv upstream/bin/wasm-opt upstream/bin/wasm-opt-bak
```

```bash:upstream/bin/wasm-opt
#!/bin/bash

$EMSDK/upstream/bin/wasm-opt-bak $@ -all
```

ファイルに実行権限を与えます。

```bash:bash
chmod +x $EMSDK/upstream/bin/wasm-opt
```

### リンカー `emcc-test` の作成

デフォルトのリンカー( `emcc` )ではなく、細工を施したリンカーを用意します。orion78frさんの手法そのままに `emcc-test` という名前にしています。場所はどこでもいいので個人的に好きな `~/bin/` 以下に置くこととします。(PATHを通してね)

```bash:~/bin/emcc-test
#!/bin/bash

arr=()

for f in "$@"; do
    if [[ "$f" == *.rlib ]]; then
        #echo Extracting $f
        
        ar --output "$(dirname $f)" -x $f
        
        ar -t $f | grep .o | while read o; do
            fo=$(dirname $f)/$o
            
            #echo File $fo
            arr+=("$fo")
        done
    else
        #echo Passing arg $f
        arr+=("$f")
    fi
done

emcc ${arr[@]}
```

`ar` コマンドはアーカイブを作成するコマンドらしい...? 正直 `emcc` を呼ぶ前に特殊なことをしているということしかわからない...まぁ動いてしまえばこっちのものです!

`emcc-test` にも実行権限を与えておきましょう。

```bash:bash
chmod +x ~/bin/emcc-test 
```

### 環境変数の用意

あともう少しです！コンパイル前に環境変数をexportしておきます！( `config.toml` に書く方法ではうまく行かなかったため直接exportしています。)

```bash:bash
export C_INCLUDE_PATH="$EMSDK/upstream/emscripten/cache/sysroot/include/"
export EMMAKEN_CFLAGS="-s SIDE_MODULE=1 -shared -Wl,--no-check-features -all"
```

`C_INCLUDE_PATH` はemsdkにあるライブラリを使いたいのですからわかるのですが、`EMMAKEN_CFLAG` の方はビルド用のおまじないみたいです()

### `./cargo/config.toml` への追記

`.dll` のとき同様、コンパイル設定を書いていきます！コンパイルまであと少し！

以下のように追記してください

```diff_toml:.cargo/config.toml
[build]
target = "x86_64-pc-windows-gnu"

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"

+ [profile.release]
+ opt-level = "s"
+ overflow-checks = false
+ debug-assertions = false
+ lto = true
+ panic = "abort"

+ [target.wasm32-unknown-emscripten]
+ linker = "emcc-test"
+ rustflags = "-C link-args=-fPIC -C relocation-model=pic -v"
```

<details><summary>コピペ用</summary>

```toml
[build]
target = "x86_64-pc-windows-gnu"

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"

[profile.release]
opt-level = "s"
overflow-checks = false
debug-assertions = false
lto = true
panic = "abort"

[target.wasm32-unknown-emscripten]
linker = "emcc-test"
rustflags = "-C link-args=-fPIC -C relocation-model=pic -v"
```
</details>

### コンパイル

ここまで正確にできていれば、あとはコンパイルするのみです！

```bash:bash
cargo build --release --target=wasm32-unknown-emscripten
```

実行後、目的の `target/wasm32-unknown-emscripten/release/hello_godot.wasm` が生成されています。

### 動作確認

最後に動作確認しましょう！ `.wasm` の方はHTMLにエクスポートして確かめます。

`.dll` のときと同様に、 `new_gdnativelibrary.gdnlib` に `hello_godot.wasm` ファイルを指定します。今回は `HTML5 -> wasm32` に指定しましょう

`プロジェクト` → `エクスポート` からエクスポートを行います。プリセットに `HTML5` を追加し、Export Typeに `GDNative` を指定した後、「プロジェクトのエクスポート」をクリックしてエクスポートします。エクスポート先では新たなディレクトリを作成してエクスポートすることを推奨します。また名前も `index.html` にしておくと楽です。

![export_wasm2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205472/8ced675c-8294-482d-2b18-02359cf42754.png)

その後例えば `python3 -m http.server` などのコマンドを使用してディレクトリをホストし、ブラウザを起動してアクセスしてみます。デベロッパーツールの出力に `Hello, world.` と表示されていれば無事にコンパイルが完了しています！

# シューティングゲーム制作編

以降、小話を一つと考察を行って本記事を締めたいと思います。

## オブジェクトプール

シューティングゲームってゲームの中では(僕が今回作ったようなレベルのものならば)簡単に作れそうな気がしませんか？UnityやGodotのように予め当たり判定用のコライダーのような仕組みがあるゲームエンジンならそのように思えるかもしれません。

しかし過去のプログラム経験が浅かった自分は一度挫折したことがあります。それはメモリ管理への意識が薄かったからでした。

初心者だった自分は敵に当たった弾や外に出ていった弾を `free` ( `destroy` や `del` とも。)してしまっていたのです！！！毎フレーム無限にメモリ確保と解放を行っていたのでは、いくら高性能な言語でも遅くなってしまいます。ましてや、当時の自分は `pygame` でゲームを作っていたのですが、限界は弾を円形に飛ばしたところですぐに来てしまいました。

これを解決する手法は至ってシンプルです。弾を「使いまわせ」ば、メモリ解放や確保の機会が極端に減ります。この手法を「オブジェクトプール」と呼びます[^5]！

[^5]: 余談ですが、東方紅魔郷に登場する[咲夜さん](https://dic.nicovideo.jp/a/%E5%8D%81%E5%85%AD%E5%A4%9C%E5%92%B2%E5%A4%9C)も画面の外に出たナイフは回収して使いまわしているそうです。人力オブジェクトプール...

挫折の経緯があって自分の中ではとりあえずオブジェクトプールを実装できればシューティングゲームは作れるのではないかという認識になっています。(もっと作り込んでいくとなると話は別ですが...)

今回のGodot+Rustによる制作では、Godotのシグナル機能がオブジェクトプール制作にとても役立ちました。

```Rust:送信側(画面外に出たときに実行)
#[export]
fn cartridge_fallen(&self, owner: &Area) {
    // 親ノードは現在ステージで、ステージの管理下から外す
    unsafe {
        let parent = owner.get_parent().unwrap().assume_safe();
        parent.remove_child(owner.assume_shared());
    }
    // 所有権に関してユニークと仮定 (areaにはポインタが入ります)
    let area = unsafe { owner.assume_unique() };
    // ポインタ(あるいはアドレス)をVariantにキャストしてシグナルとして送信
    owner.emit_signal("collect", &[Variant::from_object(area)]);
}
```

```Rust:受信側(シグナルを受け取り、チャージ)
#[export]
fn collect_bullet(&mut self, _owner: &Area, bullet_var: Variant) {
    // 動的な変数Variantの値をAreaのポインタにキャスト
    let bullet_area: Ref<Area, Unique> =
        unsafe { bullet_var.try_to_object().unwrap().assume_unique() };
    // マガジンに貯める
    self.mut_magazine().charge_bullet(bullet_area);
}
```

シグナルを受け取ったら弾を格納する配列に戻せば、それはオブジェクトプールです。今回は自機弾、敵であるエイリアン、敵の弾をオブジェクトプールで実装しました。Unityと比べるとGodotは仕組みがシンプルで扱いやすいように感じたのですが、特にシグナルという仕組みはUnityのイベントハンドラ等と比べてもわかりやすいように感じます。

## NULL安全なRustはゲーム制作向き言語なのか？[^6]

[^6]: この節の作業中にフランちゃんのテーマを聞いていたので[オマージュ](https://dic.nicovideo.jp/a/u.n.%E3%82%AA%E3%83%BC%E3%82%A8%E3%83%B3%E3%81%AF%E5%BD%BC%E5%A5%B3%E3%81%AA%E3%81%AE%E3%81%8B%3F)しました。シューティングゲーム繋がりということで...(レベルが違いすぎますが)

この節は完全にポエムになりますが、ゲームをデプロイしたのでポエムる資格はあるだろうということでポエムって行きます!

...と思いましたが長いので折りたたみます

<details><summary>推敲を全くしていない駄文</summary><div>

今回GodotとRustを連携させてゲームを作ったわけですが、当然大変でした！！

なぜかって、継承等が存在するクラス型オブジェクト指向をGodotはめちゃくちゃ使っているのに対して、Rustは真っ向からクラス型オブジェクト指向を否定する言語ですし、かみ合わないのは当然です！

しかしgdnativeクレートの作りが良いのか、Godotの仕組みが良いのかわかりませんが、むしろノードたちやそのAPIとそれを操るロジックの分離という観点ではそこまで不快な感じはまったくなかったです。

そして書いているうちにGodotだけの問題ではないんじゃないかという以下の問題に気づきました。

- グローバル変数しかないのか？というようなunsafeやunwrapの嵐(これはGodotのせいであるとも言える)
- まるでシングルトンパターンかのように構造体に連なる変数の数々

なぜこうなってしまったのか...それは「**ゲームは組合せ爆発を楽しむものだ**」からではないか？？と自分は考察しました。

情報量が多いほど楽しいのがゲームです。

例えばポケモン。色違いのポケモンというのはめちゃくちゃ レア ですよね？例えばアチーブメント機能。多くの実績を解除したというのは多くの経験をしたということなので珍しいものです。珍しいということはそれだけ情報量が多いといえます[^7]。情報量の多さは組み合わせ爆発につながります。将棋や囲碁なんかは、まさに組合せ爆発をAIがどう攻略するか？みたいな話が出ますよね。

情報量や組み合わせが多いと条件分岐は複雑になりがちで、フローチャートに起こしにくくなります。麻雀を実装しよう！ってなると楽しそうに聞こえますが、麻雀のフローチャートを描けって言われたら「無理だろ！」ってなります。でも、ゲーム制作というのは究極的にはこのフローチャートをどう実現するかということに等しいです！(強引)

麻雀を打つときに脳内にフローチャートを描いたりはしないのと同様、ゲーム制作をしている最中はおそらく多くの「仮定」を置くことで、組み合わせ爆発に対抗し、制作を楽にしていると言えます。

例えば「交通止めをしているNPCを横切るときに主人公がモンスターの卵を孵化させる」ようなことはないだろうみたいな仮定です。こんな珍しい現象をいちいち気にしていたらゲームなんて作れません。ゲーム制作は多くの仮定の元で成り立っているのでしょう。

ここでRustやElmなどを引き合いに出します。Elmアーキテクチャは強引に言えば「最初からすべての場合に対応できるようにすればバグは生まれないよね」というようなものですし、Rustは持ち前のOption型やResult型を使いつつ、グローバル変数などを禁止しできるだけ「起きうる状況を仮定ではなく確定させる」ような特徴を持っていると言えるでしょう。

もちろん、そのゲームを"完全"なものとしたいならば、RustやElmのこういった性質は喉から手が出るほど欲しくなります。ここまでロジックエラーとその他のエラーをごちゃまぜに書いてきましたが、RustやElmは時としてこのロジックエラーまでもを防ぐ可能性を持っているスーパー言語ですから。

しかしそれが「仮定駆動開発」とは正反対であるというのは明らかです。こういう場面ではリフレクションを持っている言語や動的型付けの言語のほうが楽に事が進みます。

...ふぅ...

</div></details>

つまり結論ですが、当たり前のようなことですが開発速度と正確性はトレードオフであり、「すべてを」Rustで行うゲーム開発は苦行であるというのは言い逃れができそうにないということが言いたいことになります。

### じゃあどうしろと？

今回、GUIなどはPythonライクな言語であるところのGDScriptで書いたのですが、GDScript良いですね。ゲーム制作に必要十分な言語というような感じがしました。でもRustacean的には物足りない部分があったのも事実です。

結局のところ、両方を上手に使い分けるのが一番開発効率が良さそうです。パフォーマンスや正確性を求められる場所はRustで、仮定駆動開発をしたい箇所はGDScriptで書くようにするのが、Godotで開発する場合は最適解ではないかと思います。次回作はそのように作ってみたいです。

[^7]: そういう傾向があるというだけで正確な話はしていません、ポエムなので許して...情報理論とか情報のエントロピーとか確率理論とかやってる人からみればむしろ誤りな解説かもです

# 終わりに

とは言ったものの、まだECSをやたら主張するbevyとAmethystにしっかりと入門できていなかったので、次回の記事はECSを通してRustによるゲーム制作の可能性の模索を続ける[^8]ようなものになる気がします。

[^8]: もっとも、GodotもECSでしたし、bevyに触れてみたところ結局シングルトンパターンで頑張っているような印象を受けたので、Rustのゲーム制作にまつわる大変さを軽減できるものであるのかどうかと聞かれると今は懐疑的になっていますが...いや！やってから考えるべきですね！精進します！

Unityを含め、ゲーム制作のベストプラクティスはなんだろうっていつも妄想しているので、そういった関連のコメントを頂けると大変嬉しいです！ ここまで読んでいただきありがとうございました！

# 参考・引用等

- [Godot EngineからRustを呼ぶ https://zenn.dev/kawaxumax/articles/e0dedf3f6d4219](https://zenn.dev/kawaxumax/articles/e0dedf3f6d4219)
- [godot-rust/godot-rust: Rust bindings for GDNative](https://github.com/godot-rust/godot-rust)
- [Support wasm32-unknown-emscripten target · Issue #647 · godot-rust/godot-rust https://github.com/godot-rust/godot-rust/issues/647](https://github.com/godot-rust/godot-rust/issues/647)
- [Your first game — Godot Engine (stable) documentation in English](https://docs.godotengine.org/en/stable/getting_started/step_by_step/your_first_game.html)
- [Scenes and nodes — Godot Engine (stable) documentation in English](https://docs.godotengine.org/en/stable/getting_started/step_by_step/scenes_and_nodes.html)
- [最初のゲーム — Godot Engine (stable)の日本語のドキュメント (リンク切れ) https://docs.godotengine.org/ja/stable/getting_started/step_by_step/your_first_game.html](https://docs.godotengine.org/ja/stable/getting_started/step_by_step/your_first_game.html)
- [シーンとノード — Godot Engine (stable)の日本語のドキュメント (リンク切れ) https://docs.godotengine.org/ja/stable/getting_started/step_by_step/scenes_and_nodes.html](https://docs.godotengine.org/ja/stable/getting_started/step_by_step/scenes_and_nodes.html)

## シューティングゲームに使用させていただいた素材

### 制作環境

- Godot Engine https://godotengine.org/
- Rust         https://rust-lang.org/ja
- VSCode       https://code.visualstudio.com/
- MagicaVoxel  https://ephtracy.github.io/

### 楽曲等

- メインBGM : 8bit11 魔王魂 (森田公一氏) https://maou.audio/
- 効果音各種:        無料効果音          https://taira-komori.jpn.org/freesound.html