自習室

こもります

Unity のコルーチン Coroutine についてメモ

参考にしたサイト様

コルーチンの目的

大きく三つあるかと思った

  1. ロジックの整理
    主要な処理から分岐した1セットの「ここからここまで」の処理を、主要な処理の記述や定常的に行われる処理の記述を浸食しないで別の箇所に書くことが出来る。

  2. 処理の分割
    coroutine処理内での yield return までの1ブロックは主要なUpdate 1回と同期して行われるので、coroutine内での1回の処理量をyield return のブロックで適切に分割することで、主要な処理を止めること無く実行することが出来る

  3. 処理の待機
    coroutine処理は、他に影響なく単独で処理を止めることが出来る。 yeild return WaitForSeconds(3f);

公式チュートリアルでやっていること

チュートリアルの前半では

  1. 起動時にロボットがある目的位置まで移動し(数秒かかる)
  2. 上記を達成したら一度メッセージを発し
  3. メインの描画等処理を止めずに3秒待って、再度メッセージを発する

という一連の処理をコルーチン処理として定義し、それを起動時に一度 StartCoroutine(MyCoroutine(target)) と呼び出すことで、以後、主要な処理側は何も関知せず、描画処理を邪魔することも無く、処理を完遂させている。 上記の目的3つが活かされています。

チュートリアルの後半では

「ロボットが目標位置まで移動する」をコルーチンとして定義する。

このコルーチンは、床へのマウスクリックによってStartされる。また、一度スタートされたコルーチンが終了する前に、もう一度マウスクリックが行われた場合は、StopCoroutine("Movement") とすることで、現在進行中のコルーチンを中断して、新たに StartCoroutine("Movement") でコルーチンをスタートし直している。

こういったいつ起こるか・終了するかわからない処理をどこかのメインループ内に記述しようとすると、if文とフラグだらけの複雑な記述になりがちだが、処理の一セットをまとめて書き出せるのは、非常に便利。

コルーチンを実装する上でのメモ

以下にコルーチンを実装する上で気をつけるべきことをメモしておきます。

コルーチンで実行される関数の定義

Coroutineとして実行する関数は、 IEnumerator を返値として定義する

IEnumerator MyCoroutine () {
 // 処理内容
}

コルーチンのスタートの仕方

StartCoroutine("MyCoroutine") でMyCoroutineがスタートされる

StartCoroutine(MyCoroutine()); でもスタート可能だが、この場合、StopCoroutine("MyCoroutine")としても、先ほどスタートしたコルーチンは停止できない

ブロックごとにUpdateと同期して処理される

Coroutine内の yeild return null; までを一つのブロックとし、そこまでを1回のUpdateタイミングで処理する。次のUpdateタイミングで、続きが継続的に実行される。

上記の公式チュートリアルでは、主要なUpdate(つまり描画のフレーム)と同期してロボットを目的位置まで動かすために、以下の様なコードを書いている

IEnumerator Movement (Vector3 target)
{
    while(Vector3.Distance(transform.position, target) > 0.05f)
    {
        transform.position = Vector3.Lerp(transform.position, target, smoothing * Time.deltaTime);         
        yield return null; // 
    }
}

1回のUpdateのタイミングごとに Vector3.Lerp()が行われ、このスクリプトがアタッチされているロボットが目的位置に近づく。

また、十分に近づいたら(距離が0.05fより短くなったら)while文を抜け、Coroutine処理全体が終了する。

コルーチンの実行中「終了」

途中で終了するには、StartCoroutine("メソッド名") の形式でスタートさせる必要がある。

StartCoroutine("MyCoroutine", var);
// したものを
StopCoroutine("MyCoroutine");

公式チュートリアルでは、「クリック位置まで移動する」コルーチンが完了する前に新たに「クリック位置まで移動する」コルーチンが開始されようとした際に、一個前の途中のコルーチンを終了させる、ことをしています。

public Vector3 Target
{
    get{ return target; }
    set  // 新たにTargetが設定されたら
    {
        target = value;
           
        StopCoroutine("Movement");  // この時点で今の目的地は破棄して
        StartCoroutine("Movement", target);  // 新しいターゲットに向かって走ってくれ!
    }
}

処理の待機

yield return WaitForSeconds(3f); はあくまで「自分自身がupdateのタイミングで実行されることを3秒止める、なので、主要な処理を止めるわけではない→便利!

最後に

ガンガン使っていきたいと思います