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)


前回は、アニメーションを再生する準備のため、キャラの向きを示す線（＝剣）を表示するところまで実装しました。

今回から、いよいよアニメーションの実装に入ります。

まず、アニメーションを制御するコンポーネントとして`Animator`を作ります。


```Rust:Animator

//K:アニメーションを識別するためのID（キー）
//V:1フレーム分のデータを示す型
#[derive(Default)]
pub(crate) struct Animator<K, V>
where
    K: Hash + Eq,
{
    playing_id: Option<K>,                   //現在再生中のアニメーションID 再生していないならNone
    current_frame: usize,                    //現在何フレーム目か
    animations: HashMap<K, Animation<V>>,    //アニメーションデータ
}

impl<K, V> Animator<K, V>
where
    K: Hash + Eq + Copy,
{
    //再生
    pub fn play(&mut self, animation_id: K) {
        if self.animations.contains_key(&animation_id) {
            self.playing_id = Some(animation_id);
            self.current_frame = 0;
        }
    }
    //アニメーションが終わっているか
    pub fn is_end(&self) -> bool {
        if let Some(id) = self.playing_id {
            let anim = self.animations.get(&id).unwrap();
            return !anim.looped && self.current_frame == anim.values.len();
        }
        return false;
    }
    //フレーム更新(毎フレーム呼ばれる想定)
    pub fn update(&mut self) {
        if let Some(id) = self.playing_id {
            if let Some(anim) = self.animations.get(&id) {
                self.current_frame += 1;
                if anim.values.len() <= self.current_frame && anim.looped {
                    self.current_frame = 0;
                }
            }
        }
    }
    //アニメーションの登録
    pub fn register(&mut self, id: K, anim: Animation<V>) {
        self.animations.insert(id, anim);
    }
    //現在のアニメーションの値
    pub fn value(&self) -> Option<&V> {
        let id = self.playing_id?;
        let anim = self.animations.get(&id)?;
        anim.values.get(self.current_frame)
    }
}

//アニメーション１つ分のデータ
//T: 1フレーム分のデータを示す型
#[derive(Default)]
pub(crate) struct Animation<T> {
    looped: bool,        //ループアニメかどうか
    values: Vec<T>,      //アニメーションデータ
}

impl<T> Animation<T> {
    pub fn new(looped: bool, values: Vec<T>) -> Self {
        Self { looped, values }
    }
}
```

`Animator` の中には、複数のアニメーションデータが入っており、それをIDで切り替えて再生する想定です。
今回（その４）では、待機アニメーションと攻撃アニメーションを登録しておいて、デフォルトでは待機アニメ、攻撃時は攻撃アニメを再生する、ということになります。

`Animator`はECSの「コンポーネント」として使います。
一般にコンポーネントは単なるデータで、機能を持たないということになっていると思いますが、コンポーネント内部だけで完結する機能は、コンポーネントのメソッドとして実装して良いと判断しました。

アニメーション１フレーム分のデータは、下記の様になります。

```Rust
#[derive(Default, Clone)]
pub(crate) struct CharacterAnimFrame {
    pub radius_scale: f32,      //円のスケール
    pub weapon_direction: f32,  //武器の向き
}
```

この値が時間とともに変化し、実際の描画にアニメーションが適用されるわけですね。

アニメーションのキーとなる値は、`enum`にしました。

```Rust
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub(crate) enum CharacterAnimID {
    Wait,     //待機
    Attack,   //攻撃
}
```

今回使う`Animator`の別名も定義しておきます。

```Rust
type CharacterAnimator = Animator<CharacterAnimID, CharacterAnimFrame>;
```

ここまでで、アニメーション周りのコンポーネントの準備は完了です。

続いて、`Animator`自体を更新するシステムを実装しましょう。


```Rust
impl<K, V> SystemProcess for System<CContainer<Animator<K, V>>, ()>
where
    K: Hash + Eq + Copy,
{
    fn process(animators: &mut Self::Update, _: &Self::Refer) {
        animators.iter_mut().for_each(|(_, a)| a.update());
    }
}

```

単純に`update()`を呼ぶだけです。

次に、`Animator`から現在の値を取り出し、`CharacterView`に反映させるシステムです。


```Rust
impl SystemProcess for System<CContainer<CharacterView>, CContainer<CharacterAnimator>> {
    fn process(views: &mut Self::Update, animators: &Self::Refer) {
        views
            .iter_mut()
            .zip_entity(animators)
            .for_each(|(view, animator)| {
                if let Some(val) = animator.value() {
                    view.radius_scale = val.radius_scale;
                    view.weapon_direction = val.weapon_direction;
                }
            });
    }
}
```

`radius_scale`と`weapon_direction`をコピーしているだけです。

これで、アニメーションを再生する仕組みができました。

さて、アニメーションを制御するのに、もう１つ、仕組みが必要です。
それは、「どういう条件でどのアニメーションを再生するか」を決める仕組みです。

これについては、また次の投稿で、説明します。