Disable/Enable時のCoroutineを制御する

コルーチンは便利ですが、スクリプトをアタッチしているGameObject や 自身のComponentがDisabledになったときの挙動が少し厄介です。

| (1)Active all | (2)Disable GameObject | (3)Disable Component |(4)Disable all|
|:-:|:-:|:-:|:-:|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/08f9296d-571d-4bd4-bf13-5d269e897f8d.png) |![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/b1895a61-f561-ca75-cd04-5f35498f2d1d.png) |![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/e9766c64-19d1-1eca-3b73-a233d7de6dbc.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/caa027a9-64f2-88d6-cc41-0c921620e7df.png)|
| 動き続ける | 終了する | 動き続ける | 終了する |

(1)通常時は問題ないのですが、(2)いったん```gameObject.SetActive(false)```したのち、再度```gameObject.SetActive(true)```すると、動いていたコルーチンは消えてしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/e5c3f55e-c3d0-1d9c-1d88-d8cd1e0520e1.png)


また、(3)自分自身を```this.enabled = false```してもコルーチンは動き続けてしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/63cf76c2-4019-3e44-a167-7fd2885b0887.png)


そこで、
(2)の場合は再度```StartCoroutine(TestCo())```、
(3)の場合は```WaitUntil(() => this.isActiveAndEnabled)```で待つようにしてみます。


```
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace Elix
{
    public class CoroutineCheck : MonoBehaviour
    {
        Coroutine m_co = null;
        // Start is called before the first frame update
        void Start()
        {
            Debug.Log("Start");
        }

        // Update is called once per frame
        void Update()
        {
        }

        IEnumerator TestCo()
        {
            int m_count = 0;
            while (true)
            {
                yield return new WaitUntil(() => this.isActiveAndEnabled);
                Debug.Log($"TestCo: {m_count}");
                m_count++;
                yield return new WaitForSeconds(1f);
            }
        }

        private void OnDisable()
        {
            if(gameObject.activeInHierarchy == false)
            {
                Debug.Log("<OnDisable(GameObject)>");
                if (m_co != null)
                {
                    // All coroutines will stop when the GameObject is disabled.
                    m_co = null;
                }
            }
            else
            {
                // Coroutines are alive when the Comprmemt is disabled.
                Debug.Log("<OnDisable(Component)>");
            }
        }
        private void OnEnable()
        {
            Debug.Log("OnEnable");
            if (m_co == null)
            {
                m_co = StartCoroutine(TestCo());
            }
        }
        private void OnDestroy()
        {
            Debug.Log("[OnDestroy]");
        }
    }
}
```

一見うまく動作するように見えますが、
(1)->(3)->(4)->(2) のように遷移した場合に動作しなくなります。
結局のところ、```OnDisable()```,```OnEnable()```で毎回コルーチンを初期化するのが正解のようです。

```
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace Elix
{
    public class CoroutineCheck : MonoBehaviour
    {
        [SerializeField] bool m_isResettable = true;
        [SerializeField] TMPro.TextMeshProUGUI m_text = null;
        Coroutine m_co = null;

        // Start is called before the first frame update
        void Start()
        {
            Debug.Log("Start");
            if(!m_isResettable)
            {
                m_co = StartCoroutine(TestCo());
            }
        }

        // Update is called once per frame
        void Update()
        {
        }

        IEnumerator TestCo()
        {
            int count = 0;
            while (true)
            {
                yield return new WaitUntil(() => this.isActiveAndEnabled);
                //Debug.Log($"TestCo: {m_count}");
                m_text.text = $"{count}";
                count++;
                yield return new WaitForSeconds(1f);
            }
        }

        private void OnDisable()
        {
            if (m_isResettable)
            {
                Debug.Log("OnDisable");
                if (m_co != null)
                {
                    StopCoroutine(m_co);
                    m_co = null;
                }
            }
        }
        private void OnEnable()
        {
            if (m_isResettable)
            {
                Debug.Log("OnEnable");
                if (m_co == null)
                {
                    m_co = StartCoroutine(TestCo());
                }
            }
        }
        private void OnDestroy()
        {
            Debug.Log("[OnDestroy]");
        }
    }
}
```

UniTaskの制御については[こちら](https://qiita.com/ELIXIR/items/e3e2fb374fdf447b75aa)
Awaitableの制御については[こちら](https://qiita.com/ELIXIR/items/70b1eb7a068853d9b408)