全シーン内にある特定の全コンポーネントに対して処理を行う

プロジェクト内の全てのシーンに含まれている、特定の型の全コンポーネントに対して何らかの処理を行う方法です。

 

想定される利用例

様々な用途が考えられますが、パッと思いついたものをいくつか書いておきます。

  • 全てのTextMeshProUGUIに対して、特定のFontAssetを指定する
  • 全てのTextに対して、TextMeshProUGUIに置き換える
  • 全てのImageに対して、RaycastTargetを無効にする
  • 全てのCameraに対して、Backgroundを変更する
  • 全てのGameObjectに対して、アクティブ状態にする

 

ソースコード

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

public class ComponentSearcher : MonoBehaviour
{
    /// <summary>
    /// メニューバーから実行できるようにする
    /// Tools > ProcessAllComponents
    /// </summary>
    [MenuItem("Tools/ProcessComponentsInAllScenes")]
    private static void ProcessComponentsInAllScenes()
    {
        // 例: SpriteRendererを対象にする
        ProcessComponentsInAllScenes<SpriteRenderer>();
    }

    /// <summary>
    /// 全コンポーネントに対して行う処理
    /// </summary>
    /// <param name="component">対象のコンポーネント</param>
    /// <typeparam name="T">対象のコンポーネント型</typeparam>
    private static void MyAction<T>(T component)
    {
        var spriteRenderer = component as SpriteRenderer;
        if(spriteRenderer == null) return;
        
        // 例: 全SpriteRendererのcolorをログ出力
        Debug.Log(spriteRenderer.color);
    }
    
    /// <summary>
    /// 全シーン内にあるコンポーネントに対してMyActionを行う
    /// </summary>
    /// <typeparam name="T">対象のコンポーネント型</typeparam>
    private static void ProcessComponentsInAllScenes<T>()
    {
        var dirInfo = new DirectoryInfo(Application.dataPath);
        var sceneFiles = dirInfo.GetFiles("*.unity", SearchOption.AllDirectories).ToList();
        var currentOpenScenePath = EditorSceneManager.GetActiveScene().path;

        var components = new List<T>();
        foreach (var sceneFile in sceneFiles)
        {
            var splitedScenePath = sceneFile.FullName.Split(new char['/']);
            var scenePath = splitedScenePath[splitedScenePath.Length - 1];
            var scene = EditorSceneManager.OpenScene(scenePath);
            var componentsInScene = GetComponentsInScene<T>(scene);
            foreach (var component in componentsInScene)
            {
                MyAction<T>(component);
            }
            components.AddRange(componentsInScene);
        }

        EditorSceneManager.OpenScene(currentOpenScenePath);
    }

    /// <summary>
    /// 1つのScene内にある特定のコンポーネントを全て取得
    /// </summary>
    /// <param name="scene">対象のScene</param>
    /// <typeparam name="T">コンポーネント型</typeparam>
    /// <returns>Scene内にある全コンポーネントのリスト</returns>
    private static List<T> GetComponentsInScene<T>(Scene scene)
    {
        var rootGameObjects = scene.GetRootGameObjects();
        var list = new List<T>();
        foreach (var root in rootGameObjects)
        {
            var components = root.GetComponentsInChildren<T>(true);
            list.AddRange(components);
        }

        return list;
    }
}

 

コメントで「例: 〜〜〜」と書いてある部分を適宜修正して、UnityエディタのメニューバーからTools > ProcessAllComponentsを選択すれば、全コンポーネントに対してMyActionメソッドを実行します。

 

sceneFilesを取得する部分を工夫すれば、特定のフォルダ直下にあるシーンのみを対象にしたりも可能です。

 

注意点としては、Editorスクリプトを使っているのと、「全シーンを1つずつ開いて処理して閉じるのを繰り返す」ということをしているので、PlayMode時(エディタ実行中)には使えません。

コメント