関数を呼び出すと、値を返す前に実行が完了します。これは、関数で行なわれるすべてのアクションは 1 つのフレーム内で行われるということを意味します。つまり、関数呼び出しにプロシージャルアニメーションを含んだり、時間経過が必要なイベントに使用することはできません。例として、オブジェクトのアルファ (透明度) を完全に見えなくなるまでなるまで徐々に減少させるタスクを考えてみましょう。
void Fade()
{
Color c = renderer.material.color;
for (float alpha = 1f; alpha >= 0; alpha -= 0.1f)
{
c.a = alpha;
renderer.material.color = c;
}
}
上記のままでは、Fade 関数に期待通りの効果を得られません。フェードが視覚的に分かるようにするためには、アルファが連続したフレームに渡って減少し、中間的な値がレンダリングされることが必要です。しかし、関数は 1 つのフレーム内で完全に実行され完了します。中間値は決して表示されず、オブジェクトは即時に透明になります。
このような状況を Update 関数にコードを追加して、フレームごとにフェードするように処理することも可能です。しかし、通常、このようなタスクではコルーチンを使用するとより便利です。
コルーチンとは、実行を停止して Unity へ制御を戻し、その次のフレームで停止したところから続行することができる関数です。C# では、コルーチンを以下のように宣言します。
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 が戻り値の型で、yield return ステートメントを内部に持つことを宣言した関数です。yield return の行が実行を停止して次のフレームから実行を継続する位置です。コルーチンを実行するには StartCoroutine 関数を使用します。
void Update()
{
if (Input.GetKeyDown("f"))
{
StartCoroutine(Fade());
}
}
Fade 関数のループカウンターは、コルーチンの生存期間を通して正しい値を保持します。実際、yield が発生しても、変数やパラメーターは正しく保持されます。
デフォルトでは、コルーチンは yield した直後のフレームで再開しますが、WaitForSeconds 関数を使用して、時間的に遅延させて再開することもできます。
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);
}
}
この方法はエフェクトを一定の時間遅延させるために使用できますが、最適化の方法としても便利です。ゲーム中のタスクの多くは定期的に行なう必要があります。もっとも分かりやすい方法としては Update 関数にそれらを加えてしまうことです。ただし、この関数は、通常 1 秒間に何度も呼び出されます。タスクがそれほど頻繁に繰り返す必要がない場合は、コルーチンに入れることで毎フレーム実行することなく定期的に更新することができます。例えば、敵が近くにいることをプレイヤーに知らせるアラームなどは良い例です。コード例は以下の通りです。
bool 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(;;)
{
if (ProximityCheck())
{
// Perform some action here
}
yield return new WaitForSeconds(.1f);
}
}
このようにすると、ゲームにオーバーヘッドを与えずにチェック回数を大幅に削減できます。
Note: You can stop a Coroutine with StopCoroutine and StopAllCoroutines.
The coroutine also stops when the GameObject it is attached to is disabled with SetActive(false). Take note that the coroutine does not continue if the GameObject is re-enabled. Calling Destroy(example)
(where example
is a MonoBehaviour instance) immediately triggers OnDisable
and the coroutine is processed, effectively stopping it. Finally, OnDestroy
is invoked at the end of the frame.
MonoBehaviour インスタンスで enabled を false に設定して MonoBehaviour を無効にしても、コルーチンは 停止しません。