tonari note

オンラインゲームエンジニアの雑記

Unity 2017のasync/awaitについて調べてみた

お久しぶりです。
ちょっとUnity 2017のasync/awaitで遊んだところ、割と面白い&罠が多いような気がしたので、久しぶりに重い腰を上げて記事にしてみました。

まず、MonoBehaviourの予約?メソッドのStartとかUpdateでasync/awaitを使うことができます。

public class TaskSample1 : MonoBehaviour
{
    private ILogger _logger = Debug.unityLogger;

    public async Task Start()
    {
        _logger.Log("Start 1: " + Time.realtimeSinceStartup);
        await Task.Delay(TimeSpan.FromMilliseconds(1000));

        // ちゃんと呼ばれるけど、1秒間分のUpdateが呼ばれた後に呼ばれる
        _logger.Log("Start 2: " + Time.realtimeSinceStartup);
    
        // GameObjectも触れる
        var go = new GameObject();
        await Task.Delay(TimeSpan.FromMilliseconds(1000));
        go.SetActive(false); // ちゃんと1秒後に消える
    }

    public async Task Update()
    {            
        // Start 2よりも先に呼ばれる場合もある
        _logger.Log("Update 1: " + Time.realtimeSinceStartup);

        await Task.Delay(TimeSpan.FromMilliseconds(1000));

    
        // 当然Update 1の1秒後に呼ばれるし、次のフレームのUpdate 1の方が先に呼ばれる場合もある
        _logger.Log("Update: " + Time.realtimeSinceStartup);
    }
} 

なかなか便利そう。

ですが、おそらく内部では

1フレ目: Start();
2フレ目: Update();
3フレ目: Update();
4フレ目: Update();
...以下略

されているだけなので、これらのメソッドで起こる例外はUnity側でハンドリングされません。
つまり、以下のような場合でもError Pauseが効かなくなります。

public class TaskErrorSample1 : MonoBehaviour
{
    public async Task Start()
    {        
        // 無視される
        throw new Exception();
    }
}