Rust + Entity Component System で仕様変更に強いゲーム設計 その３−３ 〜 メイン部分

## 目次

[その１ 〜 序文](https://qiita.com/mas-yo/items/e7d1e247e5d4a9f47e92)
[その２ 〜 キャラの移動](https://qiita.com/mas-yo/items/207244f04dd442cb567c)
[その３−１ 〜 コンポーネントの設計](https://qiita.com/mas-yo/items/e1f89bea5f84b7041462)
[その３−２ 〜 システムの設計](https://qiita.com/mas-yo/items/ca97fe6ed961d77be715)
【イマココ】[その３−３ 〜 メイン部分](https://qiita.com/mas-yo/items/9f0459dd942a874c6273)
[その４−１ 〜 剣を表示](https://qiita.com/mas-yo/items/daae411935d138d85c95)
[その４−２ 〜 アニメーションコンポーネント](https://qiita.com/mas-yo/items/678d775e4caf5e14abe7)
[その４－３ 〜 アニメーションを動かす](https://qiita.com/mas-yo/items/3db0a920cb76c75bca51)
[その５－１～ あたり判定](https://qiita.com/mas-yo/items/598ed52f5c08a0375ead)
[その５－２～ やられアニメーション](https://qiita.com/mas-yo/items/cf017ac1ebdd7ce25d86)
[その６ 〜 これまでの振り返り](https://qiita.com/mas-yo/items/22ead1dc226da4df54e2)

その３−１、３−２で、コンポーネントとシステムが出来上がりました。
では、それらを駆動するメイン部分である `Game` 構造体を作ってみます。

## Game 構造体

このような作りにしました。

```Rust
struct Game {
    next_entity_id: EntityID,
    inputs: CContainer<Input>,
    teams: CContainer<Team>,
    move_targets: CContainer<MoveTarget>,
    positions: CContainer<Position>,
    velocities: CContainer<Velocity>,
    character_views: CContainer<CharacterView>,
}
```

`next_entity_id` は、次に生成するエンティティのエンティティIDです。
エンティティを作るたびに、インクリメントしていきます。

他は、各コンポーネントのコンテナです。

次に、プレイヤーキャラを生成するメソッドと、敵キャラを生成するメソッドです。

```Rust
impl Game {
    fn create_hero(&mut self) {
        let entity_id = self.next_entity_id;

        self.inputs.push(entity_id, Input::default());
        self.teams.push(entity_id, Team::new(0));
        self.positions.push(
            entity_id,
            Position {
                x: 150f32,
                y: 150f32,
            },
        );
        self.velocities.push(entity_id, Velocity::default());
        self.character_views.push(
            entity_id,
            CharacterView {
                color: Color::GREEN,
                radius: 10f32,
                ..Default::default()
            },
        );

        self.next_entity_id = self.next_entity_id + 1;
    }

    fn create_enemy(&mut self) {
        let entity_id = self.next_entity_id;

        self.move_targets.push(entity_id, MoveTarget::default());
        self.teams.push(entity_id, Team::new(1));
        self.positions
            .push(entity_id, Position { x: 10f32, y: 10f32 });
        self.velocities.push(entity_id, Velocity::default());
        self.character_views.push(
            entity_id,
            CharacterView {
                color: Color::RED,
                radius: 15f32,
                ..Default::default()
            },
        );

        self.next_entity_id = self.next_entity_id + 1;
    }
}
```

それぞれ、必要なコンポーネントを生成して追加しています。

次に、quicksilverの `trait State` の実装です。

```Rust
impl State for Game {
    fn new() -> Result<Game> {
        let mut game = Self::default();
        game.create_hero();
        game.create_enemy();
        Ok(game)
    }

    /// Will happen at a fixed rate of 60 ticks per second under ideal conditions. Under non-ideal conditions,
    /// the game loop will do its best to still call the update at about 60 TPS.
    ///
    /// By default it does nothing
    fn update(&mut self, _window: &mut Window) -> Result<()> {
        System::process(
            &mut self.move_targets,
            &(&self.teams, &self.positions),
        );
        System::process(&mut self.velocities, &self.inputs);
        System::process(&mut self.velocities, &(&self.positions, &self.move_targets));
        System::process(&mut self.positions, &self.velocities);
        System::process(&mut self.character_views, &self.positions );
        Ok(())
    }
    /// Process an incoming event
    ///
    /// By default it does nothing
    fn event(&mut self, event: &Event, _: &mut Window) -> Result<()> {
        match event {
            Event::Key(key, state) => {
                let mut pressed = false;
                if *state == ButtonState::Pressed {
                    pressed = true;
                } else if *state == ButtonState::Released {
                    pressed = false;
                }
                match key {
                    Key::A => {
                        self.inputs.iter_mut().for_each(|(_, i)| {
                            i.left = pressed;
                        });
                    }
                    Key::D => {
                        self.inputs.iter_mut().for_each(|(_, i)| {
                            i.right = pressed;
                        });
                    }
                    Key::W => {
                        self.inputs.iter_mut().for_each(|(_, i)| {
                            i.up = pressed;
                        });
                    }
                    Key::S => {
                        self.inputs.iter_mut().for_each(|(_, i)| {
                            i.down = pressed;
                        });
                    }
                    _ => {}
                }
            }
            _ => {}
        }
        Ok(())
    }

    fn draw(&mut self, window: &mut Window) -> Result<()> {
        window.clear(Color::WHITE)?;
        System::process(window, &self.character_views);
        Ok(())
    }
}

```

`Game` 生成時に、プレイヤーキャラと敵キャラを生成するようにしました。

`update()` では、`System::process()` を順に更新するコンポーネントを引数として渡しながら、呼んでいます。
ただし、`Window` の更新だけは、`draw()` で行います。

`event()` の処理では、キー入力情報に従って `Input` コンポーネントを更新しています。


## 完成！

ここまでの実装で、プレイヤーキャラに加えて、敵キャラも実装することができました。
そして、エンティティ、コンポーネント、システムの基本的な設計が完了しました。

めでたしめでたし。

次からは、いろんな機能を追加しながら、この設計が、「仕様変更に強い」かどうかを、検証していきます。

次回につづく・・・