Unityのスクリプトではコルーチンを使う事が出来ます。
コルーチンを使うと一定時間毎に何らかの処理を行う事が出来るようになります。
例えば1分毎に新しい敵を登場させるといった事が出来ます。
Update関数内で時間を計測し時間がきたら敵を登場させる事も出来ますが、コルーチンを使った方が簡単に記述が出来ますし、
Update関数内で時間を毎回計測する処理負荷が軽減されるということもあります。
このコルーチンは通常のスクリプトの流れから外れて動作するのでスクリプトの流れとは別に動作させたい処理がある時に便利です。
ただいわゆるマルチスレッド(複数のスクリプトを並列に動作させる)ではなくシングルスレッドで動いているけど並列して動いているようにみせる事が出来るという事です。
コルーチンの概要
前置きが長くなりましたので、さっそくコルーチンの使い方にいきましょう。
コルーチンを開始する
まずはコルーチンを開始するやり方です。
Test関数が実行する関数です。
1 2 3 4 5 6 7 8 9 10 11 12 | // JavaScriptでのコルーチン開始方法 // 通常の関数呼び出しでコルーチンを実行(JavaScriptだと出来る方法) Test(); // StartCoroutineを使用してコルーチンを実行 StartCoroutine(Test()); // 引数は文字列として渡す StartCoroutine("Test"); |
↑のように最初のコルーチン開始は通常の関数呼び出しと同じやり方ですがこちらはJavaScriptでしか出来ません。
2番目はStartCoroutineの引数としてTest関数を渡して実行出来ます。
3番目はStartCoroutineの引数としてメソッド名(呼び名が違いますがJavaScriptだと関数名)を渡します。
2番目と3番目は同じようですが実は違いがあります。
3番目のようにメソッド名を渡して実行した場合StopCoroutineを使って動作を止める事が出来ます。
2番目の場合は処理が自然に終了出来るようにするかStartCoroutineの戻り値を変数に保持しておきStopCoroutineを使うか、StopAllCoroutinesが呼ばれるまで止まりません。
コルーチンを停止する
次はコルーチンの停止方法です。
1 2 3 4 5 6 7 | // StartCoroutineの引数で文字列として渡したものだけ名前で指定したコルーチンを止められる StopCoroutine("Test"); // StopAllCoroutineを呼ぶと全てのコルーチンが停止 StopAllCoroutines(); |
最初のやり方はコルーチンを開始する時に引数にメソッド名を記述した場合に出来る停止方法です。
StartCoroutine(“Test”)
としていた場合だけ
StopCoroutine(“Test”)
が作用します。
それ以外の方法で開始されたコルーチンには適用されず無視されてしまいます。
2番目のStopAllCoroutinesは実行しているコルーチンの関数によらず全てのコルーチンを停止します。
ゲームが終了する時は動作しているコルーチンを全部止めたいと思うので名前を指定せずこちらを呼び出して止めた方が早そうですね。
-- StopCoroutineについての追記(2017/03/17) --
以前StopCoroutineではStartCoroutineの引数に文字列として関数名を渡した時だけ作用すると書きましたが、StartCoroutineの戻り値はCoroutine型であり、
スタートさせたコルーチンの参照を保持しておくことも出来るようです。
つまり
1 2 3 4 5 6 7 | // コルーチンスタート時に参照を保持 Coroutine c = StartCoroutine(Test()); // コルーチンを止める時に参照を指定 StopCoroutine(c); |
とStartCoroutineでスタートさせたコルーチンの参照変数を保持しておき、StopCoroutineでその参照変数を使ってコルーチンを止める事が出来ます。
指摘されるまで全然気が付きませんでした。m(__)m
なお今回のサンプルでは連続してコルーチンを開始させているので開始させる毎に別の参照変数へと入れていかないといけません。
-- StopCoroutineについての追記終了 --
コルーチンの戻り値
さて、一番わかり辛いと思うのがコルーチンの戻り値です。
戻り値がIEnumerator型という事でチンプンカンプンです・・・・(-_-)
ここが特殊で解り辛いところですね・・・、やり方だけ覚えてしまいましょう。
さきほどから登場していた実行しているコルーチンTest関数を見ていきます。
JavaScriptとC#では記述が少し変わるので両方を載せておきます。
JavaScript
まずはJavaScriptでの記述を見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 | // JavaScriptでの記述 function Test() { // 1秒待つ yield WaitForSeconds(1.0f); // 次のフレームに飛ばす yield; } |
JavaScriptでは通常の関数と同じ記述で戻り値を記述しなくて大丈夫です。
明示的に
function Test() : IEnumerator {
としても大丈夫です。
yieldという見慣れない記述がありますが、難しい事はわかりません・・・・(^_^;)
一旦次のフレームに飛ばすと考えてもらえばいいのかもしれません。
yield WaitForSeconds(1.0f)
と記述すると1秒後に次のフレームに飛ばします。
yield
だけだとその場で次のフレームに飛ばします。
C#
次はC#での記述を見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 | // C#での記述 IEnumerator Test() { // 1秒待つ yield return new WaitForSeconds(1.0f); // 次のフレームに飛ばす yield return null; } |
C#で1秒待つには
yield return new WaitForSeconds(1.0f)
と記述します。
次のフレームに飛ばすには
yield return null
です。
JavaScriptとC#では多少記述が変わっていますね。
コルーチンのサンプルを作成する
コルーチンの概要を見てもよくわからないと思うので実際にサンプルを作って実行してみましょう。
以下のスクリプトを作成しMainCamera等のゲームオブジェクトに取りつけます。
ヒエラルキー上で右クリック→UI→Textを作成し、このスクリプトのインスペクタにこのTextを設定します。
JavaScriptサンプル
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | import UnityEngine.UI; public var stateText : Text; // コルーチンの状態を表示するテキスト function Start () { stateText.text = "コルーチン停止中"; } function Update () { // スペースキーを押してコルーチンを開始 if(Input.GetKeyDown("space")) { // 通常の関数呼び出しでコルーチンを実行(JavaScriptだと出来る方法) // Test(); // StartCoroutineを使用してコルーチンを実行 // StartCoroutine(Test()); // 引数は文字列として渡す StartCoroutine("Test"); stateText.text = "コルーチン実行中"; // 1のキーを押したら全コルーチンをストップ } else if(Input.GetKeyDown("1")) { // StartCoroutineの引数で文字列として渡したものだけ名前で指定したコルーチンを止められる // StopCoroutine("Test"); // StopAllCoroutineを呼ぶと全てのコルーチンが停止 StopAllCoroutines(); stateText.text = "全コルーチン停止"; } } // 実際にコルーチンで実行する関数 function Test() { // カウント用のローカル変数 var count : int = 0; // コルーチンがオンの時は1秒待ってデータ出力しカウントを進める while(true) { // 最初に現在のカウントを表示 Debug.Log(count); // カウントアップ count++; // 1秒待つ yield WaitForSeconds(1.0f); // 次のフレームに飛ばす yield; } } |
Spaceキーを押すとコルーチンをスタートさせます。
今回は引数にメソッド名を記述するやり方でやります(他のはコメント化してあります)。
1キーを押すと動作しているコルーチンを全部停止させます。
その為Test関数以外のコルーチンTester関数を作り実行していたとしても停止します。
Testだけを停止するには
1 2 3 | StopCoroutine("Test") |
を使います。
実際に動作しているTest関数内を見ていきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 実際にコルーチンで実行する関数 function Test() { // カウント用のローカル変数 var count : int = 0; // コルーチンがオンの時は1秒待ってデータ出力しカウントを進める while(true) { // 最初に現在のカウントを表示 Debug.Log(count); // カウントアップ count++; // 1秒待つ yield WaitForSeconds(1.0f); // 次のフレームに飛ばす yield; } } |
Test内ではローカル変数であるcountを宣言し0で初期化しています。
whileループで常に条件がtrueなので繰り返し実行します。
その中でcountの値を表示しカウントアップ、yield WaitForSeconds(1.0f)で1秒待機したあとyieldで次のフレームに飛ばします。
その為1秒毎にcountの数字を永遠表示し続ける処理になります。
count変数は毎回0に初期化されるような気がしますが、実際はカウントアップされていきます。
次のフレームに移動してもwhileループの中にいると考えるといいのかも?
このサンプルではSpaceキーを押すたびにコルーチンを開始していますが、そうするとどうなるのでしょうか?
実は新しくコルーチンが開始され、前に開始したコルーチンとは別物が動作します。
その為Spaceキーを押すたびに新しいコルーチンが開始されコンソールにそれぞれのカウントがされていくことになります。
C#サンプル
次はC#で記述したスクリプトです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | using UnityEngine; using System.Collections; using UnityEngine.UI; public class TestCoroutine2 : MonoBehaviour { public Text stateText; // コルーチンの状態を表示するテキスト // Use this for initialization void Start () { stateText.text = "コルーチン停止中"; } // Update is called once per frame void Update () { // スペースキーを押してコルーチンを開始 if(Input.GetKeyDown("space")) { // 通常の関数呼び出しでコルーチンを実行(C#では出来ない) // Test(); // StartCoroutineを使用してコルーチンを実行 // StartCoroutine(Test()); // 引数は文字列として渡す StartCoroutine("Test"); stateText.text = "コルーチン実行中"; // 1のキーを押したら全コルーチンをストップ } else if(Input.GetKeyDown("1")) { // StartCoroutineの引数で文字列として渡したものだけコルーチンを止められる // StopCoroutine("Test"); // StopAllCoroutineを呼ぶと全てのコルーチンが停止 StopAllCoroutines(); stateText.text = "全コルーチン停止"; } } // 実際にコルーチンで実行するメソッド IEnumerator Test() { // カウント用のローカル変数 int count = 0; // コルーチンがオンの時は1秒待ってデータ出力しカウントを進める while(true) { // 最初に現在のカウントを表示 Debug.Log(count); // カウントアップ count++; // 1秒待つ yield return new WaitForSeconds(1.0f); // 次のフレームに飛ばす yield return null; } } } |
書いてある事は同じです。
それではサンプルを実行してみましょう。
何秒かおきにSpaceキーを押してコルーチンを開始しています。
コンソールに違うコルーチンのカウントが表示されていってますね。
途中で1キーを押してコルーチンを全て停止し、コンソールのClearボタンを押してコンソールを消しコルーチンが停止している事を確認しています。
その後再びSpaceキーを押して新しいコルーチンを開始してます。
コルーチンを使ってみて
正直なところコルーチンは使い方がわかり辛いので全然使ってなかったんですが、場合によっては非常に便利かもしれませんね。
Update関数内で何らかの条件に合致するか判定する場合1秒間に何十回も判定をしてしまいますが、もっと少ない回数で条件を判定したい時は
コルーチンを使って自分で指定した秒数毎に条件を判定するようにすれば判定回数が減って処理速度の向上に繋がる可能性があります。
でもIEnumerator型やらyieldやら使い方がわかり辛いですね・・・・(^_^;)