関数を呼び出すと、値を返す前に実行が完了します。これは、関数で行なわれるすべてのアクションは 1 つのフレーム内で行われるということを意味します。つまり、関数呼び出しにプロシージャルアニメーションを含んだり、時間経過が必要なイベントに使用することはできません。例として、オブジェクトのアルファ (透明度) を完全に見えなくなるまでなるまで徐々に減少させるタスクを考えてみましょう。
void Fade()
{
for (float ft = 1f; ft >= 0; ft -= 0.1f)
{
Color c = renderer.material.color;
c.a = ft;
renderer.material.color = c;
}
}
上記のままでは、Fade 関数に期待通りの効果を得られません。フェードが視覚的に分かるようにするためには、アルファが連続したフレームに渡って減少し、中間的な値がレンダリングされることが必要です。しかし、関数は 1 つのフレーム内で完全に実行され完了します。中間値は決して表示されず、オブジェクトは即時に透明になります。
このような状況を Update 関数にコードを追加して、フレームごとにフェードするように処理することも可能です。しかし、通常、このようなタスクではコルーチンを使用するとより便利です。
コルーチンとは、実行を停止して Unity へ制御を戻し、その次のフレームで停止したところから続行することができる関数です。C# では、コルーチンを以下のように宣言します。
IEnumerator Fade()
{
for (float ft = 1f; ft >= 0; ft -= 0.1f)
{
Color c = renderer.material.color;
c.a = ft;
renderer.material.color = c;
yield return null;
}
}
It is essentially a function declared with a return type of IEnumerator and with the yield return statement included somewhere in the body. The yield return null line is the point at which execution will pause and be resumed the following frame. To set a coroutine running, you need to use the StartCoroutine function:
void Update()
{
if (Input.GetKeyDown("f"))
{
StartCoroutine("Fade");
}
}
Fade 関数のループカウンターは、コルーチンの生存期間を通して正しい値を保持します。実際、yield が発生しても、変数やパラメーターは正しく保持されます。
デフォルトでは、コルーチンは yield した直後のフレームで再開しますが、WaitForSeconds 関数を使用して、時間的に遅延させて再開することもできます。
IEnumerator Fade()
{
for (float ft = 1f; ft >= 0; ft -= 0.1f)
{
Color c = renderer.material.color;
c.a = ft;
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);
}
}
このようにすると、ゲームにオーバーヘッドを与えずにチェック回数を大幅に削減できます。
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 or when the GameObject it is attached to is disabled.