コルーチンの初歩を説明したりコルーチンが停止してしまう理由を追求したりする![Unity]

Unity

処理を何秒か止めたい時ありますよね!

どうも!Unityのコルーチンってご存じですか?

めっちゃ専門的用語で説明すると非同期処理を実行できる関数みたいなもんです。

まぁそんな専門用語はどうでもいいのですが 笑

とりあえずコルーチンで一番使うのがwaitForSecondsだと思います。

waitForSecondsは何か処理を待たせたいってときに使います。

分かりやすいのだとボタン押して数秒後に

なにかが起きるようにしたい時ですね。

例えば以下のような場合です。

上の映像は何をしているかというと

ボタンを押して3秒後に爆発のエフェクトを生成しています。

これはコルーチンを実行して実現しているのですが

どうもコルーチンが動かない場合があるみたいなんですね。

ってことでどうするとコルーチンが動かなくなるのか?っていうのを

まとめていこうと思います。

またそもそもコルーチンってどうすると実行できるの?って方のため

最初にコルーチンの基本的な書き方とかも記載していこうと思います!

そんなの知ってるよ!って方は最初は飛ばしてね!

ではいきましょー

[初級]コルーチンってどう書けばいいの?

そもそもコルーチンってどう書くの?って方のために上記のボタンを押して

3秒後にエフェクトが実行される処理を最初っから解説します。

結構初歩から説明するのでそんなん知ってるわ!

ってのもあるかと思いますがご容赦ください。

まずはボタンを作る

まずはボタンを作りましょう

hierarchyウィンドウで右クリック→UI→Buttonでボタンが作成されます。

ボタンが作成されました。

ボタンが押された時に何か処理を実行したい場合は

ボタンを選択した時のInspectorウィンドウのButtonの

OnClick()に処理を設定します。

まずは実行される処理自体を作成しましょう。

ということでC#スクリプトを作成します。

Projectウィンドウ上で右クリック→Create→C#Scriptで作成できます。

今回はこのスクリプトは「ButtonClick」という名前にしました。

中身はとりあえず以下にしています。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ButtonClick : MonoBehaviour
{
    public void buttonClick()
    {
        Debug.Log("CLICK!");
    }
}

見て分かる通りbuttonClick()というメソッドを作成しています。

これを実行するとConsoleに”CLICK!”って表示されるんです。

このスクリプトをボタンに設定します。

まずボタンのOnclick()の+をクリックして以下のようにしましょう

このNone(Object)となっているところにスクリプトを設定するのですが

ここにはゲームオブジェクトしか設定できないんです。

なのでまず空のゲームオブジェクトを作成します。

hierarchyウィンドウで右クリック→Create Emptyで作成できます。

さらに作成されたゲームオブジェクトのInspectorウィンドウの

Add Componentで先ほど作成したスクリプトをアタッチしてあげます。

以下を参考にしてください。

これでボタンに処理を追加できます!

実際にやってみましょう。

ボタンのOnClick()のところにさきほど作成したゲームオブジェクトをドラッグ&ドロップします。

その後にNo Functionというところをクリックし

先ほど作成したbuttonClick()というメソッドを選択しましょう。

これでボタンを押した時にButtonClick()という処理が実行されるようになります。

ということでボタンはゲームオブジェクトにアタッチされたスクリプトを実行するんですね。

一旦ゲーム開始してみてボタンを押してみましょう。

ボタンを押すとちゃんとConsoleにCLICK!って表示されました。

これでコルーチンを実行できる前提処理が作成できました!

ここから本題のコルーチンの作り方です。

コルーチンを実装してみる!

ではコルーチンを実装してみましょう。

ButtonClickのスクリプトを以下のように変更しましょう。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ButtonClick : MonoBehaviour
{
    [SerializeField]
    private GameObject exp;
    
    public void buttonClick()
    {
        StartCoroutine(explosion());
    }

    private IEnumerator explosion()
    {
        yield return new WaitForSeconds(3f);

        Instantiate(exp, new Vector3(0, 0.5f, 0), Quaternion.identity);
    }
}

背景色が変わっているところが変更したところです。

まず生成するゲームオブジェクトの変数「exp」を追加しました。

また新たにコルーチンの関数であるexplosion()を追加しました。

コルーチンはIEnumerator型で定義します。

IEnumeratorってなに?って思いますよね。

気持ち悪いと思いますが

とりあえずこれは「お決まり」って考えてください。

IEnumerator型は

yield return ~

を実装する必要があります。

で、今回は

yield return new WaitForSeconds();

としていますね。

これが処理を止めるコードになります。

yield return new WaitForSeconds(3f);

としているので今回は3秒待ちます。

3fと記載しているところが待つ秒数です。

5fと記載すれば5秒待ちますし、1.5fと記載すれば

1.5秒待ちます。

※このfはfloatの頭文字のfです。
integer型(整数)とかじゃなくてfloat型だよって知らせるための記号です。

最後にbuttonClickの中でStartCorountine(explosion());

としてコルーチンを実行するようにしています。

コルーチンはStartCoroutine(関数名)で実行するんです。

ちなみに StartCorountine(“explosion”); (関数名をダブルクォーテーションで囲む)


でも実行できます。

ではスクリプトは完成したので記載したGameObject expを設定してあげます。

今回は以下のアセットのWFXMR_Nukeを使いました。

War FX | VFX Particles | Unity Asset Store
Add depth to your next project with War FX from Jean Moreno. Find this & more VFX Particles on the Unity Asset Store.

こちらをimportしてWFXMR_Nukeをexpにドラッグ&ドロップします。
以下を参考にしてみてください。

これで完成しました!

ゲームを開始してボタンを押してみましょう!
冒頭のように3秒後に爆発が起きます!

本題:コルーチンが動かなくなる場合

さぁやっと本題です。

なんか色々やってて分かったのですが

コルーチンが動かなくなるパターンがあるんですよね。

どうなるとコルーチンが停止してしまうのか答えは公式に書いてありました。

Unity - Manual: Coroutines

とはいえ英語なので分かりにくいっす!

最後の方の「Note:~」に記載されています。

ここを具体的に以下に記載していきます。

ちゃんとしたコルーチンの止め方

コルーチン単体を止める場合はStopCoroutine

特定のコルーチンのみ止めたい場合は

StopCoroutineで止めます

とりあえず以下に全体のコードを記載します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ButtonClick : MonoBehaviour
{
    [SerializeField]
    private GameObject exp;

    private IEnumerator expCoroutine1;

    public void buttonClick()
    {
        expCoroutine1 = explosion();
        StartCoroutine(expCoroutine1);
    }

    public void stop1()
    {
        StopCoroutine(expCoroutine1);
    }

    private IEnumerator explosion()
    {
        yield return new WaitForSeconds(3f);

        Instantiate(exp, new Vector3(-1, 0.5f, 0), Quaternion.identity);
    }
}

背景色が変わっているところが主要な箇所です。

まず止めたいコルーチンを変数として記憶する必要があるので

IEnumerator型の変数「expCoroutine1」を定義しておきます

private IEnumerator expCoroutine1;

その上でコルーチン開始時に定義した変数に実行するコルーチンの関数を設定して

StartCoroutineしておきます。

expCoroutine1 = explosion();
StartCoroutine(expCoroutine1);

最後にStopCoroutineを実行してコルーチンを止めるのですが

その時に引数として止めたいコルーチンの変数を渡してあげます。

StopCoroutine(expCoroutine1);

これでコルーチンが止まります!

すべてのコルーチンを止めたい場合はStopAllCoroutines

すべてのコルーチンを止めたい場合は

StopAllCoroutinesで止まります

これは簡単で

StopAllCoroutines();

とすると全部止まります。

それぞれ実験してみましょう

ではStopCoroutineとStopAllCoroutinesを

実際に実行してみましょう。

ボタンを複数作ってそれぞれ

コルーチンを実行するボタンとコルーチンを停止するボタン

っていう風に分けました。

実行結果が以下になります。

それぞれのボタンの説明ですが

爆発1ボタンが3秒後に爆発エフェクト1を生成するコルーチンを実行します。

爆発2ボタンも同様に3秒後に爆発エフェクト2を生成するコルーチンを実行します。

それぞれのエフェクトが違うだけで同じ処理です。

ストップ1ボタンが爆発エフェクト1のコルーチンを停止する処理です。

ストップ2ボタンが爆発エフェクト2のコルーチンを停止する処理です。

全ストップボタンが全てのコルーチンを停止する処理です。

一応スクリプトも以下に載せておきますね。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ButtonClick : MonoBehaviour
{
    [SerializeField]
    private GameObject exp;
    [SerializeField]
    private GameObject exp2;

    private IEnumerator expCoroutine1;
    private IEnumerator expCoroutine2;

    public void buttonClick()
    {
        expCoroutine1 = explosion();
        StartCoroutine(expCoroutine1);
    }


    public void buttonClick2()
    {
        expCoroutine2 = explosion2();
        StartCoroutine(expCoroutine2);
    }

    public void stop1()
    {
        StopCoroutine(expCoroutine1);
    }

    public void stop2()
    {
        StopCoroutine(expCoroutine2);
    }

    public void allstop()
    {
        StopAllCoroutines();
    }

    private IEnumerator explosion()
    {
        yield return new WaitForSeconds(3f);

        Instantiate(exp, new Vector3(-1, 0.5f, 0), Quaternion.identity);
    }

    private IEnumerator explosion2()
    {
        yield return new WaitForSeconds(3f);

        Instantiate(exp2, new Vector3(1, 0.5f, 0), Quaternion.identity);
    }
}

以上がコルーチンをちゃんと停止する場合の処理内容でした。

追記(2023年10月12日):コルーチンの注意点

最近やってみてなんか動作がおかしい!っていうパターンがあったので

以下記事にまとめました。

ちょっとした注意点です、よければこちらも参考にしてみてください

コルーチンが勝手に止まっちゃう場合

結論から書くとアタッチしたゲームオブジェクトを
setActive(false)
としたり
Destroy()
したりするとコルーチンは止まってしまう
みたいです。

っていうかよくよく考えたらそんな気しますね。

そりゃそうだわなって感じです。

一応それぞれ実験してみましょう!

setActive(false)ってするとコルーチン停止するよ

ボタンを押すとプレイヤーが消えてその場所に爆発が起きるようにしてみましょう。

まずプレイヤーを作成してそこに以下のスクリプトをアタッチします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Effect : MonoBehaviour
{
    [SerializeField]
    private GameObject exp;

    private IEnumerator expCoroutine1;

    public void effect()
    {
        expCoroutine1 = explosion();
        StartCoroutine(expCoroutine1);
    }

    private IEnumerator explosion()
    {
        yield return new WaitForSeconds(3f);

        gameObject.SetActive(false);

        yield return new WaitForSeconds(1f);

        Instantiate(exp, new Vector3(-2, -2, 0), Quaternion.identity);
    }
}

そして「爆発1」ボタンのOnClick()にプレイヤーにアタッチしたスクリプトのeffect関数を

設定します。

これでボタンを押すと3秒後にプレイヤーが消えて更にその1秒後に

爆発が起きるはずです。

…が、コルーチンはアタッチしたゲームオブジェクトがアクティブでなくなると

停止するので最後の爆発は起きないはずですね


実行してみたのが以下になります。

予想通り爆発だけ起きなくなりました。

Destroy()でもコルーチン停止するよ

setActive(false)でコルーチン停止するので

なんとなく分かると思いますがDestroyでも停止します。

ここは実験する必要もないかなって感じですね。

疑い深い人はやってみてください。多分コルーチンが停止します!

対処法:プレイヤーを消してから爆発させたい場合はコンポーネントのenable false

では最後に対処法です!

今回の場合3秒後にプレイヤーを消してそこに爆発エフェクトを発生させたいんです。

ってなるとどうしてもコルーチンを使うしかないんですよね。

でもプレイヤーのゲームオブジェクトのsetActive(false)ってすると

コルーチン停止しちゃうからじゃぁどうすればいいわけ!?って思いますよね。

こんな時はコンポーネントのenable = falseってすればいいみたいです!

その上で爆発エフェクトを発生させてあげれば

イメージ通りの動作になります。

プレイヤーにアタッチしたEffectスクリプトの変更点は

以下になります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Effect : MonoBehaviour
{
    [SerializeField]
    private GameObject exp;

    private IEnumerator expCoroutine1;

    public void effect()
    {
        expCoroutine1 = explosion();
        StartCoroutine(expCoroutine1);
    }

    private IEnumerator explosion()
    {
        yield return new WaitForSeconds(3f);

        gameObject.GetComponent<SpriteRenderer>().enabled = false;

        yield return new WaitForSeconds(1f);

        Instantiate(exp, new Vector3(-2, -2, 0), Quaternion.identity);
    }
}

これでボタンを押したのが以下になります。

イメージ通りの動作になりました!

コルーチンを使うんなら

ゲームオブジェクト自体を消すんじゃなくてスプライトのコンポーネントの

enable をfalseにしろってことだったみたいです。

タイトルとURLをコピーしました