# フレーム間でのタスクの分割

> Write and run coroutine methods to do work that takes effect over several frames, such as a gradual fade-out effect.

コルーチンを使用すると、タスクを複数のフレームに分散させることができます。コルーチンは、実行を一時停止して制御を Unity に返した後、以下のフレームで中断したところから続行することができるメソッドです。

ほとんどの場合、メソッドを呼び出すと、メソッドは完了するまで実行され、呼び出し元のメソッドに制御を返し、オプションの戻り値を返します。つまり、メソッド内で行われるすべてのアクションは、1 回のフレーム更新中に行われる必要があります。

プロシージャルのアニメーションや経時的なイベントのシーケンスを格納するためにメソッド呼び出しを使用したい場合は、コルーチンを使用することができます。

**ノート**: **コルーチンはスレッドではない**ことを覚えておいてください。コルーチンの中で実行される同期操作は、メインスレッドでも実行されます。メインスレッドに費やされる CPU 時間を短縮したい場合は、他のスクリプトコードと同様にコルーチン内で操作をブロックしないようにすることが重要です。Unity でマルチスレッドコードを使用する場合のオプションは以下のとおりです。

* [C# Job System](/engine/6000.0/manual/scripting/optimization/job-system.md)
* .NET の[非同期および待機](/engine/6000.0/manual/scripting/optimization/async-await-support.md)と Unity のカスタム `Awaitable` サポート

HTTP 転送の待機、アセットのロードの待機、ファイル出入力の完了待ちなど、長い非同期処理を行う必要がある場合は、コルーチンの使用が最適です。

## コルーチンの例

例として、オブジェクトのアルファ (不透明度) 値を見えなくなるまで徐々に低下させるタスクについて考えてみます。

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

この例では、Fade メソッドで期待したとおりの効果が得られない可能性があります。フェードが見えるようにするには、一連のフレームでフェードのアルファを低下させることで、Unity がレンダリングする中間値を表示する必要があります。ただし、このメソッド例は、1 回のフレーム更新中に全体的に実行されます。中間値は表示されることはなく、オブジェクトはすぐに消えます。

この状況を回避するには、フレーム単位でフェードを実行するコードを `Update` 関数に追加します。ただし、このようなタスクにはコルーチンを使用する方が便利になることがあります。

C# では、以下のようにコルーチンを宣言します。

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

コルーチンは、[IEnumerator](https://docs.microsoft.com/en-us/dotnet/api/system.collections.ienumerator) 戻り値の型で宣言し、ボディのどこかに [yield](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield) return ステートメントを含んでいるメソッドです。`yield return null` の行は、実行が一時停止し、以下のフレームで再開されるポイントです。コルーチンの実行を設定するには、[StartCoroutine](/engine/6000.0/script-reference/unity-engine/mono-behaviour/start-coroutine.md) 関数を使用する必要があります。

```lang-csharp
void Update()
{
    if (Input.GetKeyDown("f"))
    {
        StartCoroutine(Fade());
    }
}
```

`Fade` 関数内のループカウンターは、コルーチンの生存期間を通して正しい値を保持します。すべての変数やパラメーターは、`yield` ステートメントの間で保持されます。

## コルーチンの時間遅延

デフォルトでは、Unity は `yield` ステートメントの後のフレームでコルーチンを再開します。時間遅延を発生させる場合は、[WaitForSeconds](/engine/6000.0/script-reference/unity-engine/wait-for-seconds.md) を使用します。

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

`WaitForSeconds` を使用して、一定期間にわたって効果を広げることができます。また、`Update` メソッドにタスクを入れる代わりに WaitForSeconds を使用することができます。Unity は `Update` メソッドを 1 秒間に数回呼び出すため、タスクをそれほど頻繁に繰り返す必要がない場合は、このメソッドをコルーチンに入れることで、フレームごとではなく定期的に更新されるようにすることができます。

例えば、敵が近くにいる場合にプレイヤーに警告するアラームが、以下のようなコードでアプリケーションに記載されているとします。

```lang-csharp
bool ProximityCheck()
{
    for (int i = 0; i < enemies.Length; i++)
    {
        if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance) {
                return true;
        }
    }

    return false;
}
```

敵が数多く存在する場合は、すべてのフレームでこの関数を呼び出すと著しいオーバーヘッドが生じる可能性があります。しかし、コルーチンを使用すると 0.1 秒ごとに呼び出すことができます。

```lang-csharp
IEnumerator DoCheck()
{
    for(;;)
    {
        if (ProximityCheck())
        {
            // Perform some action here
        }
        yield return new WaitForSeconds(.1f);
    }
}
```

このようにすると、ゲームプレイに顕著な影響を与えずに、Unity がチェックを行う回数を減らすことができます。

## コルーチンの停止

コルーチンを停止するには、[StopCoroutine](/engine/6000.0/script-reference/unity-engine/mono-behaviour/stop-coroutine.md) と [StopAllCoroutines](/engine/6000.0/script-reference/unity-engine/mono-behaviour/stop-all-coroutines.md) を使用します。[SetActive](/engine/6000.0/script-reference/unity-engine/game-object/set-active.md) を `false` に設定して、コルーチンがアタッチされているゲームオブジェクトを無効にした場合も、コルーチンが停止します。`Destroy(example)` (`example` は `MonoBehaviour` インスタンス) を呼び出すと、[OnDisable](/engine/6000.0/script-reference/unity-engine/mono-behaviour/on-disable.md) がすぐにトリガーされ、Unity がコルーチンを処理し、実質的にコルーチンを停止します。最後に `OnDestroy` がフレームの最後に呼び出されます。

**注意**[enabled](/engine/6000.0/script-reference/unity-engine/behaviour/enabled.md) を `false` に設定することで `MonoBehaviour` を無効にした場合、Unity はコルーチンを停止しません。

## コルーチンの解析

コルーチンは、他のスクリプトコードとは異なる方法で実行されます。Unity のほとんどのスクリプトコードは、パフォーマンストレース内で、特定のコールバック呼び出しの下の 1 箇所にのみ出現します。ただし、コルーチンの CPU コードはトレース内で常に 2 箇所に出現します。

コルーチン内の初期コードのすべて (コルーチンメソッドの開始から最初の `yield` ステートメントまで) は、Unity がコルーチンを開始したときにトレース内に出現します。初期コードはほとんどの場合、[StartCoroutine](/engine/6000.0/script-reference/unity-engine/mono-behaviour/start-coroutine.md) メソッドが呼び出されるときに出現します。Unity コールバックが生成するコルーチン (`IEnumerator` を返す `Start` コールバックなど) は、それぞれの Unity コールバック内で最初に出現します。

コルーチンのその他のコード (初回の再開時点から実行完了まで) は、Unity のメインループ内の `DelayedCallManager` ラインに出現します。

これは、Unity がコルーチンを実行する方法が原因です。[C# コンパイラー](/engine/6000.0/manual/scripting/environment-and-tools/overview-of-dot-net-in-unity.md)は、コルーチンを戻すクラスのインスタンスを自動生成します。Unity はこのオブジェクトを使用して、1 つのメソッドの複数の呼び出しでコルーチンの状態を追跡します。コルーチン内のローカルスコープ変数は `yield` の複数回の呼び出しにわたって存続する必要があるため、Unity はローカルスコープ変数を生成されたクラスに巻き上げ、コルーチン中はヒープに割り当てられたままになります。また、このオブジェクトはコルーチンの内部状態を追跡し、yield の後にコードのどの時点でコルーチンを再開しなければならないかを記憶しています。

このため、コルーチンの開始時にかかるメモリ負荷は、固定のオーバーヘッド割り当てに、そのローカルスコープ変数のサイズを足したものに等しくなります。

コルーチンを開始させるコードがオブジェクトの構築と呼び出しを行い、その後にこのコルーチンの `yield` 条件が満たされた時点で、 Unity の `DelayedCallManager` がもう一度呼び出します。コルーチンは通常、他のコルーチンの外部で開始されるため、実行のオーバーヘッドは `yield` 呼び出しと `DelayedCallManager` に分割されます。

Unity プロファイラーを使用すると、Unity がアプリケーションのどこでコルーチンを実行するかを調査し、理解することができます。これを行うには、[Deep Profiling](/engine/6000.0/manual/analysis/profiler/window.md#deep-profiling) を有効にしてアプリケーションをプロファイルします。これにより、スクリプトコードのすべての部分がプロファイルされ、すべての関数呼び出しが記録されます。その後、[CPU Usage Profiler モジュール](/engine/6000.0/manual/analysis/profiler/collect-data/visualizing-data/cpu/cpu.md)を使用して、アプリケーションのコルーチンを調査できます。


**DelayedCall におけるコルーチンのプロファイラーセッション:**
![](/api/media?file=/engine/6000.0/media/images/coroutines-cpu-profiler.png)

一連の操作は、できるだけ少ないコルーチンにまとめることをお勧めします。ネストしたコルーチンは、コードの明瞭化やメンテナンスの面では大変優れていますが、コルーチンがオブジェクトを追跡するため、メモリオーバーヘッドが大きくなります。

すべてのフレームで実行され、実行時間が長い操作で `yield` しないコルーチンがある場合は、`Update` または `LateUpdate` のコールバックに置き換える方がパフォーマンスが向上します。これは、実行時間が長いコルーチンや無限にループするコルーチンでは特に有用です。

## 追加リソース

* [コルーチン API リファレンス](/engine/6000.0/script-reference/unity-engine/coroutine.md)
* [MonoBehaviour.StartCoroutine](/engine/6000.0/script-reference/unity-engine/mono-behaviour/start-coroutine.md)
