【Unity】IProcessSceneWithReport.OnProcessSceneを使ってビルド時にシーン上の特定のGameObjectを削除する

Unityでシーンビルド時の処理を記述できる IProcessSceneWithReport.OnProccessScene の使い方をまとめます。

Unity2021.3.0f

はじめに

IProcessSceneWithReport.OnProcessScene を使用すると、シーンがビルドされる時の処理を記述することができます。
これにより、例えばシーンに配置したデバッグ用の GameObject を、リリースビルドの時にだけ削除することができそうです。
本記事ではこれの使い方についてまとめます。

docs.unity3d.com

なお参考までに関連する機能として、エディタでだけ存在する GameObject を作成できる EditorOnly タグがあります。
このタグをつけたGameObjectは、プレイヤービルドでは常に削除されます。

answers.unity.com

またもう一つ関連する機能として、ビルドの前後処理を挟めるIPreprocessBuildWithReportIPostprocessBuildWithReportもあります。

light11.hatenadiary.com

プレイヤービルド時に特定のGameObjectを削除する

それではこの機能を使って、プレイヤービルドの際にシーン上の特定の GameObject を削除するスクリプトを書いてみます。
以下はシーンのルートにあるGameObjectに、Debugという名前のものがあったらそれを削除するスクリプトです。

using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEngine.SceneManagement;

public sealed class Example : IProcessSceneWithReport
{
    public int callbackOrder => 0;

    public void OnProcessScene(Scene scene, BuildReport report)
    {
        // OnProcessScene はエディタで PlayMode に入る時にも呼ばれる
        // この場合には EditorApplication.isPlaying がtrueになるのでこれで判定する
        // マニュアルにはエディタ実行の際にはreport引数がnullになるという説明がされているが、
        // これだとシーンをアセットバンドルビルドした時にも当てはまってしまうので以下の判定の方がよさげ
        if (EditorApplication.isPlaying)
            return;
        
        Debug.Log("OnProcessScene: " + scene);

        // シーンのルートにDebugというGameObjectがあったら削除する
        var sceneRootObjects = scene.GetRootGameObjects();
        foreach (var rootObject in sceneRootObjects)
            if (rootObject.name == "Debug")
                Object.DestroyImmediate(rootObject.gameObject);
    }
}

コメントに書いていますが、IProcessSceneWithReport.OnProcessSceneはプレイヤービルド時の他に、エディタでシーンを再生した時にも呼ばれます。
この時には EditorApplication.isPlayingtrue になるため、この引数を見て判定し、ビルド時にだけ処理されるようにしています。

なおコメントにも書いていますが、report 引数が null かどうかで判定するのはアセットバンドルのことを考えると控えた方が良さそうです。

動作確認

さて最後にこのスクリプトをEditorフォルダに入れて動作を確認していきます。
Debug という名前の GameObject を配置したシーンを適当に作成します。
今回は Debug の子として Sphereを 配置しました。

動作確認

次にこれをビルドします。
まず比較のために、上記のスクリプトがプロジェクト内にない状態でビルドします。

スクリプトなし

当然ですが、Sphere が存在していることを確認できました。
次に上記のスクリプトを入れた状態でビルドし直します。

スクリプトあり

Sphere が消えている子を確認できました。

シーンをアセットバンドルした時にも呼ばれることを確認

また、このシーンをアセットバンドルにする可能性を考えると、アセットバンドルビルド時にもこの処理が正常に行われる必要があります。
これを確認するために、当該シーンを Addressables のエントリとして追加します。

エントリ追加

この状態で Addressables のビルドを行うと、以下のログが出力され、正常に処理が行われていることを確認できます。

ログ出力

ちなみに当然ですが一度シーンをビルドしてから、ProcessSceneWithReportを作成し差分ビルドをした場合には、シーンに変更が加わっていない限りビルドされないので、ProcessSceneWithReportの処理は行われません。
この場合には Clean Build をしてからビルドし直す必要があります。

関連

light11.hatenadiary.com

参考

docs.unity3d.com

answers.unity.com