御幸備忘録

主にUnityに関してのブログです。

【Unity】エディタ拡張で全プレハブからRigidbodyを削除する機能を作ってみた【C#】

はじめに

Unity4からUnity2018にアップデートする過程で100個以上あるPrefabからRigidbodyを外さなければなかったので作った。
ToolsからRemove Conponents From Prefabを選べば使えます。
Rigidbodyを書き換えれば他のコンポーネントにも対応できます。

ソース

using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

class RemoveConponentsFromAllPrefab : MonoBehaviour
{
    private static IEnumerator m_iEnumerator = null;

    [MenuItem("Tools/Remove Conponents From All Prefab")]
    static private void StartRemove()
    {
        var prefabs = GetPrefabs();

        foreach (var p in prefabs)
        {
            // StartCoroutineがnon staticで使えないため、MoveNext()を使用
            m_iEnumerator = RemoveConponentsFromPrefab(p);
            m_iEnumerator.MoveNext();
        }
    }

    static private List<GameObject> GetPrefabs()
    {
        Debug.Log("Prefabの取得");
        string directoryPath = "Assets";
        List<GameObject> assets = new List<GameObject>();

        // Asset以下の全ファイルを取得
        string[] filePathes = Directory.GetFiles(directoryPath, "*", SearchOption.AllDirectories);

        foreach (var filePath in filePathes)
        {
            // Prefabファイルのみ処理を行う
            if (filePath.Contains(".prefab") && !filePath.Contains(".meta"))
            {
                GameObject asset = AssetDatabase.LoadAssetAtPath<GameObject>(filePath);
                Debug.Log("Loaded " + filePath);
                if (asset != null)
                {
                    assets.Add(asset);
                }
            }
        }

        return assets;
    }

    static private IEnumerator RemoveConponentsFromPrefab(GameObject prefab)
    {
        // 非アクティブなオブジェクトのコンポーネントを取得するため引数にtrue
        var components = prefab.GetComponentsInChildren<Rigidbody>(true);

        if (components.Length > 0)
        {
            Debug.Log("Destroy " +components[0].name+ " in " +prefab.name);
            foreach (var c in components)
            {
                // Destroyでは即時反映されないためDestroyImmediateを使用
                DestroyImmediate(c, true);
            }

            var go = Instantiate(prefab);

            //待機処理
            while (go == null)
            {
                yield return null;
            }

            go = PrefabUtility.ConnectGameObjectToPrefab(go, prefab);

            Debug.Log("Replace " + prefab.name + " with " + go.name);
            PrefabUtility.ReplacePrefab(go, prefab, ReplacePrefabOptions.ConnectToPrefab);

            Debug.Log("Destroy " +go.name);
            DestroyImmediate(go, true);
        }
    }
}

あとがき

StartCorutine()ではなくMoveNext()を使った方法やPrefabUtility等、学ぶ事が多かった。
もう少し使いやすさを上げてRigitbody以外にも対応させたい。
Unity2018.2では問題ないが、Unity2018.3ではPrefabUtilityが旧形式と警告されているので後々対応したい。

参考

StartCoroutine(MonoBehaviour)を使わずにコルーチンを実行する【Unity】【エディタ拡張】
http://kan-kikuchi.hatenablog.com/entry/Coroutine_Editor

【Unity】【エディタ拡張】スクリプトからPrefabを操作する(Unity2018.2まで版)
http://light11.hatenadiary.com/entry/2018/12/19/220554