本記事はCraft Egg Advent Calendar 2021の12/19の記事です。(投稿が遅れ申し訳ありません)
12/18の記事は@arumaniさんの「iPhoneで3D空間に入って撮影できる『Unity Virtual Camera』」でした。
はじめに
株式会社Craft EggでUnityクライアントエンジニアをしている鈴木です。
UnityにおけるCoroutineのよく「どうだったっけ?」ってなるつまづきやすいところをまとめました。
基本的な項目が多めになっていますが、非同期処理ということもあり、意外と直面した時に気づくことができないこともありますので、Coroutine周りでつまづいた時にチェックするリスト代わりに使えるものになればと思います。
Coroutineのつなぎ方
- 内部でStartCoroutineする必要はない
- yield returnでつなぐと内部で即時breakしていても1フレーム経過する
- movenextでつなぐとフレーム経過しない
yield returnとmovenext検証
void Hoge()
{
Debug.Log($"メインコルーチン開始 : {Time.frameCount}");
//任意のコルーチンを実行
//StartCoroutine(MoveNextCoroutine());
}
private IEnumerator YieldReturnCoroutine()
{
Debug.Log($"YieldReturnCoroutine開始 : {Time.frameCount}");
yield return SubCoroutine();
Debug.Log($"YieldReturnCoroutine終了 : {Time.frameCount}");
}
private IEnumerator MoveNextCoroutine()
{
Debug.Log($"MoveNextCoroutine開始 : {Time.frameCount}");
IEnumerator coroutine = SubCoroutine();
while(coroutine.MoveNext()) { yield return coroutine.Current; }
Debug.Log($"MoveNextCoroutine終了 : {Time.frameCount}");
}
private IEnumerator SubCoroutine()
{
Debug.Log($"SubCoroutine開始 : {Time.frameCount}");
Debug.Log($"SubCoroutine終了 : {Time.frameCount}");
yield break;
}
YieldReturnCoroutine
メインコルーチン開始 : 1
YieldReturnCoroutine開始 : 1
SubCoroutine開始 : 1
SubCoroutine終了 : 1
YieldReturnCoroutine終了 : 2
MoveNextCoroutine
メインコルーチン開始 : 1
MoveNextCoroutine開始 : 1
SubCoroutine開始 : 1
SubCoroutine終了 : 1
MoveNextCoroutine終了 : 1
Coroutineの止め方
- StopCoroutineだけだと破棄されない
- 一度null埋めをすることで破棄される
- null埋めしなかった場合止めた部分から再開される
- 開始したMonoBehaviorからしか止められない
- GameObjectが非アクティブになったタイミングでCoroutineも止まる
- 非アクティブなGamerObjectでは開始もできない
- startしたときと同じ記法でないと止めることができない
Coroutineのnull埋め検証
private IEnumerator mainCoroutine;
void Start ()
{
Debug.Log($"メインコルーチン開始");
mainCoroutine = MainCoroutine();
StartCoroutine(mainCoroutine);
// 任意のサブコルーチンを実行
StartCoroutine(SubCoroutineB());
}
private IEnumerator MainCoroutine()
{
Debug.Log("MainCoroutine 1");
yield return null;
Debug.Log("MainCoroutine 2");
yield return null;
Debug.Log("MainCoroutine 3");
}
private IEnumerator SubCoroutineA()
{
yield return null;
StopCoroutine(mainCoroutine);
Debug.Log("Stop MainCoroutine");
yield return mainCoroutine;
}
private IEnumerator SubCoroutineB()
{
yield return null;
StopCoroutine(mainCoroutine);
mainCoroutine = null;
Debug.Log("Stop MainCoroutine");
mainCoroutine = MainCoroutine();
yield return mainCoroutine;
}
SubCoroutineA
メインコルーチン開始
MainCoroutine 1
MainCoroutine 2
Stop MainCoroutine
MainCoroutine 3
SubCoroutineB
メインコルーチン開始
MainCoroutine 1
MainCoroutine 2
Stop MainCoroutine
MainCoroutine 1
MainCoroutine 2
MainCoroutine 3
Coroutineの実行者の検証
private IEnumerator mainCoroutine;
private MonoBehaviour cachedCoroutineOwner;
void CoroutineStart(MonoBehaviour coroutineOwner)
{
Debug.Log($"メインコルーチン開始");
mainCoroutine = MainCoroutine();
coroutineOwner.StartCoroutine(mainCoroutine);
// 任意のサブコルーチンを実行
StartCoroutine(SubCoroutineB());
cachedCoroutineOwner = coroutineOwner;
}
private IEnumerator MainCoroutine()
{
Debug.Log("MainCoroutine 1");
yield return null;
Debug.Log("MainCoroutine 2");
yield return null;
Debug.Log("MainCoroutine 3");
}
private IEnumerator SubCoroutineA()
{
yield return null;
StopCoroutine(mainCoroutine);
Debug.Log("Stop MainCoroutine");
}
private IEnumerator SubCoroutineB()
{
yield return null;
cachedCoroutineOwner.StopCoroutine(mainCoroutine);
Debug.Log("Stop MainCoroutine");
}
SubCoroutineA
MainCoroutine 1
MainCoroutine 2
Stop MainCoroutine
MainCoroutine 3
SubCoroutineB
MainCoroutine 1
MainCoroutine 2
Stop MainCoroutine
その他
- Coroutineから戻り値を受け取ることもできる
- 明示的に型変換する必要あり
- StopAllCoroutineでMonoBehaviorが保持しているCoroutineをすべて止めることができる
- StartをIEnumratorにしていたらそれも止まるので注意
- DoTweeenの終わりを待つ方法
Coroutineから戻り値を受け取る
private void CoroutineStart()
{
StartCoroutine(MainCoroutine());
}
private IEnumerator MainCoroutine()
{
IEnumerator subCoroutine = SubCoroutine();
yield return subCoroutine;
Debug.Log($"subCoroutine.Current : {(string)subCoroutine.Current}");
}
private IEnumerator SubCoroutine()
{
yield return "SubCoroutine is End";
}
subCoroutine.Current : SubCoroutine is End
参考記事
終わりに
今回はCoroutineのつまづきやすいところをまとめてみました。最近は非同期処理でCoroutineを採用することは少ないですが、ちょっとした個人開発などではまだまだ現役かと思いますので参考になれば幸いです。