関数を呼び出すと、値を返す前に実行が完了します。これは、関数で行なわれるすべてのアクションは 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 関数に期待通りの効果を得られません。フェードが視覚的に分かるようにするためには、アルファが連続したフレームに渡って減少し、中間的な値がレンダリングされることが必要です。しかし、関数は 1 つのフレーム内で完全に実行され完了します。中間値は決して表示されず、オブジェクトは即時に透明になります。
このような状況を 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");
}
}
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);
}
}
この方法はエフェクトを一定の時間遅延させるために使用できますが、最適化の方法としても便利です。ゲーム中のタスクの多くは定期的に行なう必要があります。もっとも分かりやすい方法としては 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);
}
}
このようにすると、ゲームにオーバーヘッドを与えずにチェック回数を大幅に削減できます。
注意: MonoBehaviour が無効の場合でも、MonoBehaviour が完全に破棄されない限りは、コルーチンは停止しません。MonoBehaviour.StopCoroutine と MonoBehaviour.StopAllCoroutines を使ってコルーチンを停止することができます。また、MonoBehaviour が破棄されるとコルーチンも停止します。