【Unity】UnityEditor上でスクリーンショットを撮る方法、UnityRecorderとスクリプト

## 概要
UnityEdito上でスクリーンショットを取る方法について説明します。
大きく分けて**Unity Recorder**を使う方法と、**スクリーンショットを撮るスクリプト**を書いて使う方法があります。
この記事ではスクリプトを使う場合のサンプルコードも掲載しています。

※2018年に執筆した時、備忘録程度に雑に描いた記事なのですが…思ったよりも使ってる人が多かったので2022年7月に大幅な記事のリライトを行いました。

## 【1-1】Unity Recorderの説明
Unityには<a href="https://docs.unity3d.com/ja/Packages/com.unity.recorder@2.6/manual/index.html">Unity Recorder</a>というUnityが作ってる公式のアセットがあります。
こちらを使う事でUnityEditor上で再生されたGameタブをイメージ(JPEG,PNG,EXR)、ムービー(MP4,WebM,QuickTime)、アニメーションクリップ形式、GIF、オーディオ(WAV)、AOV(JPEG,PNG,EXR)で保存できます。

### 【1-2】Unity Recorderの導入

![2022-07-01_13h36_47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149449/3d9599fb-03f8-687f-6526-2b27dc52cafe.png)
1.UnityEditorのメニューから「Window」→「PackageManager」を選択して、PackageManagerを開きます。
![2022-07-01_13h38_56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149449/9dfc0b01-d123-261d-9580-9cc85d782750.png)
2.左上のタブを「Unity Registry」に変更した後、右上の検索欄に「Unity Recorder」と入力します。
![2022-07-01_13h42_56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149449/aaac479a-79fc-6351-fd05-c24f8949a731.png)
3.「Unity Recorder」の詳細が表示されてる状態で右下の「Install」を押すことで導入完了です。

### 【1-3】UnityRecorderの利用

![2022-07-01_13h45_18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149449/e761cb17-4c3a-65de-a731-b1380c73abba.png)
「Window」→「General」→「Recorder」→「RecorderWindow」を開くとRecorderの設定画面が開けます。
![UnityRecorder_HOW.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149449/6a40a60f-0f45-f011-fed3-252b26f1ba54.png)
大まかな使い方の流れは…
1.「Add Recorder」から撮影したいメディアを追加。
2.設定Windowでメディアの設定を行う。
3.左上の再生ボタンを押すとUnityが実行されて撮影が始まる。

詳細なマニュアルについては下記リンクをご確認ください。
・<a href="https://docs.unity3d.com/ja/Packages/com.unity.recorder@2.6/manual/RecordingRecorderWindow.html">Recorder ウィンドウからのレコーディング</a>


## 【2-1】スクリーンショットを撮るスクリプトを書く

```c#:ScreenShot.cs
using System;
using System.Collections;
using System.IO;
using UnityEngine;

namespace ScreenshotUtility
{
    //タイムスタンプの書式設定
    public enum TIME_STAMP
    {
        MMDDHHMMSS,
        YYYYMMDDHHMMSS,
    }

    //背景色の設定
    public enum BACK_GROUND_COLOR
    {
        Alpha, //透過PNG
        CustomColor, //カスタムカラー
        Skybox, //スカイボックス
    }

    //解像度の設定
    public enum SCREEN_SIZE_PIXEL
    {
        //1:1
        p256x256,
        p512x512,
        p1024x1024,
        p2048x2048,
        p4096x4096,

        //16:9
        p1280x720, //HD
        p1920x1080, //FullHD
        p2560x1440, //2k
        p3840x2160, //4k
        CustomSize //指定したピクセルサイズで出力(下限32/上限4096)
    }

    public class ScreenShot : MonoBehaviour
    {
//UnityEditorのみで実行
#if UNITY_EDITOR
        [Header("撮影に使うカメラの割り当て")]
        [Tooltip("撮影に使うカメラを割り当ててください"), SerializeField]
        Camera _UseCamera;

        [Header("画像のサイズの設定")]
        [Space(10)]
        [Tooltip("ScreenSizePixelをCustomにするとCustomSizeの解像度が適用されます"), SerializeField]
        SCREEN_SIZE_PIXEL _screenSizePixel = SCREEN_SIZE_PIXEL.p1024x1024;

        [Tooltip("ScreenSizePixelがCustomになってる場合の解像度設定（下限32Pixel/上限4096Pixel）"), SerializeField]
        Vector2Int _customSize = new Vector2Int(1024, 1024);

        [Header("背景色の設定")]
        [Space(10)]
        [Tooltip("Alpha:背景透過 White:白 CustomColor:指定した色 Skybox:スカイボックス"), SerializeField]
        BACK_GROUND_COLOR _buckGroundColorType = BACK_GROUND_COLOR.Alpha;

        [Tooltip("BuckGroundColorTypeがCustomの場合の背景色"), SerializeField]
        UnityEngine.Color _customColor = UnityEngine.Color.green;

        [Header("ファイル名の書式設定")]
        [Space(10)]
        [Tooltip("タイムスタンプの書式設定"), SerializeField]
        TIME_STAMP _timeStampStyle = TIME_STAMP.MMDDHHMMSS;
        [Tooltip("ファイル名の先頭に付く文字列"), SerializeField]
        string _screenShotsTitle = "img";

        [Header("保存先のフォルダ名")]
        [Space(10)]
        [Tooltip("スクリーンショットを保存するフォルダ名 Assetsフォルダーの直下に配置されます"), SerializeField]
        string _screenShotFolderName = "ScreenShots";

        [Header("実行中の撮影キー")]
        [Space(10)]
        [Tooltip("Unity実行中にスクリーンショットを撮る場合のキーバインド")]
        public KeyCode _screenShotsKeybinding = KeyCode.F1;

        [Header("Consoleに保存先のパスのログを出力します")]
        [Space(10)]
        [Tooltip("チェックをすると、画像のファイル名と画像の保存先のパスのログの出力します"), SerializeField]
        bool _consoleLogIsActive = true;

        void Update()
        {
            if(Input.GetKeyDown(_screenShotsKeybinding))
            {
                getScreenShots();
            }
        }

        private bool NullCheck()
        {
            bool isNull = false;
            if(_UseCamera == null)
            {
                Debug.LogWarning("画像の書き出しに使用するカメラを ScreenSchotCamera に割り当ててください");
                isNull = true;
            }
            if(_screenShotsTitle == "")
            {
                Debug.LogWarning("ScreenShotsTitle に画像ファイルに付ける末尾の文字を入力してください");
                isNull = true;                
            }
            if(_screenShotFolderName == "")
            {
                Debug.LogWarning("ScreenShotFolderName にScreenShotの保存先のフォルダ名を入力してください");
                isNull = true;                
            }            
            return isNull;
        }
        
        [ContextMenu("スクリーンショットを撮影する")]
        public void getScreenShots()
        {
            //NullCheck
            if(NullCheck()){ return; }
            // Application.dataPath = ../Assets
            string path = UnityEngine.Application.dataPath + "/" + _screenShotFolderName + "/";
            StartCoroutine(imageShooting(path, _screenShotsTitle));
        }

        //撮影処理
        //第一引数 ファイルパス / 第二引数 タイトル
        private IEnumerator imageShooting(string path, string title)
        {
            yield return new WaitForEndOfFrame();

            //パスの作成
            imagePathCheck(path);
            string name = title + getTimeStamp(_timeStampStyle) + ".png";

            //元々の背景色をCache
            Color32 CacheColor = _UseCamera.backgroundColor;
            //背景色：透過
            if(_buckGroundColorType == BACK_GROUND_COLOR.Alpha)
            {
                _UseCamera.backgroundColor = new Color32(0, 0, 0, 0);
                _UseCamera.GetComponent<Camera>().clearFlags = CameraClearFlags.SolidColor;
            }
            //背景色：カスタム
            if(_buckGroundColorType == BACK_GROUND_COLOR.CustomColor)
            {
                _UseCamera.backgroundColor = _customColor;
                _UseCamera.GetComponent<Camera>().clearFlags = CameraClearFlags.SolidColor;
            }
            //背景色：スカイボックス
            if(_buckGroundColorType == BACK_GROUND_COLOR.Skybox)
            {
                _UseCamera.GetComponent<Camera>().clearFlags = CameraClearFlags.Skybox;
            }

            //スクショ作成
            //書き出しサイズの取得
            Vector2Int size = getScreenSizePixel2Int(_screenSizePixel);
            Texture2D screenShot = new Texture2D(size.x, size.y, TextureFormat.ARGB32, false);
            RenderTexture rt = new RenderTexture(screenShot.width, screenShot.height, 32);
            RenderTexture prev = _UseCamera.targetTexture;
            _UseCamera.targetTexture = rt;
            _UseCamera.Render();
            _UseCamera.targetTexture = prev;
            RenderTexture.active = rt;
            screenShot.ReadPixels(new Rect(0, 0, screenShot.width, screenShot.height), 0, 0);
            screenShot.Apply();
            byte[] bytes = screenShot.EncodeToPNG();
            //UnityEngine.Object.Destroy(screenShot);

            //書き込み
            File.WriteAllBytes(path + name, bytes);

            //ログの表示
            if(_consoleLogIsActive)
            {
                Debug.Log("Title: " + name);
                Debug.Log("Directory: " + path);
            }

            //カメラの背景色を元に戻す
            _UseCamera.backgroundColor = CacheColor;
            //Assetフォルダのリロード
            UnityEditor.AssetDatabase.Refresh();
        }

        //ファイルパスの確認
        private void imagePathCheck(string path)
        {
            if (Directory.Exists(path))
            {
                //Debug.Log("The path exists");
            }
            else
            {
                //パスが存在しなければフォルダを作成
                Directory.CreateDirectory(path);
                Debug.Log("CreateFolder: " + path);
            }
        }

        private Vector2Int getScreenSizePixel2Int(SCREEN_SIZE_PIXEL ScreenSize)
        {
            //デフォルト設定
            Vector2Int size = new Vector2Int(1024, 1024);
            switch(ScreenSize)
            {
                //1:1(正方形)
                case SCREEN_SIZE_PIXEL.p256x256:
                    size = new Vector2Int(256, 256);
                    break;                
                case SCREEN_SIZE_PIXEL.p512x512:
                    size = new Vector2Int(512, 512);
                    break;
                case SCREEN_SIZE_PIXEL.p1024x1024:
                    size = new Vector2Int(1024, 1024);
                    break;
                case SCREEN_SIZE_PIXEL.p2048x2048:
                    size = new Vector2Int(2048, 2048);
                    break;
                case SCREEN_SIZE_PIXEL.p4096x4096:
                    size = new Vector2Int(4096, 4096);
                    break;

                //16:9
                case SCREEN_SIZE_PIXEL.p1280x720:
                    size = new Vector2Int(1280, 720);
                    break;
                case SCREEN_SIZE_PIXEL.p1920x1080:
                    size = new Vector2Int(1920, 1080);
                    break;
                case SCREEN_SIZE_PIXEL.p2560x1440:
                    size = new Vector2Int(2560, 1440);
                    break;
                case SCREEN_SIZE_PIXEL.p3840x2160:
                    size = new Vector2Int(3840, 2160);
                    break;

                //CustomscreenSize
                case SCREEN_SIZE_PIXEL.CustomSize:
                    //UpperLimit
                    if(_customSize.x > 4096)
                    {
                        _customSize.x = 4096;
                        Debug.LogWarning("PixelSizeのXを4096に設定しました。");
                    }
                    if(_customSize.y > 4096)
                    {
                        _customSize.y = 4096;
                        Debug.LogWarning("PixelSizeのYを4096に設定しました。");
                    }
                    //UnderLimit
                    if(_customSize.x < 32)
                    {
                        _customSize.x = 32;
                        Debug.LogWarning("PixelSizeのXを32に設定しました。");
                    }
                    if(_customSize.y < 32)
                    {
                        _customSize.y = 32;
                        Debug.LogWarning("PixelSizeのYを32に設定しました。");
                    }
                    size = _customSize;
                    break;                

                //設定されてないSCREEN_SIZE_PIXELが選択された場合
                default:
                    Debug.LogWarning("設定されてないSCREEN_SIZE_PIXELが選択されました。");
                    Debug.LogWarning("解像度 " + size + " で書き出します。");
                    break;
            }
            return size;
        }

        //タイムスタンプ
        private string getTimeStamp(TIME_STAMP type)
        {
            string time;
            //タイムスタンプの設定書き足せます
            switch (type)
            {
                case TIME_STAMP.MMDDHHMMSS:
                    time = DateTime.Now.ToString("MMddHHmmss");
                    return time;
                case TIME_STAMP.YYYYMMDDHHMMSS:
                    time = DateTime.Now.ToString("yyyyMMddHHmmss");
                    return time;
                default:
                    time = DateTime.Now.ToString("yyyyMMddHHmmss");
                    return time;
            }
        }

#endif
    }
}
```

### 【2-2】プロジェクトへの導入

1.UnityEditorのProjectタブでメニューを出して「Create」→「C# Script」でScriptを作成します。
　Scriptのファイル名は必ず「ScreenShot」にしてください。別名なだとエラーになります。
2.作成したC#のファイルを開いて、全て削除した後に上記のサンプルコードを全て上書きします。
　using System;の行から } で終わってる行まで全てコピペしてください。
3.ScreenShotのスクリプトを適当なゲームオブジェクトにアタッチしてください。

### 【2-3】使い方
![howto.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149449/8de8580a-946f-1003-fa2b-38f47fa42e63.png)

1.UseCameraの部分に撮影に使うCameraを割り当ててください。
2.実行中の場合は F1キーで撮影 実行中じゃない場合も歯車アイコンからメニューを開いて「スクリーンショットを撮影する」を押すと撮影できます。

**ScreenSizePixel**
プリセットをいくつか用意しました。プリセットに無い場合はCustomSizeを選択して、CustomSizeの欄に画像の解像度を入力してください。
最大解像度は4096、最小解像度は32に制限してあります。

**背景色の設定**
背景色はAlpha(透明)、CustomColor(選択した色)、Skybox(スカイボックスを撮影する)の３種類から設定できます。

**ファイル名の書式設定**
タイムスタンプの形式と末尾の文字列を変更できます。
他の書式にする場合はソースコードを直接編集してください。

**保存先のフォルダ名**
Assetsフォルダ以下に配置されます。（存在しない場合は自動で作成されます）

## Gameタブの解像度を指定する方法

![gametabu.png](https://qiita-image-store.s3.amazonaws.com/0/149449/26fa7f89-2606-a6ac-f2fa-c2e9d5fbc633.png)
1.Gameタブのあの部分をクリックするとメニューが展開される。
2.＋から「Add New Item」を押す。
3.ウインドウでLabel (名前) Width＆Height (縦横の解像度)を設定する。
4.プリセットに追加されるので、任意の解像度にできる。

Vtuberの用途的には、Youtubeの基本の解像度を入れておくとよいと思います。
また 128x128 や 1024x1024 等も入れておくと、アイコン作成やテクスチャ作成時に便利です。

参考：<a href="http://tsubakit1.hateblo.jp/entry/2016/03/16/231329">【Unity】高解像度のゲーム画面向けにゲームビューを調整する</a>
参考：<a href="https://support.google.com/youtube/answer/6375112?co=GENIE.Platform%3DDesktop&hl=ja">Youtubeで推奨される解像度とアスペクト比</a>

## カメラを移動しながら撮影したい。

<a target="blank" href="https://qiita.com/Nekomasu/items/f195db36a2516e0dd460">【Unity】カメラ移動を制御するスクリプト</a>
上記を参考にしてみてください。キャラクターを撮影するプロジェクトが作れます。
※キー設定が被ってるのでよしなに。