コルーチン処理をキャンセル処理を含むUniTaskに置換する

Unity Learnにあるこのプロジェクト,キャラクターが死ぬと死ぬアニメーションの途中で
GameOver画面が表示されてしまいます.

learn.unity.com

f:id:xrdnk:20200413220057g:plain

チュートリアルではTime.deltaTimeを使って間を入れていますが,
ここではコルーチン処理を使った場合と,UniTaskを使った場合にしてみます.

GameOverManager.cs 変更前

using UnityEngine;

public class GameOverManager : MonoBehaviour
{
    public PlayerHealth playerHealth;

    Animator anim;

    void Awake()
    {
        anim = GetComponent<Animator>();
    }

    void Update()
    {
        if (playerHealth.currentHealth <= 0)
        {
            anim.SetTrigger("GameOver");
        }
    }
}

このチュートリアルのアセットはもうAsset Storeにない

assetstore.unity.com

このチュートリアルのアセット,実はもうAsset Storeにないので,ここではもう入手できません.
入手するには誰かのGitHubからcloneするしかない.

自分は以下からcloneしました.

github.com

コルーチン処理の場合

using UnityEngine;
using System.Collections;  // IEnumeratorが使えるようにするために必要

public class GameOverManager : MonoBehaviour
{
    public PlayerHealth playerHealth;

    Animator anim;

    void Awake()
    {
        anim = GetComponent<Animator>();
    }

    void Update()
    {
        if (playerHealth.currentHealth <= 0)
        {
            // コルーチン版
            StartCoroutine(GameOver());
        }
    }

    // コルーチン版ゲームオーバー処理
    IEnumerator GameOver()
    {
        yield return new WaitForSeconds(2f); 
        anim.SetTrigger("GameOver");
    }
}

2秒待ってから実行してる.

f:id:xrdnk:20200413221957g:plain

コルーチン処理は以下の記事を読むと良い.まとまっている.各所でよく取り上げられている.
developers.wonderpla.net

(上記の記事,つい最近リンクが変わっているため,昔のリンクだと飛ばなくなっている
昔のリンク: http://developer.wonderpla.net/entry/blog/engineer/Unity_Co-routine/

UniTask

UniTaskとは

これを読めば完結.

www.slideshare.net

実際に使ってみる.

とりあえずasync/awaitに置き換える

using UnityEngine;
using UniRx.Async; // UniTaskのために必要

public class GameOverManager : MonoBehaviour
{
    public PlayerHealth playerHealth;

    Animator anim;

    void Awake()
    {
        anim = GetComponent<Animator>();
    }

    void Update()
    {
        if (playerHealth.currentHealth <= 0)
        {
            // UniTask版
            GameOverAsync();
        }
    }

    // UniTask版ゲームオーバー処理
    async UniTask GameOverAsync()
    {
        await UniTask.Delay(2000);
        anim.SetTrigger("GameOver");
    }
}

実際にプレイしてみるとコルーチン処理の時と同じようになります.ただ,問題点があります.

無限に処理続けている

プレイをやめると以下のWARNが出ます.UniTaskには注意点があります.

f:id:xrdnk:20200413222922p:plain

qiita.com

UniTaskはシーンの切り替えや、オブジェクトの破棄では止まらない

裏でずっと続いてしまう可能性があるため,キャンセル処理を入れる必要があります.

それでは,キャンセル処理を含むUniTaskで書いてみます.

キャンセル処理を含むUniTask版

using UnityEngine;
using UniRx.Async; // UniTaskのために必要
using UniRx.Async.Triggers; // GetCancellationTokenOnDestroy()のために必要
using System.Threading; // CancellationTokenのために必要

public class GameOverManager : MonoBehaviour
{
    public PlayerHealth playerHealth;

    Animator anim;

    void Awake()
    {
        anim = GetComponent<Animator>();
    }

    void Update()
    {
        if (playerHealth.currentHealth <= 0)
        {
            // キャンセル処理込みUniTask版
            // 「this.GetCancellationTokenOnDestroy()」はオブジェクトの破棄で関数を止めるために必要 
            // Forget()をチェーンすることで警告を出さないようにする
            GameOverAsync(this.GetCancellationTokenOnDestroy()).Forget();
        }
    }

    // キャンセル処理含むUniTask版ゲームオーバー処理
    async UniTask GameOverAsync(CancellationToken cancellationToken)
    {
        await UniTask.Delay(2000, cancellationToken: cancellationToken);
        anim.SetTrigger("GameOver");
    }
}

いい感じにできました.

終わりに

UniRxだけでなく,Unity非同期処理,UniTaskも完全に理解したいなあ.難しいです.
Unity Learn Premiumが今某ウイルスによって無料キャンペーンやってるので時間の合間にやっているのですが,
なかなか良い題材がたくさんある,いい宝庫であることに今気づいた….早くやればよかったなあ.

unity.com

この記事の題材になっているコースはもとから無料です.

参考資料

【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう

Unite2018でやってたセクション.参加した覚えがある.

www.youtube.com

www.slideshare.net

UniTask – Unityでasync/awaitを最高のパフォーマンスで実現するライブラリ

UniTask作成者による説明記事.

tech.cygames.co.jp

Unity 非同期完全に理解した勉強会

connpass.com

UniTask機能紹介

qiita.com