コルーチンを自前で動かしたい
コルーチンはMonoBehaviour
を継承していればStartCoroutine
で動かすことができます。しかし、MonoBehaviour
を継承していなくても使いたい場面は割と多いです、多分。
MonoBehaviour
なしでコルーチンを動かす方法は調べるとそれなりに出てきますが、そもそもコルーチンで使われているIEnumerator
とはなんぞ?から始めたい所存です。
IEnumeratorインターフェース?
MSDNを見てみるとシンプルに書かれています。
非ジェネリック コレクションに対する単純な反復処理をサポートします。
IEnumerator
インターフェースは反復処理を実現するためのインターフェースになります。
コルーチンはUnityが反復処理をいい感じに実行してくれてる仕組みというわけですね。
反復処理を作って動かしてみる
とりあえず物は試しということでUnityで適当な反復処理を作ってみます。
わかりやすくデバッグログも出してこんな感じに。
public IEnumerator Coroutine() {
Debug.Log("Coroutine 1");
yield return 1;
Debug.Log("Coroutine 2");
yield return 2;
}
こいつをこんな感じで動かしてみます
public void Sample() {
var iEnumerator = Coroutine();
while (iEnumerator.MoveNext()) {
Debug.Log($"Coroutine Return {iEnumerator.Current}");
}
}
Coroutine 1
Coroutine Return 1
Coroutine 2
Coroutine Return 2
おもったよりかんたん!
何をやっているの?
まずは反復処理についてです。何気なく使っているyield
ですが、yield
を含むブロックのことをイテレータブロックと言います。
大雑把に説明すると「1回の処理でここまでやってね」というブロックになります。yield
には値を返すreturn
と処理を抜けるbreak
の2つのキーワードがあります。
次にMoveNext
です。これはイテレータを次に進ませます。イテレータが進んだ場合はtrue
を返すため、while
を使うとイテレータを全て進めることができます。
Current
は反復処理が返した値を保持しているプロパティです。yield return
で返した値がそのまま入ってくる感じですね。
object
型なので色んなものを返せます。
public IEnumerator Coroutine() {
yield return 1;
yield return 9.6;
yield return "ほげえ";
}
Coroutine Return 1
Coroutine Return 9.6
Coroutine Return ほげえ
また、Reset
に関してですが、こちらは自前で実装する必要があります(実装してないとNotSupportedException
がスローされます)。
単純にIEnumerator
インターフェースで実装した反復処理メソッドをやり直したい場合は対象のメソッドを再び取得して動かします。
public void Sample() {
var iEnumerator = Coroutine();
while (iEnumerator.MoveNext()) {
Debug.Log($"Coroutine Return {iEnumerator.Current}");
}
// iEnumerator.Reset(); <- NotSupportedExceptionがスローされる
iEnumerator = Coroutine();
while (iEnumerator.MoveNext()) {
Debug.Log($"Coroutine Return {iEnumerator.Current}");
}
}
思ったよりシンプル!これならStartCoroutine
なしでもなんとかなりそう・・・?
WaitFor関係は更に自前で実装しないとダメ
注意点としてWaitForSecounds
等は単純にyield return
しただけではうまくいかないです。
よくあるのがyield return WaitForSeconds
でカウントをするコルーチンですが、単純にMoveNext
をしただけではできません。
MoveNext
は1フレーム毎にイテレータを進めるというイメージが良いと思います。この辺はStartCoroutine
がいい感じに処理してくれてる部分です。
反復処理の入れ子をやりたい
反復処理から反復処理を実行するような形にすればできます。実はIEnumerator
インターフェースを調べ始めたきっかけでもあります。
public IEnumerator Coroutine() {
Debug.Log("Coroutine Start");
var coroutine2 = Coroutine2();
while (coroutine2.MoveNext()) {
yield return coroutine2.Current;
}
Debug.Log("Coroutine End");
}
public IEnumerator Coroutine2() {
Debug.Log("Coroutine2 1");
yield return 1;
Debug.Log("Coroutine2 2");
yield return 2;
}
public void Sample() {
var iEnumerator = Coroutine();
while (iEnumerator.MoveNext()) {
Debug.Log($"Coroutine Return {iEnumerator.Current}");
}
}
Coroutine Start
Coroutine2 1
Coroutine Return 1
Coroutine2 2
Coroutine Return 2
Coroutine End
おしまい
何気なく使っていたコルーチンとIEnumerator
インターフェースですが、思ったよりシンプルでした。
これでもまだ触りの部分だと思いますが、当初の目的を達成しつつある程度は分かった気がしたのでとりあえずOKとしましょう。
何かあったらコメントください。
参考
++C++; // 未確認飛行 C イテレーター
WisdomSoft 処理の分割(コルーチン)
Qiita:[C#]IEnumeratorとIEnumerableを調べた