白黒羊

UniTask.WaitForEndOfFrame + ScreenCaptureAsTexture でクラッシュする

ちゃんと UniTaskのReadmeに書いてありましたね。

await UniTask.WaitForEndOfFrame() is not equilavelnt to coroutine’s yield return new WaitForEndOfFrame(). Coroutine’s WaitForEndOfFrame seems to run after the PlayerLoop is done. Some methods that require coroutine’s end of frame(ScreenCapture.CaptureScreenshotAsTextureCommandBuffer, etc) does not work correctly when replace to async/await. In that case, use a coroutine.

https://github.com/Cysharp/UniTask

コルーチンの yield return new WaitForEndOfFrame() は PlayerLoopが終わってから実行されるようで、ScreenCapture.CaptureScreenshotAsTexture のような完全にフレームが終了するのを待つ必要があるメソッドではそちらを使うべきとのことです。

クラッシュしたコード

using Cysharp.Threading.Tasks;
using UnityEngine;

public class CaptureScreen
{
    private Texture2D _texture;

    public async UniTask CaptureWholeScreenAsync()
    {
        await UniTask.WaitForEndOfFrame();
        _texture = ScreenCapture.CaptureScreenshotAsTexture();
        var bytes = _texture.EncodeToJPG();
    }
}

修正したコード

using System.Collections;
using Cysharp.Threading.Tasks;
using UniRx;
using UnityEngine;

public class CaptureScreen
{
    private Texture2D _texture;

    public async UniTask CaptureWholeScreenAsync()
    {
        await RecordFrame().ToObservable();
        var bytes = _texture.EncodeToJPG();
    }

    private IEnumerator RecordFrame()
    {
        yield return new WaitForEndOfFrame();
        _texture = ScreenCapture.CaptureScreenshotAsTexture();
    }
}

備考

Unity 2020.2の Release Note に ScreenCapture.CaptureScreenshotAsTexture でクラッシュするバグを直したよ〜という記述があるので、古いバージョンでは UniTaskとか関係なくクラッシュするのかもしれないです。

sassembla/EditorScreenshotScript – GitHub Gist

What’s new in Unity 2020.2.0 – Unity

参考