関数を呼び出すと戻り値を返す前に実行完了されます。すなわち関数で行なわれるアクションはひとつのフレームで行なわれるということを意味し,関数呼び出しをはプロシージャル アニメーションを含めたり,時間の経過を伴う一連のイベントには使用できません。例えば,オブジェクトのアルファ(透明度)が完全に透明になるまで徐々に減少し続ける場面を想定したとします。
void Fade() {
for (float f = 1f; f >= 0; f -= 0.1f) {
Color c = renderer.material.color;
c.a = f;
renderer.material.color = c;
}
}
上記のままでは, Fade 関数が望むような効果が得られません。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 関数で行なうことです。しかしこの関数は一秒間に何度も呼び出しされます。タスクがそれほど頻繁に繰り返す必要がない場合,コルーチンに入れることで毎フレーム実行することなく定期的に更新することができます。ひとつの例は敵が近くにいることをプレイヤーに知らせるアラームです。コードは次のようなものです:
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);
}
}
これによりゲームに悪影響を与えずにチェック回数を飛躍的に減少させられます。