うら干物書き

ゲームを作っています。

【Unity】MonoBehaviour.Invoke()ってなんだろう?

 こんにちは、うら干物です。

 ネット上でコードを見ていたらInvokeという関数を見つけました。
 てっきり自作された関数かと思いきや、MonoBehaviourの持つPublic関数でした。

 うーむ、使ったことがない。
 今回はInvoke関数について調べてみましたよ。


この記事にはUnity5.4.0f3を使用しています。

f:id:urahimono:20170128173205p:plain

Invoke()は何する関数なの?

 この関数は何する関数なのでしょうか。
 ドキュメントを見ると、指定した時間指定した関数を呼び出すというもののようです。
 おや、なかなか便利そうな関数ですよ。
 同様のことをコルーチンなどでやろうとすると、そこそこのコードを記述する必要がありますもんね。

 指定する関数には、返り値なし、そしてSendMessage()と違って引数指定はできないため、引数もない関数でなければいけません。

 気になったのは、呼び出される関数を指定する方法が関数名を文字列でということでしょうか。
 うーん、関数を文字列で指定するのは少し抵抗がありますね。
 関数名を変更などした際に、指定文字列を変更し忘れてもコンパイルエラーにならないので、面倒なバグを生みそうなものですから。

 ただ、テスト用の処理をパっと記述するには良さそうですね。

 Invoke()にはUnity公式のチュートリアルもあります。使用方法の参考になりそうです。

unity3d.com

Invoke関連の関数

 Invoke関連の関数はいくつかあるみたいです。

  • Invoke()
  • InvokeRepeating()
  • CancelInvoke()
  • IsInvoking()

 指定した時間繰り返したり、キャンセルしたりといろいろありますね。
 次の章では各関数の説明と使う際に気になってので調査した点を紹介します。

Invoke()

public void Invoke(string methodName, float time)

docs.unity3d.com

 指定した時間に指定した関数を呼び出す関数です。
 指定する時間の単位はです。

 この関数を使う際に気になった点を以下に書き出してみました。
 私が調べた限りで回答も記述してみました。ご参考になればと思います。

  • Q:Awake()やStart()などで使えるの?
    A:Awake()でもStart()でも使えました。

  • Q:同じ名前の関数を複数回使えるの?
    A:指定した関数名をすでにInvoke()を使っている最中でも使えました。その場合はInvoke()を呼んだ回数分、指定した関数が実行されます。
     同フレーム内で呼んでもOKでした。

  • Q:同じ時間を指定して複数回使えるの?
    A:使えました。呼び出される時間がまったく同じ場合は、Invoke()を呼んだ順で関数が実行されました。

  • Q:存在しない関数名を呼ぶとどうなるの?
    A:指定時間後に以下のログが表示されました。
    Trying to Invoke method: [クラス名].[指定した関数名] couldn’t be called.
    nullを指定すると指定時間後、エディタがクラッシュしました。良い子はまねしないようにしてくださいね。

  • Q:指定時間に0秒を指定するとどうなるの?
    A:以下のような結果になりました。
    Awake()で呼ぶと、同フレームのStart()後Update()前
    Start()で呼ぶと、同フレームのStart()後Update()前
    Update()で呼ぶと、同フレームのUpdate()後LateUpdate()前
    LateUpdate()で呼ぶと、同フレームのLateUpdate()後、同フレーム内。

  • Q:指定時間に負数を指定するとどうなるの?
    A:扱いは0秒を指定した時と同じでした。

  • Q:別のコンポーネントからInvoke()を呼べるの?
    A:呼んで実行できました。

  • Q:オーバーロードされた関数名を指定する場合はどうなるの?
    A:引数がないものが呼ばれました。

  • Q:引数がある関数でもデフォルト引数で用いて引数なしでも呼べる関数の場合はどうなるの?
    A:呼ばれませんでした。

  • Q:Invokeを使用するコンポーネントのenabledがfalseの状態で呼んだらどうなるの?
    A:とくに問題なく実行されました。

  • Q:Invokeを使用するコンポーネントがアタッチしているGameObjectがSetActive(false)の状態で呼んだらどうなるの?
    A:とくに問題なく実行されました。

  • Q:Invoke()を読んだ後、コンポーネントのenabledがfalseの状態になったらどうなるの?
    A:とくに問題なく実行されました。

  • Q:Invoke()を読んだ後、コンポーネントがアタッチしているGameObjectがSetActive(false)の状態になったらどうなるの?
    A:とくに問題なく実行されました。

  • Q:Invoke()を読んだ後、コンポーネントをDestroyしたらどうなるの?
    A:さすがに実行されませんでした。

  • Q:Invoke()を読んだ後、コンポーネントがアタッチしているGameObjectをDestroyしたらどうなるの?
    A:さすがに実行されませんでした。

 以下の呼び出し方をした場合、ログはこうなります。

using UnityEngine;

public class DemoInvoke : MonoBehaviour
{
    
    void Awake()
    {
        Debug.LogFormat( "Awake. time={0}", Time.time );

        Invoke( "MethodA", 0.0f );
    }

    void Start()
    {
        Debug.LogFormat( "Start. time={0}", Time.time );

        Invoke( "MethodB", 0.0f );
    }

    void Update()
    {
        Debug.LogFormat( "Update A. time={0}", Time.time );

        Invoke( "MethodC", 0.0f );

        Debug.LogFormat( "Update B. time={0}", Time.time );
    }

    void LateUpdate()
    {
        Debug.LogFormat( "LateUpdate A. time={0}", Time.time );

        Invoke( "MethodD", 0.0f );

        Debug.LogFormat( "LateUpdate B. time={0}", Time.time );
    }


    void MethodA()
    {
        Debug.LogFormat( "Call MethodA. time={0}", Time.time );
    }

    void MethodB()
    {
        Debug.LogFormat( "Call MethodB. time={0}", Time.time );
    }

    void MethodC()
    {
        Debug.LogFormat( "Call MethodC. time={0}", Time.time );
    }

    void MethodD()
    {
        Debug.LogFormat( "Call MethodD. time={0}", Time.time );
    }


} // class DemoInvoke

f:id:urahimono:20160827102609p:plain

InvokeRepeating()

public void InvokeRepeating(string methodName, float time, float repeatRate)

docs.unity3d.com

 Invoke()の関数に繰り返す時間を指定できるようになった関数です。
 繰り返しに指定する時間の単位もです。

  • Q:存在しない関数名を呼ぶとどうなるの?
    A:Invoke()同様のログが出ました。繰り返されるので何回もログが出ます。
    nullを指定するとやっぱりクラッシュします。

  • Q:繰り返す時間に0を指定するとどうなるの?
    A:繰り返しませんでした。Invoke()扱いになるみたいです。

  • Q:繰り返す時間に負数を指定するとどうなるの?
    A:InvokeRepeating()を呼んだ瞬間に以下の例外が発生しました。
    UnityException: Invoke repeat rate has to be larger than 0.00001F)
    上記の例外メッセージを見る限り、0を指定するのもよくないみたいですね。

CancelInvoke()

public void CancelInvoke()
public void CancelInvoke(string methodName)

docs.unity3d.com

 現在呼び出しているInvoke()、InvokeRepeating()をキャンセルする関数です。
 引数に関数名を渡すことで、指定した関数名で呼び出したInvoke()、InvokeRepeating()のみをキャンセルすることができます。

  • Q:関数名を指定した場合、複数のInvoke()を呼んでいたらどうなるの?
    A:同じ関数名で指定しているInvoke()は全てキャンセルされました。

  • Q:Invoke()を呼び出していない場合はどうなるの。
    A:何も起こりません。

  • Q:存在しない関数名を呼ぶとどうなるの?
    A:何も起こりません。
    ただし、nullを指定するとクラッシュするのはお約束です。

  • Q:別のコンポーネントから呼べるの?
    A:呼べました。

IsInvoking()

public bool IsInvoking()
public bool IsInvoking(string methodName)

docs.unity3d.com

 現在呼び出しているInvoke()、InvokeRepeating()をがあるかを調べる関数です。
 引数に関数名を渡すことで、指定した関数名で呼び出したInvoke()、InvokeRepeating()があるかを調べることもできます。

  • Q:Invoke()を呼んだすぐ次の行で呼ぶとどうなるの?
    A:trueが返ってきたので、即時対応されるようです。

  • Q:InvokeRepeating()で呼び出されているものはどういう対応になるの?
    A:キャンセルされるまで、ずっとtrueが返ってきました。

  • Q:引数にnullを渡すとどうなるの?
    A:無論、エディタがクラッシュします。

 いかがでしたでしょうか。
 とりあえず、文字列を指定する引数にnullは渡すなを合言葉にUnityでゲームを作っていきましょうね!