【Unity】リリースビルド時に不要なオブジェクトを削除する

はじめに

2022.3.16f1での確認です。後継では解消されている問題もあるかもしれません。

Unityでデバッグ機能などをリリース時には除外したいという場面があると思います。

OnPreprocessBuildとかOnPostprocessBuildで適当にできると思っていたら地味にハマったので備忘として残しておきます。

削除したいGameObjectのタグを「EditorOnly」にするという手段もありますが、これだとDevelopmentBuild時にも除外されてしまうため今回別の手段を検討した次第です。

完成形

とりあえずこれなら行けそうという形になったものはこちら。

結果だけほしい方はこちらからコピペしてEditorフォルダ配下に置いてください。

削除したいオブジェクトのTagをDebugにするとビルド時に除外されます。
※この後に書きますが、シーンに変更をいれるか、CleanビルドしないとOnProcessSceneがコールされないので注意してください!!

using UnityEngine;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine.SceneManagement;
using System.Collections.Generic;

#if UNITY_EDITOR
public class CustomBuildProcessor : IPreprocessBuildWithReport, IPostprocessBuildWithReport, IProcessSceneWithReport
{
    // コールバックの順序を指定
    public int callbackOrder => 0;

    // ビルド前に呼ばれるメソッド(空実装)
    public void OnPreprocessBuild(BuildReport report)
    {
        // ビルド前の処理をここに追加できます
        Debug.Log($"OnPreprocessBuildをコールしました");
    }

    // ビルド後に呼ばれるメソッド(空実装)
    public void OnPostprocessBuild(BuildReport report)
    {
        // ビルド後の処理をここに追加できます
        Debug.Log($"OnPostprocessBuildをコールしました");
    }

    // ビルド中に各シーンごとに呼ばれるメソッド
    public void OnProcessScene(Scene scene, BuildReport report)
    {
        // シーンビルド時の処理
        Debug.Log($"OnProcessSceneをコールしました");

        if (EditorApplication.isPlaying)
            return;

        // ビルド中であることを確認
        if (BuildPipeline.isBuildingPlayer)
        {
            // Development Buildでない場合のみ処理を実行
            if ((report.summary.options & BuildOptions.Development) == 0)
            {
                ProcessScene(scene);
                Debug.Log($"ProcessSceneをコールしました");
            }
            else
            {
                Debug.Log($"[BuildProcessTemplate] Development Buildなので処理は行われません");
            }
        }
    }

    // 具体的な処理を関数化
    private void ProcessScene(Scene scene)
    {
        // タグが "Debug" のGameObjectを削除する
        RemoveDebugObjects(scene);

        // 他の処理をここに追加できます
    }

    // タグが "Debug" のGameObjectを削除する関数
    private void RemoveDebugObjects(Scene scene)
    {
        // シーン内のすべてのルートオブジェクトを取得
        GameObject[] rootObjects = scene.GetRootGameObjects();

        // "Debug" タグを持つオブジェクトを格納するリスト
        var debugObjects = new List<GameObject>();

        // すべてのルートオブジェクトを走査
        foreach (GameObject rootObject in rootObjects)
        {
            // 子オブジェクトも含めて再帰的に探索(非アクティブなオブジェクトも対象)
            foreach (Transform childTransform in rootObject.GetComponentsInChildren<Transform>(true))
            {
                if (childTransform.gameObject.CompareTag("Debug"))
                {
                    debugObjects.Add(childTransform.gameObject);
                }
            }
        }

        // 発見した "Debug" タグのオブジェクトを削除
        foreach (GameObject obj in debugObjects)
        {
            Debug.Log($"[BuildProcessTemplate] Deleting Debug Object: {obj.name} in Scene: {scene.name}");
            Object.DestroyImmediate(obj);
        }
    }
}
#endif

OnPreprocessBuildの問題

IPreprocessBuildWithReportを継承するとビルド前にOnPreprocessBuildが呼び出されて、ビルド時の前処理を走らせることができます。

ここでDebug用のオブジェクトなどを削除してしまいたいところなのですが、ここで削除すると永続的に削除されるため、前もって保存して、ビルド後に復元ということをしなければいけません。

これは面倒なので今回手段から除外しました。

OnProcessSceneが呼ばれない問題

さて、そうなると別の手段が必要です。

ChatGPTなどに聞くとIProcessSceneWithReportを継承してOnProcessSceneを定義するとシーンのビルド時にこのメソッドがコールされるらしく、これが呼ばれたときに該当のオブジェクトをDestroyすると良いとのことだったのでこれを実装してみました。

が、どうもOnProcessSceneがコールされていないようです。

ここで見事にハマりました。

結論としてはOnProcessSceneはシーンに変更が入っている場合か、Cleanビルドしたときにしかコールされないらしく、これに見事に引っかかったようです。

Xに投稿されていたものを引用(感謝!!)

これは現状クリーンビルドするしかないかなと思っていますが、シーンに変更入れなくてもコールされるようにする方法を知っている方がいたら教えて下さい。

OnProcessScene 再生時にも呼ばれる問題

そんなやっかいなOnProcessScene君ですが、なんとビルドではなくシーンを再生するときには毎回呼ばれます。(なんでやねん)

if (EditorApplication.isPlaying)
return;

なので上記のように再生時には実行されないようにガードが必要です。

Debug.isDebugBuild の判定適当問題

目的はデバッグ機能の除外なのでDevelopmentBuildのときには有効で、リリースビルドのときには削除としたかったので、Debug.isDebugBuildの判定を実装してみました。

しかし、これも何やらビルド時にDevelopmentBuildのチェックを外しているにもかかわらずDebug.isDebugBuildがtrueで返ってくる問題が・・・

ChatGPTに聞くと「ビルドプロセス中に Debug.isDebugBuild が常に true を返すためです。」とのことですが、ホントかどうかは不明です。

が実際にtrueが返ってくるのは事実なので対策が必要です。

if ((report.summary.options & BuildOptions.Development) == 0)

代替手段としては上記判定を行うことで正常に判定されているようです。

さいごに

ざっくりまとめると、OnProcessSceneはシーンが変更されたときにしかコールされない。

Development Buildの判定に Debug.isDebugBuild は使えない。

というところでしょうか。

どなたかのお役に立てば幸いです。

コメント

タイトルとURLをコピーしました