Rust + SDL2でゲーム開発

## はじめに

RustとSDL2を使ってゲームを開発する環境を作ったので紹介します。
最近はUnreal EngineやUnity等のゲームエンジンを使った開発が主流ですが
ゲームプログラミングは内部の仕組みや設計を勉強するのに役立つと思います。

## SDLとは

SDLとはSimple DirectMedia Layerの略で画面描画、音声出力、キー入力等の機能を提供するライブラリです。
SDL自体はC言語で書かれていますが、様々なプログラミング言語向けのラッパーが用意されています。
Rustではrust-sdl2というクレートを使ってSDLの機能を利用できます。

## セットアップ

事前にRustを[インストール](https://www.rust-lang.org/ja/learn/get-started)しておきます。

以下はWindows(64bit)用のセットアップ手順です。

1. リポジトリの[リリースページ](https://github.com/libsdl-org/SDL/releases)から`SDL2-devel-2.x.x-VC.zip`をダウンロードし展開してください。

2. 展開したフォルダ内の`lib\x64`フォルダを開いてください。
   4つのファイルが置いてあります。

    ![file.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2693350/7841f796-d4e3-c8f8-497d-05adbb484753.png)

3. 上記のファイルを以下のパスにコピーしてください。
   
   ```
   C:\Users\{USER_NAME}\.rustup\toolchains\{TOOLCHAIN}\lib\rustlib\{TOOLCHAIN}\lib
   ```
   
   * `{USER_NAME}`は自身のアカウント名に置き換えてください。
   
   * `{TOOLCHAIN}`はツールチェイン名に置き換えてください。
     通常は`stable-x86_64-pc-windows-msvc`の場合が多いようですが
     該当のフォルダがない場合は`rustup show`でツールチェイン名を確認してください。

    ![toolchain.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2693350/f911300a-5b8a-0fd6-5128-7ccaa3fcbfb7.png)

以上でセットアップは完了です。

## 実行

では実際にコードを動かしましょう。

1. 新しいプロジェクトを作成します。
   
   ```
   $ cargo new sample_sdl2
   ```

2. `sample_sdl2`内の`Cargo.toml`に以下を追加します。
   sdl2の最新バージョンは[crates.io](https://crates.io/crates/sdl2)で確認してください。
   
   ```
   [dependencies]
   sdl2 = "0.32.0"
   ```

6. `main.rs`に以下の処理を行うコードを入力します。
    1. ライブラリを初期化
    2. ウィンドウを表示
    3. 画面を描画するCanvasのインスタンスを取得
    4. 以下の処理を無限ループで実行
        1. 終了イベントが受け取ったらループを抜ける
        2. 画面をクリア
        3. 矩形を描画
        4. 画面を更新して待機
<details><summary>main.rs</summary>

```rust
use sdl2::event::Event;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use std::time::Duration;

pub fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    let window = video_subsystem
        .window("Rust-SDL2", 800, 600)
        .position_centered()
        .build()
        .unwrap();
    let mut canvas = window.into_canvas().build().unwrap();
    let mut event_pump = sdl_context.event_pump().unwrap();
    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. }
                | Event::KeyDown {
                    keycode: Some(Keycode::Escape),
                    ..
                } => break 'running,
                _ => {}
            }
        }
        canvas.set_draw_color(Color::RGB(0, 0, 0));
        canvas.clear();

        canvas.set_draw_color(Color::RGB(255, 255, 255));
        canvas.fill_rect(Rect::new(350, 250, 100, 100)).unwrap();

        canvas.present();
        std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
    }
}
```

</details>

7. `cargo run`で実行します。
   ウィンドウの真ん中に矩形が表示されています。

    ![window1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2693350/9423d20a-3571-584a-16a6-9b35989a1534.png)

## キー入力で矩形を移動させる

上記のコードではキーを押しても何も反応しません。
キー入力操作で矩形を移動できるようにします。

座標を表す変数を追加します。

```rust
let (mut x, mut y) = (350, 250);
```

次にキー入力に応じて座標を移動させる処理を追加します。
キーボード状態を取得できる`KeyboardState`構造体を取得します。

```rust
let state = event_pump.keyboard_state();
```

キーが押されたかを判定するは`is_scancode_pressed`関数を呼び出します。
引数には[Scancode列挙体](https://docs.rs/sdl2/latest/sdl2/keyboard/enum.Scancode.html)を渡します。
以下のコードで矢印キーの入力に応じて座標を移動させています。

```rust
if state.is_scancode_pressed(Scancode::Up) {
    y -= 5;
}
if state.is_scancode_pressed(Scancode::Down) {
    y += 5;
}
if state.is_scancode_pressed(Scancode::Left) {
    x -= 5;
}
if state.is_scancode_pressed(Scancode::Right) {
    x += 5;
}
```

矩形を描画する処理に座標の変数を渡します。

```rust
canvas.fill_rect(Rect::new(x, y, 100, 100)).unwrap();
```

再度`cargo run`を実行します。
今度は矢印キーを押して矩形を移動させることができます。

![move.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2693350/5aee80fc-18cb-c1ac-ffed-baffe12bc63c.gif)

## おわりに

ゲーム開発は書いたコードが視覚的に分かりやすく反映されるため楽しいです。
ゲームが好きな方はゲーム開発にも挑戦してみてはいかがでしょう。

## 参考文献

https://www.libsdl.org/

https://github.com/Rust-SDL2/rust-sdl2

https://crates.io/crates/sdl2

https://docs.rs/sdl2/latest/sdl2/