いろいろなタイマー

2021年5月5日

Unityで使えるいろいろなタイマーについて見ていきましょう。
今回は、3秒後にメソッドが実行される様子を確認していきます。

サンプル作成に必要な準備

タイマーが正常に動作しているか確認するため、サンプルは、ストップウォッチで時間測定します。

きちんと3秒後にコードが実行できるか確かめるため、C#のStopwatchクラスを使います。ネームスペースSystem.Diagnosticsに実装されていますので、使うには、usingで定義する必要があります。

時間測定用にタイマーを用意

// Stopwatchクラスを使うため、usingを追加
using System.Diagnostics;

// Stopwatchのインスタンスを作成
Stopwatch stopwatch = new Stopwatch();

// ストップウォッチをスタート
stopwatch.Start();

/* ここで何からの処理 */

// ストップウォッチをストップ
stopwatch.Stop();

// コンソールに結果を表示
Debug.Log(“Time “ + stopwatch.Elapsed);

Debugクラスの記述省略

System.Diagnosticsネームスペースには、Debugクラスがあります。Unityにも同じクラスが存在するため、どちらのクラスを使うのか不定なので、System.Diagnostics.Debeg.Log()やUnityEngine.Debug.Log()のようにネームスペースを含めた記述で判別しなければなりませんが、これを省略することができるようになりました。

省略しない場合

using System.Diagnostics;
using UnityEngine;

UnityEngine.Debug.Log(“サンプル”);

省略できる場合

ただし、省略するにはクラスが staticである必要があります。

using System.Diagnostics;
using UnityEngine;
using static UnityEngine.Debug;

Debug.Log(“サンプル”);

InvokePattern

MonobehaviourクラスのInvokeメソッドを使って遅延した処理を実装してみましょう。

Invoke

設定した時間(単位は秒)にメソッドを呼び出します。
例えば次のコードは、3秒後にHogeメソッドが呼び出されます。

Invoke("Hoge", 3f);

第1引数の“Hoge”は、引数なしのメソッド名を記述します。なのでHoge()メソッドを呼び出すわけですが、マジックワード(コード中に文字列を直接記述すること)があると綴りミスでもコンパイルエラーにならないため、メソッド名を文字列で取得することができるnameofメソッドに置き換えるといいでしょう。

Invoke(nameof(Hoge), 3f);

実行できるコードにまとめると次のようになります。

MonobehaviourPattern.cs

using System.Diagnostics;
using UnityEngine;
using static UnityEngine.Debug;

public class MonobehaviourPattern : MonoBehaviour
{
    readonly Stopwatch stopwatch = new Stopwatch();

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            stopwatch.Start();
            Invoke(nameof(Hoge), 3f);
        }
    }

    void Hoge()
    {
        stopwatch.Stop();
        Log("MonobehaviouPattern " + stopwatch.Elapsed);
    }
}

結果

MonobehaviouPattern 00:00:02.9610232

CoroutinePattern

Coroutine

メソッドを呼び出しですが、今度はコルーチンを使ってみます。
コルーチンは、遅延実行が呼ばれるメソッド側で可能です。

StartCoroutine(Hoge());

呼び出されるメソッド側に、次のコードを記述するとここに到達したら、3秒経過するまで次のコードは実行しないようにすることができます。ただし、その間、スレッドがロックされることはないにで、他のコードに制御が渡され(例えば、他のスクリプトのUpdateメソッド)継続して処理を進めることができます。

yield return new WaitForSeconds(3f);

実行できるコードにまとめると次のようになります。

using System.Collections;
using System.Diagnostics;
using UnityEngine;
using static UnityEngine.Debug;

public class CoroutinePattern : MonoBehaviour
{
    readonly Stopwatch stopwatch = new Stopwatch();

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            stopwatch.Start();
            StartCoroutine(Hoge());
        }
    }

    IEnumerator Hoge()
    {
        yield return new WaitForSeconds(3f);
        stopwatch.Stop();
        Log("CoroutinePattern " + stopwatch.Elapsed);
    }
}

結果

CoroutinePattern 00:00:02.9782285

UpdatePattern

Update, Time.deltatime

Updateメソッドは、1フレームごとに実行されるメソッドになります。
60fpsだと、1秒間に60回実行されます。

次のコードでは、タイマー値をTime.deltaTime分(1フレームあたりの処理時間。つまりUpdateメソッドが実行される間隔)足し込んでいき、3秒が経過すると、メソッドが呼び出されるようにしています。

float timer;

void Update()
{
    timer += Time.deltaTime;

    if (timer >= 3f)
    {
        Hoge();
    }
}

実行できるコードにまとめると次のようになります。

using System.Diagnostics;
using UnityEngine;
using static UnityEngine.Debug;


public class UpdatePattern : MonoBehaviour
{
    readonly Stopwatch stopwatch = new Stopwatch();

    float timer;

    bool IsStart;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            stopwatch.Start();
            IsStart = true;
        }

        if (!IsStart)
        {
            return;
        }

        timer += Time.deltaTime;

        if (timer >= 3f)
        {
            Hoge();
            timer = -100000;
        }
    }

    void Hoge()
    {
        stopwatch.Stop();
        Log("UpdatePattern " + stopwatch.Elapsed);
    }
}

結果

UpdatePattern 00:00:02.9597800

TaskPattern

Task, await, async

Taskは、簡易に非同期プログラムが作成できるクラスになります。

非同期とは、コードの処理が順番に実行される同期処理と違い、並行して実行されるコードになります。ちなみに1つのプロジェクトで複数のスクリプトが実行されたとき、Updateメソッドが同期処理にように実行されると勘違いしてしまいそうですが、これはそれぞれのUpdateが順番に実行されているためで、同時にUpdataメソッドが実行されているわけではありません。

呼び出し側のコード

引数は、デリゲートになります。(今回は、ラムダ式を使ってメソッドを呼び出します。無名メソッドでも構いません)

Task.Run(() => HogeAsync());

呼び出されるコード

await就職誌をつけることで処理待ちのメソッドを表しています。
メソッド名には、async就職誌をつけて同期処理メソッドであることを明示します。(しないとエラーになりますが・・)
また、戻り値は、Task型にしておきます。

async Task HogeAsync()
{
    await Task.Delay(3000);
    Log("TaskPattern “);
}

実行できるコードにまとめると次のようになります。

using System.Diagnostics;
using System.Threading.Tasks;
using UnityEngine;
using static UnityEngine.Debug;

public class TaskPattern : MonoBehaviour
{
    readonly Stopwatch stopwatch = new Stopwatch();

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            stopwatch.Start();
            Task.Run(() => HogeAsync());
        }
    }

    async Task HogeAsync()
    {
        await Task.Delay(3000);
        stopwatch.Stop();
        Log("TaskPattern " + stopwatch.Elapsed);
    }
}

結果

TaskPattern 00:00:03.0210585

DoTween

アセットストアの無料ツールを使って遅延実行を実現してみます。

DOVirtual.DelayedCall

遅延ようにメソッドが用意されています。

呼び出し側のコード

第1引数に遅延させる秒数、第2引数にデリゲートを記述します。(今回は、ラムダ式を使っていますが、無名メソッドでも実現できます。

DOVirtual.DelayedCall(3f, () => Hoge());

呼び出されるコード

void Hoge()
{
    Log("DoTweenPattern ";
}

実行できるコードにまとめると次のようになります。

using System.Diagnostics;
using DG.Tweening;
using UnityEngine;
using static UnityEngine.Debug;

public class DoTweenPattern : MonoBehaviour
{
    readonly Stopwatch stopwatch = new Stopwatch();

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            stopwatch.Start();
            DOVirtual.DelayedCall(3f, () => Hoge());
        }
    }

    void Hoge()
    {
        stopwatch.Stop();
        Log("DoTweenPattern " + stopwatch.Elapsed);
    }
}

結果

DoTweenPattern 00:00:03.0138715

UniRxPattern

アセットのUniRxを使って非同期プログラムで実現してみましょう。

Observable.Timer

遅延ようにメソッドが用意されています。

呼び出し側のコード

Timerメソッドで、3000ms後に遅延実行されるように指示します。続けてメソッドチェーンで、Hogeメソッドが実行されるように記述します。

Observable.Timer(TimeSpan.FromMilliseconds(3000))
        .Subscribe(_ => Hoge());

呼び出されるコード

void Hoge()
{
    Log("UniRxPattern ");
}

実行できるコードにまとめると次のようになります。

using System;
using System.Diagnostics;
using UniRx;
using UnityEngine;
using static UnityEngine.Debug;

public class UniRxPattern : MonoBehaviour
{
    readonly Stopwatch stopwatch = new Stopwatch();

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            stopwatch.Start();
            Observable.Timer(TimeSpan.FromMilliseconds(3000))
                    .Subscribe(_ => Hoge());
        }
    }

    void Hoge()
    {
        stopwatch.Stop();
        Log("UniRxPattern " + stopwatch.Elapsed);
    }
}

結果

DoTweenPattern 00:00:03.0138715

2021年5月5日C#,Unity

Posted by hidepon