関数を呼び出すと戻り値を返す前に実行完了されます。すなわち関数で行なわれるアクションはひとつのフレームで行なわれるということを意味し、関数呼び出しはプロシージャルアニメーションを含めたり、時間の経過を伴う一連のイベントには使用できません。例えば、オブジェクトのアルファ(透明度)が徐々に減少し続けて完全に透明になるまで場面を想定したとします。
void Fade() {
for (float f = 1f; f >= 0; f -= 0.1f) {
Color c = renderer.material.color;
c.a = f;
renderer.material.color = c;
}
}
上記のままでは、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);
}
}
このようにすると、ゲームにオーバーヘッドを与えずにチェック回数を大幅に削減できます。
注意: MonoBehaviour が無効の場合でも、MonoBehaviour が完全に破棄されない限りは、コルーチンは停止しません。MonoBehaviour.StopCoroutine と MonoBehaviour.StopAllCoroutines を使ってコルーチンを停止することができます。また、MonoBehaviour が破棄されると、コルーチンも停止します。