Unityで遅延処理をする方法色々

2016-11-22 (lastmod: 2021-06-15) /Development #Unity

Unityは遅延処理を実現する方法がたくさんあってどれにしようか迷う事が多いのでメモメモ。


Invokeを使う方法

一番簡単に実装できる。けど引数を渡す事ができないのが難点。

void Start()
{
  Invoke("DelayMethod", 1.0f);
}

private void DelayMethod()
{
  Debug.Log("Invoke");
}

StartCoroutineを使う方法

StartCoroutineはコルーチンの中で一定時間中断する処理を実行する。

呼び出される関数の方は内部でyield return を実行して IEnumerator のインスタンスを返す必要がある。

StartCoroutineに渡すメソッドには遅延時間を引数として渡せるようにしておくと便利。

void Start()
{
  Coroutine coroutine = StartCoroutine("DelayMethod", 1.0f);
}

private IEnumerator DelayMethod(float waitTime)
{
  yield return new WaitForSeconds(waitTime);
  Debug.Log("StartCoroutine");
}

キャンセルしたい場合には Coroutine coroutine = StartCoroutine() で返ってくるコルーチンに StopCoroutine (coroutine); を実行する事で止める事ができる。

また、下記のように仲介するメソッドを一つ作成しておけばアロー関数で書くことができるようになるのでさらに便利。

void Start()
{
  Coroutine coroutine = StartCoroutine(DelayMethod(1.0f, () => {
    Debug.Log("StartCoroutine");
  }));
}

private IEnumerator DelayMethod(float waitTime, Action action)
{
  yield return new WaitForSeconds(waitTime);
  action();
}

さらにIEnumeratorのメソッドの方に引数を渡す処理を書けば intstring などの引数を渡すことができるようになるのでもっと便利。

void Start()
{
  Coroutine coroutine = StartCoroutine(DelayMethod(1.0f, (int id) => {
    Debug.Log("StartCoroutine: "+id);
  }, 0));
}

private IEnumerator DelayMethod<T>(float waitTime, Action<T> action, T t)
{
  yield return new WaitForSeconds(waitTime);
  action(t);
}

Taskを使う方法

Task は非同期で処理を実行してくれる。

使うにはまず System.Threading.Tasks を読み込む必要がある。

using System.Threading.Tasks;

await を使用して待機させるには、実行されるメソッドに async が付与されている必要がある。

単純に1秒待たせたいだけならこんな感じで書ける。

private async void DelayMethod()
{
  await Task.Delay(1000);
  Debug.Log("DelayMethod");
}

自分でメソッドを作りたい場合にはこんな感じで書く。

async void Start()
{
  await DelayMethod();
}

private async Task DelayMethod()
{
  await Task.Delay(1000);
  Debug.Log("DelayMethod");
}

引数を渡したり返したりもできる。

async void Start()
{
  int id = await DelayMethod(1);
}

private async Task<int> DelayMethod(int id)
{
  await Task.Delay(1000);
  Debug.Log("DelayMethod: " + id);
  return id;
}

Task.Run とアロー関数を利用して無名関数で非同期処理を利用する事も可能。

この場合はasyncではないメソッドから呼び出す事が可能。

Task.Run(async () => {
  await Task.Delay(1000);
  print("Task.Run");
});

ラッパークラスを使う方法

また、小規模なプロジェクトで毎回色々処理を書くのが面倒な時などには、前述のコルーチンの処理を使用した自分用のオレオレラッパークラスを作成する方法もある。

DelayUtil.cs

ラッパークラスを使用すると下記のように簡単に記載できるようになるのでコードの可読性も上がるし、カスタマイズすれば自分好みの使用方法で遅延処理を実行する事ができるようになる。

Coroutine coroutine = DelayUtil.Delay (1.0f, (int id) => {
  Debug.Log ("DelayUtil.Delay: "+id);
}, 10);

この例ではラッパークラスの中でGameObjectを作成し、その中でコルーチン処理を行っているので、キャンセルを実行したい場合には同じくラッパークラスのキャンセル用の関数DelayUtil.Stop(coroutine);を呼び出す必要がある。


MonoBehaviourを拡張する方法

また、拡張メソッドをモリモリ使っているようなプロジェクトの場合、MonoBehaviourを拡張する事でさらに簡単に書くこともできるようになる。

MonoBehaviorExtentsion.cs

MonoBehaviorExtentsion.csの中ではMonoBehaviourを拡張し、Delayというメソッドを追加して、内部で前述の例と同様に作成したDelayMethodを呼び出すようにしている。

Coroutine coroutine = this.Delay(1.0f, (int id) => {
  print("Delay: "+id);
}, 0);

DOTweenを使う方法

DOTweenを入れているプロジェクトの場合には、ラッパークラスや拡張を行わなくてもDOTweenで用意されている関数を利用して遅延処理を行う事ができる。

DOVirtual.DelayedCall (1.0f, () => {
  print ("DOVirtual.DelayedCall");
});

UniRxを使う方法

またUniRxを入れているプロジェクトの場合にも同様にUniRxに用意されている関数を利用して遅延処理を行う事ができる。

TimerTimerFrameを使用して遅延させSubscribeで実行する方法と

Timerを使用する方法:

Observable.Timer(TimeSpan.FromSeconds(1.0f))
  .Subscribe(_ => {
    print("UniRx Observable.Timer");
  });

TimerFrameを使用する方法:

Observable.TimerFrame(1)
  .Subscribe(_ => {
    print("UniRx Observable.TimerFrame");
  });

Delayを使用する方法がある。

Delayを使用する方法:

Observable.Return(Unit.Default)
  .Delay(TimeSpan.FromSeconds(1.0f))
  .Subscribe(_ => {
    print("UniRx Observable.Delay");
  });

Delayを使用する方法(引数を渡したい時):

Observable.Return(0)
  .Delay(TimeSpan.FromSeconds(1.0f))
  .Subscribe((int id) => {
    print("UniRx Observable.Delay: "+id);
  });

  • 2018-09-04 追記: UniRxの記述を追加しました。
  • 2021-06-15 追記: 非同期処理の記述を追加しました。
Profile

石原 悠 / Yu Ishihara

デザインとプログラミングと編み物とヨーグルトが好きです。