Unityの継承周りの苦痛をどうにかしたい

## エネミーを実装しよう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188872/9ab3b285-af18-6dff-9d1d-71428cb24fe7.png)

なんてことはない、ごく普通のエネミーコンポーネントである。

## すごいエネミーは移動する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188872/cb6eade3-31a6-2c1a-3a3e-c56f3b62b97a.png)

普通に実装すれば、恐らくはこうなる。

```cs
public class SugoiiEnemy : MonoBehaviour {
  private void Update() {
    // 移動処理
  }
}
```

エネミーは動いたよ、やったね！

## エネミーは体力が0を下回ると死亡する

苦痛の始まりである。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188872/1a8be520-c946-d633-e744-023c39afa5b8.png)

簡単だ、さっそく実装しよう。

```cs
public class Enemy : MonoBehaviour {
  public int hp = 10;
  private void Update() {
    if(hp <= 0) Destroy(gameObject);
  }
}
```

あれ？　Enemy死なないよ？
おっと、間違えた。
SugoiiEnemyでもUpdateを実装しているから、こうでなくては。

```cs
public class Enemy : MonoBehaviour {
  public int hp = 10;
  protected virtual void Update() {
    if(hp <= 0) Destroy(gameObject);
  }
}
```

あれ？　これでも死なない？
あ、SugoiiEnemyの方もも変えなくては。

```cs
public class SugoiiEnemy : MonoBehaviour {
  protected override void Update() {
    base.Update();
    // 移動処理
  }
}
```

苦痛だ。

## 苦痛だ

例はよくないかもしれないが、ニュアンスは伝わったはずだ。
今回はEnemyの派生先クラスがSugoiiEnemy 1つだったから、これだけで勘弁してもらえたものの、気合入れて20個作った、みたいな場合だともう目も当てられない。
継承のネストがもっと深い場合は、もう寝てよし。

## 三つ子問題

見返してみると、私のコードには3種類のUpdateがあった。

```cs
// 継承しておらず、かつ継承されない場合
private void Update(){}
// 継承される場合
protected virtual void Update(){}
// 継承する場合
protected override void Update(){base.Update()}
```

ギャグか？　ギャグなのか？
何かしらの県の条例に違反してるとしか思えない所業である。

この3つのUpdateを状況に応じて使い分けるのがプログラマーの仕事であるのなら、私は今すぐプログラマーをやめたい。

## なぜこんな問題が起こっているのか

MonoBehaviourに`protected virtual void Update(){}`のようなコードがないからだ。
MonoBehaviourに`protected virtual void Update(){}`のようなコードがないから、我々は3種類のUpdateを使い分けるなどという愚行が**できてしまう**のだ。

解決せねば。
解決するか、Unityをやめるかの二択だ。

## 解決法　全部定義してしまう

ないなら足せばいい。
BaseBehaviourみたいなクラスを作って、MonoBehaviourの代わりに継承すればいい。

```cs
    public class BaseBehaviour: MonoBehaviour {
        protected virtual void Reset() {}
        protected virtual void Awake() {}
        protected virtual void OnEnable() {}
        protected virtual void Start() {}
        protected virtual void FixedUpdate() {}
        protected virtual void OnTriggerEnter(Collider other) {}
        protected virtual void OnTriggerEnter2D(Collider2D other) {}
        protected virtual void OnTriggerStay(Collider other) {}
        protected virtual void OnTriggerStay2D(Collider2D other) {}
        protected virtual void OnTriggerExit(Collider other) {}
        protected virtual void OnTriggerExit2D(Collider2D other) {}
        protected virtual void OnCollisionEnter(Collision other) {}
        protected virtual void OnCollisionEnter2D(Collision2D other) {}
        protected virtual void OnCollisionStay(Collision other) {}
        protected virtual void OnCollisionStay2D(Collision2D other) {}
        protected virtual void OnCollisionExit(Collision other) {}
        protected virtual void OnCollisionExit2D(Collision2D other) {}
        protected virtual void OnMouseEnter() {}
        protected virtual void OnMouseOver() {}
        protected virtual void OnMouseUp() {}
        protected virtual void OnMouseDrag() {}
        protected virtual void OnMouseDown() {}
        protected virtual void OnMouseUpAsButton() {}
        protected virtual void OnMouseExit() {}
        protected virtual void Update() {}
        protected virtual void LateUpdate() {}
        protected virtual void OnWillRenderObject() {}
        protected virtual void OnPreCull() {}
        protected virtual void OnBecameVisible() {}
        protected virtual void OnBecameInvisible() {}
        protected virtual void OnPreRender() {}
        protected virtual void OnRenderObject() {}
        protected virtual void OnPostRender() {}
        protected virtual void OnRenderImage(RenderTexture src, RenderTexture dest) {}
        protected virtual void OnDrawGizmos() {}
        protected virtual void OnGUI() {}
        protected virtual void OnApplicationPause(bool pauseStatus) {}
        protected virtual void OnDisable() {}
        protected virtual void OnDestroy() {}
        protected virtual void OnApplicationQuit() {}
        protected virtual void OnApplicationFocus(bool focusStatus) {}
    }
```

[イベント関数の実行順](https://docs.unity3d.com/jp/460/Manual/ExecutionOrder.html)を参考に列挙した。
これでコードは常に`protected override void XXX(){base.XXX();}`に統一される。
詳しくは知らないが、パフォーマンスはやばそうである。

## 軽減法　コンポーネント指向

そもそも、Enemyを継承して別タイプのEnemyを作る、という手法自体が時代遅れなものだ（たぶんな）。
もっとスマートなやり方として、

- ステータスコンポーネント
- 死亡コンポーネント
- 移動コンポーネント
- 上記のコンポーネントを制御する頭脳コンポーネント(Enemyコンポーネント)

という四つのコンポーネントを作り、SugoiiEnemyというプレファブにつける、という手法がある。
この手法のメリットは、継承では対処が困難な、

1. 歩行エネミー
2. 飛行エネミー
3. 歩行＋飛行エネミー
4. 不死身の飛行エネミー

のような、変則的な実装にもスマートに対応できるところだ。

ただし、各コンポーネントの設計が従来のやり方とは少し異なるため、慣れるまでは面倒に思うかもしれない。
責務とか責任とかをクラスごとにきっちり分けたい、イカしたエンジニアはこうしているはずだ。

このやり方であれば、少なくとも今回のような単純な場合においては、三つ子問題は起きない。
コンポーネントを継承して新しいコンポーネントを実装するときには起きるが、今までのやり方ほど影響範囲は大きくはないだろう。
しかし起きる可能性がある以上、問題は解決したとは言えない。

## たせけて

本当はイカした方法があって、私が知らないだけだということを切に願う。
偉い人、愚かな私をたせけてください。


P.S. `base.XXX();`書くのしんどいです、自動で最初に呼び出してくれるイケメン機能はないですか？