Version: 2017.1
ゲームオブジェクトの作成および削除
名前空間

コルーチン

関数を呼び出すと戻り値を返す前に実行完了されます。すなわち関数で行なわれるアクションはひとつのフレームで行なわれるということを意味し、関数呼び出しはプロシージャルアニメーションを含めたり、時間の経過を伴う一連のイベントには使用できません。例えば、オブジェクトのアルファ(透明度)が完全に透明になるまで徐々に減少し続ける場面を想定したとします。

void Fade() {
    for (float f = 1f; f >= 0; f -= 0.1f) {
        Color c = renderer.material.color;
        c.a = f;
        renderer.material.color = c;
    }
}

上記のままでは、Fade 関数が望むような効果が得られません。Fade が視覚的に分かるようにするためには、アルファは数フレームに渡り、レンダリングされる中間的な値に変化したうえで、減少していく必要があります。しかし、上記の関数は一つのフレームでのみ実行されます。中間的な値は視覚的に分からず、オブジェクトは瞬間的に透明になります。

Update 関数にコードを追加することでも、フレームごとにフェードすることは可能です。しかし、このようなタスクでは通常コルーチンを使用すると便利です。

コルーチンとは実行を停止して Unity へ制御を戻し、ただし続行するときは停止したところから次のフレームで実行を継続することができる関数です。C# でコルーチンは次のように宣言します:

IEnumerator Fade() {
    for (float f = 1f; f >= 0; f -= 0.1f) {
        Color c = renderer.material.color;
        c.a = f;
        renderer.material.color = c;
        yield return null;
    }
}

要するに、これは IEnumerator 型を戻り値として yield return ステートメントをボディ部分に含める、という関数の宣言です。yield return の行が実行を停止して次のフレームから実行を継続する場所です。コルーチンを実行するには StartCoroutine 関数を使用します:

void Update() {
    if (Input.GetKeyDown("f")) {
        StartCoroutine("Fade");
    }
}

UnityScript ではもう少し簡単です。yield ステートメントを含む関数はすべてコルーチンとして解釈され IEnumerator 戻り型は明示的に宣言する必要がありません:

function Fade() {
    for (var f = 1.0; f >= 0; f -= 0.1) {
        var c = renderer.material.color;
        c.a = f;
        renderer.material.color = c;
        yield;
    }
}

さらに UnityScript でコルーチンを開始する場合、通常の関数と同様に呼び出しします:

function Update() {
    if (Input.GetKeyDown("f")) {
        Fade();
    }
}

Fade 関数のループカウンターはコルーチンのライフサイクルを通じて正しい値を保持します。yield の最中に任意の変数またはパラメーターは正しく保持されます。

デフォルトでコルーチンは yield した直後のフレームで再開しますが、遅延してから再開させるには WaitForSeconds 関数を使用します:

IEnumerator Fade() {
    for (float f = 1f; f >= 0; f -= 0.1f) {
        Color c = renderer.material.color;
        c.a = f;
        renderer.material.color = c;
        yield return new WaitForSeconds(.1f);
    }
}

UnityScript では

function Fade() {
    for (var f = 1.0; f >= 0; f -= 0.1) {
        var c = renderer.material.color;
        c.a = f;
        renderer.material.color = c;
        yield WaitForSeconds(0.1);
    }
}

この方法でエフェクトを一定の時間範囲で広げられますが、最適化の方法としても便利です。ゲーム中のタスクは定期的に行なう必要があり、単純な方法としては Update 関数になります。しかしこの関数は 1 秒間に何度も呼び出されます。タスクがそれほど頻繁に繰り返す必要がない場合、コルーチンに入れることで毎フレーム実行することなく定期的に更新することができます。ひとつの例としては、敵が近くにいることをプレイヤーに知らせるアラームです。コードは次のようなものです:

function ProximityCheck() {
    for (int i = 0; i < enemies.Length; i++) {
        if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance) {
                return true;
        }
    }
    
    return false;
}

敵が数多くいる場合にこの関数を毎フレーム呼び出しすることで顕著なオーバーヘッドを生じさせるかもしれません。しかし、コルーチンを使用して 1/10 秒おきに呼び出すことができます:

IEnumerator DoCheck() {
    for(;;) {
        ProximityCheck;
        yield return new WaitForSeconds(.1f);
    }
}

これによりゲームに悪影響を与えずにチェック回数を飛躍的に減少させられます。

Note: Coroutines are not stopped when a MonoBehaviour is disabled, but only when it is definitely destroyed. You can stop a Coroutine using MonoBehaviour.StopCoroutine and MonoBehaviour.StopAllCoroutines. Coroutines are also stopped when the MonoBehaviour is destroyed.

ゲームオブジェクトの作成および削除
名前空間