Unityのプレイ画面を自動録画したい

# はじめに

Unityでゲーム開発中に起きたバグを動画に記録し、チーム内に共有したい場合があります。

しかしUnityRecorderが標準で利用するH.264コーデックでは

録画ファイルのサイズが大きく記録用には向きません。

記録用には小さいファイルサイズで保存できるH265(HEVC)コーデックを使いたいです。

ここではUnityRecorderからFFmpegエンコーダーを介してH265(HEVC)コーデックを使って

**Unity Editor上でプレイ動画を自動収録する方法**を説明します。

**満たしたい要件**
| **フレームレート** | 60FPS |
| --- | --- |
| **画質** | ノイズが少ない |
| **ファイルサイズ** | 小さい |
| **フォーマット** | mp4 |

**バージョン**
| **名前** | **バージョン** |
| --- | --- |
| **OS** | Windows 11 |
| **Unity** | 6 (60000.0.23f1) |
| **FFmpeg** | 7.1 |
| **Unity Recorder** | 5.1.2 |

# 手順
## FFmpegの準備

1. [FFmpeg公式サイト](https://www.ffmpeg.org/download.html#build-windows)>Windowsダウンロードリンク ([**Windows builds from gyan.dev**](https://www.gyan.dev/ffmpeg/builds/))を開きます。
2. release builds > ffmpeg-release-full.7zをダウンロードして展開します。
3. binの中身をAssets/ExternalAssets/FFmpeg/Win内にコピーします。

## Unity Recoder カスタムレコーダーの準備

1. Unity起動＞メニュー＞Window＞Package Managerを起動します。
2. Unity RegistryからRecorderをインストールします。
3. Samplesタブ>Custom Encoder:FFmpeg>Importでカスタムエンコーダースクリプトを追加します。
![image (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3962875/b09d7778-6fab-a9a0-dc4d-aecd30d33462.png)

4. Assets/ExternalAssets/Recorder/に配置します。

## 録画スクリプトScreenRecorderの準備

本来カスタムレコーダーのサンプルスクリプトであるFFmpegEncoderSettings.csを修正していくのですが、面倒なのでパスだけ外部から設定できるようにします。
あとは録画スクリプトを用意してプログラムから呼び出せるようにします。

1. FFmpegの設定を渡せるようにスクリプトAssets/ExternalAssets/Recorder/5.1.2/Custom Encoder_ FFmpeg/FFmpegEncoderSettings.csを以下のように修正します。
   ```csharp
   public sealed class FFmpegEncoderSettings : IEncoderSettings, IEquatable<FFmpegEncoderSettings>
   {
       [SerializeField] string ffmpegPath;
       // 修正前
       //public string FFMpegPath => ffmpegPath;
       // 修正後
       public string FFMpegPath{set => ffmpegPath = value; get => ffmpegPath;}
       // ...
    }   
   ```
2. 録画スクリプトEditorScreenRecorder.csを以下のように用意します。
FFmpegEncoderSettingsをMovieRecorderSettingsに渡し、MovieRecorderSettingsをRecorderControllerSettingsに渡して初期化します。

   ```csharp
   public class EditorScreenRecorder : IScreenRecorder
   {
    private RecorderControllerSettings _controllerSettings;
    private RecorderController _controller;
    private ScreenRecorderSettings _settings;
    
	public void Initialize(ScreenRecorderSettings settings)
    {
        _settings = settings;
        
        // FFmpegの設定
        var ffmpegSettings = new FFmpegEncoderSettings
        {
            Format = GetOutputFormat(),
        };
        ffmpegSettings.FFMpegPath = settings.FFmpegPath;
        
        // レコーダーの設定
        var movieSettings = ScriptableObject.CreateInstance<MovieRecorderSettings>();
        movieSettings.name = MovieRecorderSettingsName;
        movieSettings.Enabled = true;
        movieSettings.ImageInputSettings = new GameViewInputSettings
        {
            OutputWidth = _settings.resolution.x,
            OutputHeight = _settings.resolution.y
        };
        movieSettings.EncoderSettings = ffmpegSettings;
        _settings.GenerateFileName();
        movieSettings.OutputFile = _settings.OutputFilePath;
        
        // コントローラーの設定
        _controllerSettings = ScriptableObject.CreateInstance<RecorderControllerSettings>();
        _controllerSettings.AddRecorderSettings(movieSettings);
        _controllerSettings.ExitPlayMode = true;
        _controllerSettings.SetRecordModeToManual();
        _controllerSettings.FrameRatePlayback = FrameRatePlayback.Constant;
        _controllerSettings.FrameRate = settings.frameRate;
    }
    // 録画を開始する
    public void StartRecording()
    {
        _controller = new RecorderController(_controllerSettings);
        _controller.PrepareRecording();
        _controller.StartRecording();
        Debug.Log($"録画開始 出力先: {_settings.OutputDirPath}");
    }
   }
   ```

1. 制御用のスクリプトScreenRecorderControllerを用意してHierarchyにあるオブジェクトにアタッチします。
   ```csharp
   public class ScreenRecorderController : MonoBehaviour
   {
    [SerializeField]
    private ScreenRecorderSettings settings;
    public IScreenRecorder ScreenRecorder{ get; private set; }

    private void Awake()
    {
        InitializeScreenRecorder();
    }
    private void Start()
    {
        // 起動時に自動録画
        if(settings.allowAutoRecordOnStart) ScreenRecorder.StartRecording();
    }
    private void InitializeScreenRecorder()
    {
   #if UNITY_EDITOR        
        ScreenRecorder = new EditorScreenRecorder();
        ScreenRecorder.Initialize(settings);
   #endif        
    }
   }
   ```
2. これでPlayすると自動的に録画されStopすると録画が停止します。

フルのコードは[**GitHubScreenRecorder**](https://github.com/KokiShoji/ScreenRecorder/tree/main/Assets/ExternalAssets/ScreenRecorder)に置いていますのでご参照ください
こちらでは録画設定をScriptableObjectのScreenRecorderSettingsにまとめてあります。
また起動時に古いファイルを消すスクリプトScreenRecordingFileCleanerも追加してあります。

# パフォーマンス比較

以下①と②のケースを以下の設定で録画し、比較した結果を説明します。

| **録画時間** | 10分 |
| --- | --- |
| **FPS** | 60 |
| **解像度** | 1920×1080 |

**ケース①** UnityRecorder(H264)を手動で録画する方法
**ケース②** ScreenRecorder( UnityRecorder+FFmpeg+H265)で自動で録画する方法 

| **手間** | ②のほうがGood |
| --- | --- |
| **画質** | ①と②は同程度 |
| **ファイルサイズ** | ②は①の1/4程度 |

**画質**

①と②は同程度でした。
|①|②|
|---|---|
|![RecorderDefaultjpg.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3962875/393c1ef0-ae16-fa86-3bd7-fe5108c6ed4a.jpeg)|![NVidiaHEVC.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3962875/900cfe1f-6c52-57fc-8aee-3f5cc31c5853.jpeg)|

**ファイルサイズ**
H265とH264のほぼ1/4のファイルサイズに抑えられています。

| **コーデック** | **ファイルサイズ** |
| --- | --- |
| H264 | 57.8MB |
| H.265(HEVC） | 15.1MB |

# 結論

- Unity開発中にプレイ動画を残してチームで共有することはバグ調査の効率化に有用です。
- Unity Recoder+FFmpeg+H265でより画質を維持しつつて少ないファイルサイズで動画を記録できます。
- ScreenRecorderのスクリプトでPlay時に自動で保存できるようにしました。
- フルのコードは[GitHubScreenRecorder](https://github.com/KokiShoji/ScreenRecorder/tree/main/Assets/ExternalAssets/ScreenRecorder)にあります。
- お役に立てたら幸いです！