【Unity】コルーチンとは?任意のタイミングで処理の中断や再開を行える機能

コルーチンとは? Unity

こんにちは、ともくんのゲーム作り部屋にようこそ!

このページでは、

「コルーチンってなに?」

「コルーチンでどんなことができるの?」

というお悩みの方に向けた内容となっています。

Unityでは、コルーチンという機能を使うことができます。

このコルーチンとは、処理を中断したり再開したりすることができるメソッドのことで、例えば、オブジェクトを移動させた後に2秒間停止させて、その後回転処理を行うなどの場合に活用できます。

また、コルーチンを使うことで、複数の処理を疑似的に並行して行うといったことができるようになるので、ゲーム内の処理の効率を上げることができます。

そこで、このページでは、Unityで使えるコルーチンという機能について、どんな機能なのか、また使い方までをまとめていきます。

この記事を書いた人

ゲーム作りを学び始めた一児のパパです。
このブログは、子供から「ゲームを作ってみたい!」と言われ、非プログラマーでゲーム作りをしたことない僕が、ゲーム作りの本を読んで独学でゲーム開発を学んでいるブログです。
同じように初めてゲーム作りをしている方と一緒に学んでいけるようなブログに出来たらいいなと思っています。
また、「このコードはおかしい」とか「もっと良い書き方があるよ!」などあれば、どんどん指摘して頂けると助かります。

コルーチンとは?

まずは、コルーチンがどういうものなのかを紹介していきます。

任意のタイミングで処理を中断したり再開できるメソッド

コルーチンとは、任意のタイミングで処理を中断させたり再開させることができるメソッドのことを言います。

通常のメソッドの場合、実行されると処理は1フレームの中で行われることになります。

例えば、オブジェクトを移動させる処理とオブジェクトの色を変える処理をメソッド内で記述していた場合に、そのメソッドを実行したら1フレーム内で処理が行われることになります。

しかし、コルーチンを使って記述した場合、オブジェクトを移動させる処理が行われて、その後の処理を一旦中断させることができます。

そして、5秒経過したらオブジェクトの色を変える処理を実行させるということができるようになります。

このようにコルーチンを使うことで、メソッド内の処理を途中で中断させることができ、そして任意のタイミングで再開させるという仕組みを作ることができるようになります。

並行した複数の処理を作成できる

このコルーチンを使うことで、通常の処理の流れとは別で複数の処理を疑似的に並行して実行することができるようになります。

例えば、2つのコルーチンを作って、Startメソッドから実行させると、以下のように複数の処理が同時に行われるようになります。

このように、複数の処理が同時に実行されていることを非同期処理と呼びます。

コルーチンを使うことでこの非同期処理を作ることができ、ゲーム内の描画などの処理を停止せずに別の処理を行うことができるというメリットがあります。

コルーチンの基本的な使い方

ここからは、コルーチンの基本的な使い方について紹介していきます。

IEnumerator型のメソッドでコルーチンを作る

まず、コルーチンを作る場合は、IEnumerator型というメソッドを作成します。

IEnumerator メソッド名()
{
//コルーチンの処理
}

このメソッド自体がコルーチンとなり、呼び出して使うことで中に書いた処理が実行されるようになります。

ただし、このIEnumerator型を使う際は、以下のように先に「using System.Collections;」と書いておく必要があります。

using System.Collections;

なお、メソッド内に記述する中断する処理のコードなどは、後ほどまとめていきます。

コルーチンの実行や中断は「StartCoroutine」「StopCoroutine」で記述

先ほど作ったコルーチンを呼び出す場合、StartCoroutineメソッドを使って以下のように記述していきます。

StartCoroutine(routine);

routine:実行させたいコルーチン(IEnumerator型もしくはstring型)

例えば、UnityのStartメソッド内でコルーチンを実行させるという場合、以下のようにスクリプトを記述していきます。

using System.Collections;   // コルーチンを使うのに必要
using UnityEngine;

public class Sample : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(Test()); // コルーチンを実行させる処理
    }

    IEnumerator Test()
    {
        //コルーチンの処理
    }

}

11行目で作成したTestというメソッドがコルーチンとなっていて、それを8行目のStartCoroutineメソッドで実行するという処理を書いています。

また、実行しているコルーチンを停止させたい場合は、StopCoroutineメソッドを使って同様に以下のように記述します。

StopCoroutine(routine);

routine:停止させたいコルーチン(IEnumerator型もしくはstring型)

なお、StopAllCoroutinesメソッドを使うと、コルーチンが複数動いている場合に、全てのコルーチンの処理が停止することになります。

StopAllCoroutines();

コルーチン内で使える主な機能

コルーチンの中では、通常のコード以外に以下の停止処理を行うコードを記述していくことができます。

  • yield return null:1フレーム停止させる
  • yield return new WaitForSeconds:指定した秒数停止させる
  • yield break:コルーチンを途中で終了させる
  • yield return new WaitUntil:条件がtrueになるまで停止させる
  • yield return new WaitWhile:条件がfalseになるまで停止させる
  • yield return StartCoroutine:別のコルーチンを開始して終わるまで停止させる

ちなみに、コルーチン内では、これらの「yield return」という記述が必ず一つは必要になります。

ここでは、それぞれのコードの仕組みと実際の使用例を合わせて紹介していきます。

yield return null:1フレーム停止させる

コルーチンの中で以下のように記述すると、1フレーム停止させるという処理を行います。

yield return null;

つまり、このコードを書いた後の処理は、次のフレームで実行されることになります。

例えば、以下のようにコルーチンを使ってコンソールウィンドウに表示させる処理のスクリプトを書いてみます。

using System.Collections;
using UnityEngine;

public class Sample : MonoBehaviour
{
    void Start()
    {
        Debug.Log("処理1");
        StartCoroutine(Test()); // コルーチンを実行させる処理
        Debug.Log("処理3");
    }

    IEnumerator Test()
    {        
        yield return null;  // 1フレーム停止
        Debug.Log("処理2");
    }

}

9行目でStartCoroutineメソッドを使って、Testというコルーチンを実行する処理を行っています。

そして、13行目でTestのコルーチンを定義して、この中で「yield return null;」で1フレーム停止させた後に、コンソールウィンドウに表示する処理を書いています。

これでゲームを実行してみると、

Startメソッド内で書いたDebug.Logの方が先に表示されていて、Testのコルーチン内の処理が次のフレームで実行されているのが分かります。

yield return new WaitForSeconds:指定した秒数停止させる

コルーチンの中で以下のように記述すると、指定した秒数の間、停止させるという処理が行われます。

yield return new WaitForSeconds(seconds);

seconds:停止させたい秒数(float型)

例えば、スペースキーを押すとオブジェクトの色が変わった後、5秒後にオブジェクトが移動するという処理を作ってみます。

using System.Collections;
using UnityEngine;

public class Sample : MonoBehaviour
{
    Renderer rend;

    void Start()
    {
        rend = GetComponent<Renderer>();
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            StartCoroutine(Move()); // コルーチンを実行する処理
        }
    }

    IEnumerator Move()
    {
        rend.material.color = Color.red;    // 赤色に変更する処理
        yield return new WaitForSeconds(5f);    // 5秒中断させる
        transform.position = new Vector2(7.5f, 1f); // 座標の位置を移動させる処理
    }

}

20行目でMoveというコルーチンを定義しています。

そして、この中の23行目で5秒間停止させる処理を行っています。

ゲームを実行してみると、

スペースキーを押すと色が変わり、その後5秒間経過したらオブジェクトが移動する処理が行われているのが分かります。

yield break:コルーチンを途中で終了させる

コルーチンの中で以下のように記述すると、そのコルーチンを途中で終了させることができます。

yield break;

例えば、先ほどのMoveというコルーチンを少し修正してみます。

using System.Collections;
using UnityEngine;

public class Sample : MonoBehaviour
{
    Renderer rend;

    void Start()
    {
        rend = GetComponent<Renderer>();
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            StartCoroutine(Move()); // コルーチンを実行する処理
        }
    }

    IEnumerator Move()
    {
        rend.material.color = Color.red;    // 赤色に変更する処理
        yield break; ;    // コルーチンを終了させる
        transform.position = new Vector2(7.5f, 1f); // 座標の位置を移動させる処理
    }

}

コルーチンの中の23行目で、コルーチンを途中で終了させる処理を行っています。

これでゲームを実行してみると、

スペースキーを押すと、オブジェクトの色が変わりますが、その後の移動処理は行われず、途中で終了しているのが分かります。

yield return new WaitUntil:条件がtrueになるまで停止させる

コルーチンの中で以下のように記述すると、条件がtrueになるまで停止させることができます。

yield return new WaitUntil(条件);

この条件を指定する際は、デリゲートというものを使用する必要があるため、実際には以下のように書くことで処理を作ることができます。

yield return new WaitUntil(() => 条件式);

例えば、aという変数がtrueになるまで停止させる場合は、

yield return new WaitUntil(() => a == true);

と記述します。

実際に、スペースキーを押してオブジェクトが回転した後に、マウスを5回クリックしていたら、オブジェクトがさらに回転するという処理を作ってみます。

using System.Collections;
using UnityEngine;

public class Sample : MonoBehaviour
{
    int mouseCount;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            StartCoroutine(Rotation()); // コルーチンを実行する処理
        }

        if (Input.GetMouseButtonDown(0))
        {
            mouseCount++;   // マウスのクリック回数を計測
        }
    }

    IEnumerator Rotation()
    {
        transform.Rotate(0, 0, 10f);    // オブジェクトを10度回転させる
        yield return new WaitUntil(() => mouseCount >= 5);  // マウスが5回以上クリックするまで停止
        transform.Rotate(0, 0, 20f);    // オブジェクトを20度回転させる
    }

}

21行目でRotationというコルーチンを作成して、この中でyield return new WaitUntil」を使って、マウスをクリックしたカウントの変数が5以上になるまで停止させる処理を記述しています。

また、マウスのクリックカウントは、Updateメソッド内の17行目でカウントするようにしています。

これでゲームを実行してみると、

スペースキーを押すと少しオブジェクトが回転した後、マウスが5回クリックされるまでは停止されていて、5回以上クリックされたら、オブジェクトが再度回転する処理が行われているのが分かります。

yield return new WaitWhile:条件がfalseになるまで停止させる

コルーチンの中で以下のように記述すると、条件がfalseになるまで停止させることができます。

yield return new WaitWhile(条件);

先ほどの「WaitUntil」と同様に、条件にはデリゲートを使用するため、

yield return new WaitWhile(() => 条件式);

上記のような形で指定していきます。

例えば、スペースキーを押すとオブジェクトが移動した後に、もう一度スペースキーを押すと今度は回転するという処理を作ってみます。

using System.Collections;
using UnityEngine;

public class Sample : MonoBehaviour
{
    bool isCoroutine = false;
    bool isLocking = false;

    void Update()
    {
        // スペースキーを押すとtrueとfalseが変わる
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (!isLocking)
            {
                isLocking = true;
            }

            else if (isLocking)
            {
                isLocking = false;
            }
        }

        if (Input.GetKeyDown(KeyCode.Space) && isCoroutine == false)
        {
            StartCoroutine(SpaceClick()); // コルーチンを実行する処理
            isCoroutine = false;
        }
    }

    IEnumerator SpaceClick()
    {
        transform.position = new Vector2(3f, 3f);    // オブジェクトを移動させる処理
        yield return new WaitWhile(() => isLocking);  // 変数がfalseになるまで停止させる処理
        transform.Rotate(0, 0, 20f);    // オブジェクトを20度回転させる
    }

}

32行目でSpaceClickのコルーチンを定義していて、この中の35行目で「yield return new WaitWhile」を使って、bool型の変数がfalseになるまで停止させる処理を作ってみます。

そして、Updateメソッドの中の12行目で、スペースキーを押した場合に、trueとfalseを変更する処理を行っています。

これでゲームを実行してみると、

スペースキーを押したらオブジェクトが移動して、もう一度スペースキーを押すとオブジェクトが回転するようになっているのが分かります。

yield return StartCoroutine:別のコルーチンを開始して終わるまで停止させる

コルーチンの中で以下のように記述すると、別のコルーチンを実行させることができ、そのコルーチンの処理が終わるまでの間、停止させることができます。

yield return StartCoroutine(routine);

routine:実行させたいコルーチン(IEnumerator型もしくはstring型)

例えば、オブジェクトを動かす処理のコルーチンと、オブジェクトを回転する処理のコルーチンを作ってみます。

using System.Collections;
using UnityEngine;

public class Sample : MonoBehaviour
{
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            StartCoroutine(Move()); // コルーチンを実行する処理
        }
    }

    IEnumerator Move()
    {
        transform.position = new Vector2(transform.position.x, 3f);    // 上に移動させる処理
        yield return new WaitForSeconds(0.5f); // 0.5秒停止させる処理
        yield return StartCoroutine(Rotation());    // Rotationのコルーチンが終わるまで中断する処理
        transform.position = new Vector2(5f, transform.position.y);    // 右に移動させる処理
    }

    IEnumerator Rotation()
    {
        for (int i = 0; i < 10; i++)
        {
            transform.Rotate(0, 0, 10f);    // 回転させる処理
            yield return new WaitForSeconds(0.5f); // 0.5秒停止させる処理
        }
    }
}

オブジェクトを動かすMoveのコルーチンの中の18行目で、「yield return StartCoroutine」を使ってRotationのコルーチンを開始する処理を記述しています。

そして、Rotationのコルーチンでは、for文を使い回転処理をフレーム毎に10回繰り返す処理を行っています。

また、回転させる間隔が0.5秒ごとになるように、「WaitForSeconds」を使って中断処理を行っています。

これで実際にゲームを実行してみると、

Moveのコルーチンでオブジェクトが上に移動した後、Rotationのコルーチンの回転処理が10回行われて、回転処理が終了したらオブジェクトが右に移動できているのが分かります。

まとめ

このページでは、Unityのコルーチンという機能について、どんな仕組みなのか、また使い方までをまとめていきましたが、いかがでしたでしょうか?

コルーチンとは、処理の中断や再開を行うことができるメソッドのことで、IEnumerator型のメソッドで作ります。

このコルーチンを使うことで、指定秒数待たせてから処理を行ったり、条件を達成するまで処理を停止したりすることができます。

また、複数の処理を疑似的に並行して行うことができるので、ゲーム内の処理の向上に繋がります。

最後までお読みいただきまして、ありがとうございました!

コメント