Rustで書かれたゲームエンジンamethyst勉強記#5

<< [#4 エンティティの作成・削除](https://qiita.com/9laceef/items/04e6221ff62fa3fe1b25)　　[#6 アニメーション](https://qiita.com/9laceef/items/3d1ad9f60344643f642e) >>

# ご注意！
この記事の内容は2019/06/17現在の最新版amethystクレート(v0.11.0)では仕様が変わっており動きません！随時更新していきますので、しばしお待ちください。
もし更新が待てなかったり直接尋ねたいことなどありましたらコメントにてお願いいたします。

### はじめに

[#3](https://qiita.com/9laceef/items/5b39fcf5e6fd6340ec78)ではマウスからの入力を扱いました。今回は設定ファイルを用いたキーボード入力について扱っていきます。

## `bindings.ron`

ronファイルについては過去の章を参照。`Key()`に与えるキーの種類については[こちら](https://docs.rs/winit/0.18.0/winit/enum.VirtualKeyCode.html)。ちなみに`Key()`の部分を`Controller()`などに変えると別の入力を扱うこともできると思われます（=>[リファレンス](https://docs.rs/amethyst_input/0.6.0/amethyst_input/enum.Button.html)）。

```ron
(
  axes: {
    "x_axis": Emulated(pos: Key(Right), neg: Key(Left)),
    "y_axis": Emulated(pos: Key(Up), neg: Key(Down)),
  },
  actions: {},
)
```

`"x_axis"`, `"y_axis"`はリストのキーになります。`pos`はそのキーが押されているときに`0.0~1.0`を返し、`neg`は`0.0~-1.0`を返します（キーボードなら`0.0`or`1.0`or`-1.0`）。両方を押していると合計で`0.0`になります。

## `main.rs`

例によってインポートは省略しています。

```rust
// examples/06_animation/main.rs

struct ExampleState;

impl SimpleState for ExampleState {
    fn handle_event(
        &mut self,
        _: StateData<'_, GameData<'_, '_>>,
        event: StateEvent,
    ) -> SimpleTrans {
        if let StateEvent::Window(e) = event {
            if is_key_down(&e, VirtualKeyCode::Escape) {
                return Trans::Quit;
            }
        }
        Trans::None
    }
}

struct ExampleSystem;

impl<'s> System<'s> for ExampleSystem {
    type SystemData = Read<'s, InputHandler<String, String>>;

    fn run(&mut self, input: Self::SystemData) {
        let x_axis = input.axis_value("x_axis");
        let y_axis = input.axis_value("y_axis");
        match (x_axis, y_axis) {
            (Some(x_axis), Some(y_axis)) => {
                println!("x_axis: {}, y_axis: {}", x_axis, y_axis);
            }
            _ => {}
        }
    }
}

fn main() -> amethyst::Result<()> {
    // amethyst::start_logger(Default::default());

    let app_root = PathBuf::from(application_root_dir()).join("examples/05_keyboard_input/");

    let pipe = Pipeline::build().with_stage(
        Stage::with_backbuffer()
            .clear_target([1.0; 4], 1.0)
            .with_pass(DrawFlat2D::new())
    );
    let config = DisplayConfig::load(app_root.join("config.ron"));
    let render_bundle = RenderBundle::new(pipe, Some(config));

    let input_bundle = InputBundle::<String, String>::new()
        .with_bindings_from_file(app_root.join("bindings.ron"))?;

    let game_data = GameDataBuilder::new()
        .with_bundle(render_bundle)?
        .with_bundle(input_bundle)?
        .with(ExampleSystem, "example-system", &[]);

    Application::new(app_root, ExampleState, game_data)?.run();

    Ok(())
}
```

`with_bindings_from_file`メソッドにより、いままで作成していた`InputBundle`に設定ファイルから情報を読み込ませています。
また`ExampleSystem`内でキーボードの入力を受け取っています。

```rust
struct ExampleSystem;

impl<'s> System<'s> for ExampleSystem {
    type SystemData = Read<'s, InputHandler<String, String>>;

    fn run(&mut self, input: Self::SystemData) {
        let x_axis = input.axis_value("x_axis");
        let y_axis = input.axis_value("y_axis");
        match (x_axis, y_axis) {
            (Some(x_axis), Some(y_axis)) => {
                println!("x_axis: {}, y_axis: {}", x_axis, y_axis);
            }
            _ => {}
        }
    }
}
```

`axis_value`にキーを与えることで、対応した軸の値を得ることができます。

### おわりに

お疲れさまでした。`cargo run --example 05`で真っ白な画面が現れ、矢印キーを押して値が変化するのを確認出来たら完成です！

次の章ではアニメーションについて解説する予定です。
その次#7では、今までの知識を用いて簡単なゲームを作ってみようと思います。

### バインディングのキーの型

ちなみに少々面倒ではありますが、キーは`String`ではなく自作のenumなんかを使えます。
`serde`と`serde_derive`クレートを利用し以下のようにキーとなるものを用意します。

```rust
use serde::*;

#[derive(Hash, Eq, PartialEq, Clone, Serialize, Deserialize)]
enum Key {
    X_AXIS, Y_AXIS
}
```

こうすることで、`bindings.ron`は次のようにキーを設定できます。

```ron
(
  axes: {
    X_AXIS: Emulated(pos: Key(Right), neg: Key(Left)),
    Y_AXIS: Emulated(pos: Key(Up), neg: Key(Down)),
  },
  actions: {},
)
```

取得する際は次のようになります。`Borrow<K>`を必要とするため、参照を使っている点には注意。

```rust
let x_axis = input.axis_value(&Key::X_AXIS);
let y_axis = input.axis_value(&Key::Y_AXIS);
```

いつか使う機会があるかもですね。

それでは。