【Unity(C#)】UIを画面に映さずにスクリーンショットし、ios・Androidそれぞれの写真アプリに保存


はじめに

ARなどでUIを非表示にしてスクショを取りたい場面があるかと思います。
今回はいろいろ調べて下記アセットに辿り着きました。

Unity Native Gallery Plugin

自前で用意することがほとんどなくなる神ライブラリだったので使い方をメモします。

バージョン情報

諸々名前 バージョン
Unity 2019.4.8f1(LTS)
UniTask 2.2.4
ARFoundation 4.1.1
ARCore XR Plugin 4.1.1
ARCore Kit Plugin 4.1.1
AR Subsystems 4.1.1
XR Plugin Management 3.2.16
Unity Native Gallery Plugin 1.6.4

下準備

■ Android
「Player Settings」で「Write Permission」を「External (SDCard)」に変更します。

■ ios
どうやら、Permissionなどの記述は自動的に行ってくれるようです。
下記がProject Settingsに追加されていました。

サンプルコード

using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// スクショ
/// 適当なオブジェクトにアタッチ
/// </summary>
public class SaveScreenShot : MonoBehaviour
{
    [SerializeField] private Button _ssButton;
    [SerializeField] private Camera _camera;

    void Start()
    {
        _ssButton.onClick.AddListener(SaveScreenShotToGallery);
    }

    private void OnDestroy()
    {
        _ssButton.onClick.RemoveListener(SaveScreenShotToGallery);
    }

    /// <summary>
    /// ボタンに登録するスクショ処理
    /// </summary>
    private void SaveScreenShotToGallery()
    {
         var ct = this.GetCancellationTokenOnDestroy();
         SaveScreenShotToGalleryAsync(ct).Forget();
    }

    /// <summary>
    /// スクショを作成して保存する
    /// </summary>
    private async UniTask SaveScreenShotToGalleryAsync(CancellationToken ct)
    {
        //任意のフレームの描画処理が終わるまで待つ
        await UniTask.WaitForEndOfFrame(ct);

        //Cameraの描画領域をRenderTextureとして取り出す
        var rt = new RenderTexture(_camera.pixelWidth, _camera.pixelHeight, 24);
        var prev = _camera.targetTexture;
        _camera.targetTexture = rt;
        _camera.Render();
        _camera.targetTexture = prev;
        RenderTexture.active = rt;

        var screenShot = new Texture2D(
            _camera.pixelWidth,
            _camera.pixelHeight,
            TextureFormat.RGB24,
            false);
        screenShot.ReadPixels(new Rect(0, 0, screenShot.width, screenShot.height), 0, 0);
        screenShot.Apply();

        var date = DateTime.Now.ToString("yyyyMMdd");

        //CameraのRenderTextureを元に画像を作成して保存
        NativeGallery.SaveImageToGallery(screenShot, "GalleryTest", $"{date}.png" );
    }
}

保存先には指定した名前のアルバムが作成されます。
アプリをアンインストールしてもアルバムと写真は残ります。

任意のカメラのRenderTextureをスクショに利用しています。
これにより、Screen Space OverlayのUIはスクリーンショットに映りません。

なぜ映らないのか、それっぽい言及があったので載せときます。

The 'overlay' mode doesn't go through the normal render pipeline so it's not injected into a normal camera render.
【引用元】:https://forum.unity.com/threads/render-a-canvas-to-rendertexture.272754/

デモ

赤がWorld SpaceのUI、白がCubeです。
スクリーンショットと書かれたボタンはScreen Space OverlayのUIです。
スクショのタイミングで若干画面が止まりますが、まあ許容範囲内でしょう。

下記が実際に保存された画像です。
Screen Space OverlayのUI以外が画面に映ります。

ネイティブのUIなども映らないので通知などに邪魔されることもなく便利ですね。

おわりに

動画もUnity Native Gallery Pluginで保存できるらしいですが、
そもそも動画を書き出す処理が面倒なのでそこは自前かアセットの力を借りる必要がありそうです。

参考リンク

【Unity】iOS の写真や Andoid のギャラリーに画像や動画を保存できる「Unity Native Gallery Plugin」紹介
【Unity】スクリーンショットを保存する