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)

## システム

[３−１](https://qiita.com/mas-yo/items/e1f89bea5f84b7041462)に引き続き、実装を進めます。
`Component` `ComponentContainer` ができたので、次に `System` を作ってみましょう。

その２で作った（ECSの）システムは、単なる関数として、

```Rust
fn update_velocity(velocity: &mut Velocity, input: &Input) {}
```

この様な形になっていました。
よく見ると、この関数名の `velocity` と、引数の `&mut Velocity` は、表現として冗長な気がしますね。
例えば、あとから、 `Velocity` の名前を変えてしまった場合、 `update_velocity` の方も変えることになり、面倒なことになりそうです。

そこで、同じ関数名で引数が違うだけで、別の機能を表現できる様にしました。
Rustでは関数のオーバーロードがありませんが、下記の様な形で実現できます。


```Rust
pub(crate) trait SystemInterface {
    type Update;
    type Refer;
}
pub(crate) trait SystemProcess: SystemInterface {
    fn process(update: &mut Self::Update, _ref: &Self::Refer);
}

pub(crate) struct System<U, R> {
    phantom: PhantomData<(U, R)>,
}

impl<U, R> SystemInterface for System<U, R> {
    type Update = U;
    type Refer = R;
}
```

そして、`update_velocity` に相当する関数は、下記の様に書きます。

```Rust
impl SystemProcess for System<CContainer<Velocity>, CContainer<Input>> {
    fn process(velocities: &mut Self::Update, inputs: &Self::Refer) {
        velocities
            .iter_mut()
            .zip_entity(inputs)
            .for_each(|(velocity, input)| {
                velocity.x = 0f32;
                velocity.y = 0f32;
                if input.left {
                    velocity.x = -2f32;
                }
                if input.right {
                    velocity.x = 2f32;
                }
                if input.up {
                    velocity.y = -2f32;
                }
                if input.down {
                    velocity.y = 2f32;
                }
            });
    }
}

```

こうしておくと、呼び出し側では、

```Rust
System::process(&mut velocities, &inputs);
```
の様な形で呼び出せます。
関数名は `process` で共通となり、引数の型によって機能が変わるようになっています。

`System<CContainer<Velocity>, CContainer<Input>>::process` の中身は実装は、同じエンティティIDを持つ `Velocity` `Input` に対して、`Input`の情報を`Velocity`に反映させるようになっています。

### 敵の動きの実装

では、今回（その３）のゴールである、敵の動きを実装してみます。
まずは、敵の `Velocity` です。


```Rust
impl SystemProcess
    for System<CContainer<Velocity>, (&CContainer<Position>, &CContainer<MoveTarget>)>
{
    fn process(velocities: &mut Self::Update, pos_tgt: &Self::Refer) {
        velocities
            .iter_mut()
            .zip_entity2(pos_tgt.0, pos_tgt.1)
            .for_each(|(vel, pos, target)| {
                let mut tmp = Vector::default();
                tmp.x = target.x - pos.x;
                tmp.y = target.y - pos.y;
                vel.x = tmp.x / 50f32;
                vel.y = tmp.y / 50f32;
            });
    }
}
```

敵の `Velocity` は、自分の `Position` と `MoveTarget` との差分から計算する必要があります。
よって、System::Refer（Velocityを更新するために参照するデータ）の型は、

```Rust
(&CContainer<Position>, &CContainer<MoveTarget>)
```

このようなタプルとなりました。
上記ソース内の、`zip_entity2()` は、２つの `ComponentContainer` を受け取って、同じエンティティIDのコンポーネントを順に返してくれるイテレータです。

### 敵かどうかの判定

では、 `MoveTarget` 自体は、どのように値を更新したら良いでしょうか？

`MoveTarget` を決めるためには、他のキャラを見て、これが敵だと認識できなければなりません。
そこで、Team というコンポーネントを導入することにしました。

```Rust
pub(crate) struct Team {
    team_id: u32,
}
```

プレイヤーキャラ、敵キャラは、 `Team` を持っていて、チームIDが違うもの同士は敵、という意味です。

`MoveTarget` は、他のすべてのキャラのうち、自分から一定距離の範囲にいて、自分とは違うチームのキャラの、位置に設定される、という処理にしました。


```Rust
impl SystemProcess for System<CContainer<MoveTarget>, (&CContainer<Team>, &CContainer<Position>)> {
    fn process(move_targets: &mut Self::Update, team_pos: &Self::Refer) {
        let (teams, positions) = team_pos;
        move_targets
            .iter_mut()
            .zip_entity2(teams, positions)
            .for_each(|(target, self_team, self_pos)| {
                teams
                    .iter()
                    .filter(|(_, team)| team.team_id() != self_team.team_id())
                    .for_each(|(entity_id, _)| {
                        if let Some(pos) = CContainer::<Position>::get(positions, entity_id) {
                            let distance = pos.distance((self_pos.x, self_pos.y));
                            if distance < 100f32 {
                                target.x = pos.x;
                                target.y = pos.y;
                            } else {
                                target.x = self_pos.x;
                                target.y = self_pos.y;
                            }
                        }
                    });
            });
    }
}
```

ここまでで、敵の動きの実装は完了です。

次回、３−３で、メインの処理を実装します。