Amethyst: Pong Tutorialを読んでみた 其の一

# 序文

Rustで書かれたRustで書けるRustなゲームエンジンである[Amethyst](https://github.com/amethyst/amethyst)の[Pong Tutorial](https://book.amethyst.rs/stable/pong-tutorial.html)を読んでみた。

英文読んで要約するだけなので詳しい解説が欲しいなら他記事を読んでください。
またがんばって英語を読んでますが頑張らないと読めない程度なので所々間違いがあるかと思います。

訂正箇所があればやさしくコメントしてください。

バージョンは`0.11.1`。

本記事では[4.1 Setting up the project](https://book.amethyst.rs/stable/pong-tutorial/pong-tutorial-01.html)についての内容である。

# Projectの作成

さて先ずは`cargo new`を打ちたい気持ちに駆られますがここはぐっと堪えてください。

[amethyst_tools](https://github.com/amethyst/tools)という超便利アイテムをインストールします。

以下のコマンドを打てばインストール完了。

```
cargo install amethyst_tools
```

次に以下コマンドでProject作成ができます。

```
amethyst new <project_name>
```

`cargo new`のように`Cargo.toml`や`src/main.rs`等が作成されますが、大きな違いは以下の通りです。

- `Cargo.toml`に`[dependencies]`, `[features]`が書き足されてる
- `README.md`に"How to run"が書き足されてる
- `resources/`という設定ファイル等を置くディレクトリ、およびウィンドウ設定ファイル`display_config.ron`が作成されている
- `main.rs`にいっぱい書き足されてる

要はそのままRunさせることができます。
  
## 試しにRunさせてみよう

ここで早速`cargo run`を打ちたい気持ちに駆られますがここはぐっと堪えてください。（二回目）

`README.md`と[GraphicalBackendError](https://github.com/amethyst/amethyst/wiki/GraphicalBackendError)を見ていただければわかりますが、`--features`オプションをつけないと走ってくれません。

つまりコマンドは以下のように打ちます。

> Windows, Linuxの場合：

> ```
> cargo run --features "vulkan"
> ```

> macOSの場合：

> ```
> cargo run --features "metal"
> ```

> グラフィックバックエンド無しで走らせたい場合：

> ```
> cargo run --features "empty"
> ```

ただし`empty`を使った場合いずれ走らなくなるようなのでできるだけVulkanかMetalを使おう。

ようやく準備ができましたので上記に従ってRunさせてください。
（Vulkanの場合ここでエラーが出たりしたら[こちら](https://qiita.com/celluloce/items/4d21944cab3c68feb233)を参照してみてください。Metalは知らん。）

コンパイルが無事成功して実行されると、500×500サイズの淡い紺色のウィンドウが生成されるはずです。

さてこの時点で[4.1 Setting up the project](https://book.amethyst.rs/stable/pong-tutorial/pong-tutorial-01.html)の内容はコンプリートしましたので解説を終わります。

お疲れ様でした。

※まだ続きます



# GameStateの作成

ここから先の内容は`amethyst_tools`が自動で生成するので読まなくても問題ないです。

私が楽しいから書いてます。

## GameStateとは

[3.1 State](https://book.amethyst.rs/stable/concepts/state.html)は以下のように語ります。

> A game state is a general and global section of the game.

ちょっとよくわかりにくいですが、例えば

- ゲーム開始時のロードしてる状態 -> `LoadingState`
- メインメニュー画面を表示してる状態 -> `MainMenuState`
- ゲームをプレイできる状態 -> `GameplayState`
- ゲームを一時停止してる状態 -> `PauseState`
- ゲームが終わりスコア結果等を表示する状態 -> `ResultState`

といった具合です。

GameStateを1つだけ作ってプログラムを詰め込むのではなく、プログラムを各GameState毎にわけることでクオリティの高いゲームがより簡単に作れます。

実行中は[`Trans`](https://docs.rs/amethyst/0.11.1/amethyst/enum.Trans.html)列挙型を使って切り替えることができます。

## モジュールのインポート

以下を書いておけばよし。

```Rust:main.rs
use amethyst::{
    core::transform::TransformBundle,
    ecs::prelude::{ReadExpect, Resources, SystemData},
    prelude::*,
    renderer::{
        pass::DrawShadedDesc,
        rendy::{
            factory::Factory,
            graph::{
                render::{RenderGroupDesc, SubpassBuilder},
                GraphBuilder,
            },
            hal::{format::Format, image},
        },
        types::DefaultBackend,
        GraphCreator, RenderingSystem,
    },
    utils::application_root_dir,
    window::{ScreenDimensions, Window, WindowBundle},
};
```
## GameStateを作成

`Pong`構造体を作成し、[SimpleState](https://docs.rs/amethyst/0.11.1/amethyst/prelude/trait.SimpleState.html)トレイトを実装させます。

```Rust:main.rs
struct Pong;

impl SimpleState for Pong {}
```

`SimpleState`の他に[State](https://docs.rs/amethyst/0.11.1/amethyst/trait.State.html)や[EmptyState](https://docs.rs/amethyst/0.11.1/amethyst/trait.EmptyState.html)があります。

`SimpleState`の方が`State`より扱いやすいので先ずはこっちを使おう。

# main関数を書いていこう

## "?"演算子を使えるように

Rustではエラーハンドリングに`unwrap()`を使ったりしますが、`Result`型を返す関数内では代わりに`?`演算子が使えます。

参考：https://qiita.com/kanna/items/a0c10a0563573d5b2ed0

これを利用するにあたってmain関数は以下のように書きます。

```Rust:main.rs
fn main() -> amethyst::Result<()> {

    Ok(())
}
```

## Loggerを表示させる

実行中のinfo, werning, errorをターミナル上で表示させるために以下文をmain関数に書きます。

```Rust:main.rs
amethyst::start_logger(Default::default());
```

わざわざ自分で`println()`デバッグしなくても良くなります。

超便利。

## display configファイルの作成

ゲームウィンドウの大きさ、ヘッダに書くタイトル等を設定するために`display_config.ron`ファイルを作成します。

`.ron`という得体の知れない拡張子ですが、これはRustに適したシリアライズフォーマット[RON](https://github.com/ron-rs/ron)です。

（Jsonより、RONの方がRustプログラマにやさしいと考えておけばいい。フォーマットがほとんどRustのまんまだから。）

てきとーな場所に置いたら知らないおじさんに怒られてしまいそうなので<sup>[要出典]</sup>、`resources`ディレクトリ内に置いておこう。

```RON:resources/display_config.ron
(
  title: "<project_name>",
  dimensions: Some((500, 500)),
)
```

その他の設定を書き足したければ[`DisplayConfig`](https://docs-src.amethyst.rs/master/amethyst_window/struct.DisplayConfig.html)構造体に従って書いてください。

書き足さなくても動きます。

ここで`main.rs`に戻ってmain関数を書き進めよう。

後でdispley configファイルを読み込むために、ファイルへのパスの変数を書いておきます。

```Rust:main.rs
let app_root = application_root_dir()?;
let display_config_path = app_root.join("resources").join("display_config.ron");
```
`application_root_dir()`関数でCargoプロジェクトのパスが取得されます。

試しに`println!("{:?}", app_root);`とでも書いてみてください。

何も起きません。
（Runさせた時にCargoプロジェクトへのパスがターミナル上に表示されます）

ついでにmain関数の返り値とresourcesディレクトリの作成をすっとばした方に朗報です。

そのままでは後々動きません。読み返してください。

## Windowを生成しよう

ようやくこのセッションでWindowが生成できます。

長かった...
読む側では5分くらいでしょうが書く側は昼食挟んで既に5時間くらい経ってます。

以下文をmain関数に書き足します。

```Rust:main.rs
let game_data = GameDataBuilder::default()
    .with_bundle(WindowBundle::from_config_path(display_config_path))?;

let assets_dir = app_root.join("assets");
let mut game = Application::new(assets_dir, Pong, game_data)?;
game.run();
```
これを書けば後はCargoRunさせるだけですがここはぐっと堪えて（3回目）コードを読んでいきます。
（とっととRunさせたい人はここまで読んでないでしょう？）

[`Application`](https://docs-src.amethyst.rs/stable/amethyst/type.Application.html)はゲームエンジンのルートオブジェクトです。

これに必要なデータをぶち込めば、他のゲーム実行中に必要なグローバルな変数は必要ないです。（正確には[CoreApplication](https://docs-src.amethyst.rs/stable/amethyst/struct.CoreApplication.html)）

[`Application::new()`](https://docs-src.amethyst.rs/stable/amethyst/struct.CoreApplication.html#method.new)を使って、初期状態のAssetのロード, State, GameDataを変数としてぶち込みます。

[Asset](https://book.amethyst.rs/stable/assets.html)はテクスチャ, 音楽等のデータです。
今回全く触れなかったですがいずれ必要になります。
エラーが出るわけでもないので書くだけ書いときましょう。

Stateは最初の方で書いたやつです。
ようやく必要になりました。

GameDataはゲーム実行中のcentral repositoryになります。
要はゲームデータです((雑
[`with()`](https://docs-src.amethyst.rs/stable/amethyst/struct.GameDataBuilder.html#method.with)や[`with_bundle()`](https://docs-src.amethyst.rs/stable/amethyst/struct.GameDataBuilder.html#method.with_bundle)を使って[System](https://book.amethyst.rs/stable/concepts/system.html)やECSバンドルをGameDataに追加していきます。
上コードではDisplayConfigを追加してます。

ここで[`.run()`](https://docs-src.amethyst.rs/stable/amethyst/struct.CoreApplication.html#method.run)を呼ぶことでゲームが動きます。

Windowが生成します。
やったね。

# レンダリングのベースとなるコードを書こう

Windowを生成するだけなら必要ありませんが、何かを表示させたりする時に必要になります。

今書かなくてええやろって？
だって[4.1 Setting up the project](https://book.amethyst.rs/stable/pong-tutorial/pong-tutorial-01.html)に書いてるもん。

コードは長いので[ここ](https://book.amethyst.rs/stable/pong-tutorial/pong-tutorial-01.html#setting-up-basic-rendering)からコピペしてください。
というか`amethyst_toos`で自動生成します。

しっかりコードを読んで行くと私の睡眠時間が消えてしまうのでウルトラざっくり読んで行きます。

`ExampleGraph`構造体を作成して[`GraphCreator`](https://docs.amethyst.rs/v0.10.0/amethyst_rendy/trait.GraphCreator.html)トレイトを実装させます。

`ClearValue::Color`内の変数を書き換えると背景色を変えられます。
好きな色に変えましょう。
（値は0.0-1.0で、RGBAです。例えば`[0.34, 0.36, 0.52, 1.0]`で淡い藍色になります。）

最後にmain関数に戻って`game_data`に新たにメソッドを書き足します。

```Rust:main.rs
let game_data = GameDataBuilder::default()
    ...
    .with_thread_local(RenderingSystem::<DefaultBackend, _>::new(
        RenderingGraph::default(),
    ));
```

以上。

# 次回予告

Pong Tutorialと言いながらPong要素の無かった今回。
このままではタイトル詐欺もいい所なので次回パドルを生成させます。
AssetやGraphCreatorの伏線回収なるか！？

次回 私の気分次第