コルーチンは便利ですが、スクリプトをアタッチしているGameObject や 自身のComponentがDisabledになったときの挙動が少し厄介です。
(1)Active all | (2)Disable GameObject | (3)Disable Component | (4)Disable all |
---|---|---|---|
動き続ける | 終了する | 動き続ける | 終了する |
(1)通常時は問題ないのですが、(2)いったんgameObject.SetActive(false)
したのち、再度gameObject.SetActive(true)
すると、動いていたコルーチンは消えてしまいます。
また、(3)自分自身をthis.enabled = false
してもコルーチンは動き続けてしまいます。
そこで、
(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]");
}
}
}