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

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

■ [Unity Native Gallery Plugin](https://github.com/yasirkula/UnityNativeGallery)

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

## バージョン情報

| 諸々名前 | バージョン |
|:-:|:-:|
|  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)」に変更します。
![NativeGalleryWriteSS.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/15c7ff81-3163-02fa-0f45-297e994202bf.png)

**■ ios**
どうやら、Permissionなどの記述は自動的に行ってくれるようです。
下記が`Project Settings`に追加されていました。
![NativeGallerySettingSS.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/3c9152d0-32fa-38ea-9449-91c52e4a3d3a.png)


## サンプルコード

```C#
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です。
スクショのタイミングで若干画面が止まりますが、まあ許容範囲内でしょう。
![SSTest.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/cc684588-3b9c-2140-d68d-6c7b1aa967df.gif)

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

![59206.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/e628a41a-03b4-ecfa-5bf3-60e80efcbb40.jpeg)

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

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

##参考リンク
[【Unity】iOS の写真や Andoid のギャラリーに画像や動画を保存できる「Unity Native Gallery Plugin」紹介](https://baba-s.hatenablog.com/entry/2017/12/26/210500)
[【Unity】スクリーンショットを保存する](https://nekojara.city/unity-screenshot)
[]()