自学内容网 自学内容网

Unity 使用Editor工具查找 Prefab 中的指定脚本

在 Unity 项目中,随着项目规模的扩大和 Prefab 数量的增加,管理和定位 Prefab 中的脚本变得更加复杂。为了提高开发效率,所以需要编写一个自定义的 Unity Editor 工具,帮助查找某个 Prefab 中是否使用了指定的脚本。本文将介绍如何通过 Unity Editor API 来实现一个 "Prefab Script Finder" 工具,该工具可以在 Prefab 模式下帮助查找附带特定脚本的对象,并直接在编辑器中进行选择和高亮。

一、工具实现目标

在本次实现中,我们的目标是构建一个简单且实用的 Unity Editor 工具,通过以下几个步骤实现:

  • 允许用户从编辑器中选择某个 MonoScript,即一个脚本文件。
  • 查找当前正在编辑的 Prefab 中是否有 GameObject 附带这个脚本。
  • 将找到的对象列出来,并允许开发者在结果列表中点击对象以高亮和定位到该对象。

这个工具非常适合于 Prefab 数量庞大、对象和脚本复杂度较高的项目,有助于快速定位特定组件,提高开发效率。

二、效果

三、代码实现

首先,可以使用 Unity 的 EditorWindow 创建一个自定义的编辑器窗口。在该窗口中,用户可以选择一个脚本文件并点击按钮来查找 Prefab 中附带该脚本的对象。接下来,我们通过递归的方式遍历 Prefab 的所有子对象,检查是否附带指定的脚本。如果找到了,便将这些对象显示在结果列表中。以下是完整的代码实现:

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

public class PrefabScriptFinder : EditorWindow
{
    private MonoScript selectedScript;
    private string scriptTypeName = "";
    private List<(GameObject obj, string prefabPath)> foundObjects = new List<(GameObject, string)>();
    private Vector2 scrollPos;

    [MenuItem("GameObject/ZYT ASSETS/Prefab Script Finder", false, 100)]
    public static void ShowWindow()
    {
        GetWindow<PrefabScriptFinder>("Prefab Script Finder");
    }

    private void OnGUI()
    {
        selectedScript = (MonoScript)EditorGUILayout.ObjectField("Script Type", selectedScript, typeof(MonoScript), false);

        if (selectedScript != null)
        {
            if (selectedScript.GetClass() != null)
            {
                scriptTypeName = selectedScript.GetClass().FullName;
            }
            else
            {
                scriptTypeName = "";
            }
        }
        else
        {
            scriptTypeName = "";
        }

        if (GUILayout.Button("Find in Prefab"))
        {
            if (string.IsNullOrEmpty(scriptTypeName))
            {
                EditorUtility.DisplayDialog("No Script Selected", "Please select a valid script type.", "OK");
                return;
            }
            FindInPrefab();
        }

        if (foundObjects.Count > 0)
        {
            GUILayout.Space(10);
            GUILayout.Label("Found Objects:", EditorStyles.boldLabel);

            scrollPos = GUILayout.BeginScrollView(scrollPos);
            EditorGUILayout.BeginVertical();

            // 显示找到的对象列表
            foreach (var (obj, prefabPath) in foundObjects)
            {
                // 对象点击可以选中
                EditorGUI.BeginChangeCheck();
                GameObject selectedObj = (GameObject)EditorGUILayout.ObjectField(obj, typeof(GameObject), true);
                if (EditorGUI.EndChangeCheck())
                {
                    SelectObjectInOpenedPrefab(obj, prefabPath);
                }
            }

            EditorGUILayout.EndVertical();
            GUILayout.EndScrollView();
        }
    }

    // 在Prefab中查找
    private void FindInPrefab()
    {
        foundObjects.Clear();

        // 获取当前打开的Prefab场景
        PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
        if (prefabStage == null)
        {
            EditorUtility.DisplayDialog("No Opened Prefab", "Please open a prefab in Prefab Mode.", "OK");
            return;
        }

        GameObject prefabInstance = prefabStage.prefabContentsRoot;

        if (prefabInstance == null)
        {
            EditorUtility.DisplayDialog("Error", "Failed to get the prefab contents.", "OK");
            return;
        }

        // 遍历Prefab中的对象
        FindObjectsWithScript(prefabInstance.transform, prefabStage.assetPath);

        if (foundObjects.Count > 0)
        {
            Repaint();
        }
        else
        {
            EditorUtility.DisplayDialog("No Results", $"No objects with script '{scriptTypeName}' found in the opened prefab.", "OK");
        }
    }

    // 递归查找带有指定脚本的对象
    private void FindObjectsWithScript(Transform parent, string prefabPath)
    {
        foreach (Transform child in parent)
        {
            Component[] components = child.GetComponents<Component>();

            foreach (Component comp in components)
            {
                if (comp != null && comp.GetType().FullName == scriptTypeName)
                {
                    // 将找到的对象和Prefab路径保存
                    foundObjects.Add((child.gameObject, prefabPath));
                    break;
                }
            }

            // 递归检查子对象
            FindObjectsWithScript(child, prefabPath);
        }
    }

    // 选中Prefab中的对象
    private void SelectObjectInOpenedPrefab(GameObject obj, string prefabPath)
    {
        PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
        if (prefabStage == null)
        {
            prefabStage = PrefabStageUtility.OpenPrefab(prefabPath);
        }

        if (prefabStage != null)
        {
            // 选中并高亮对象
            Selection.activeGameObject = obj;
            EditorGUIUtility.PingObject(obj);
        }
        else
        {
            Debug.LogError("Failed to open Prefab in Prefab Mode.");
        }
    }
}

四、主要功能解读

  1. 脚本选择: 用户通过 EditorGUILayout.ObjectField 来选择一个脚本MonoScript,当脚本被选择后,我们使用 GetClass() 方法来获取该脚本所对应的 C# 类类型,并获取其全名。

  2. Prefab 搜索: 我们使用 PrefabStageUtility.GetCurrentPrefabStage() 获取当前正在编辑的 Prefab 场景。如果 Prefab 场景未打开,我们提示用户打开 Prefab 模式。然后我们通过递归遍历 Prefab 中的每一个子对象,检查对象上是否附带了指定的脚本。

  3. 结果显示: 找到的对象会被列出,并且用户可以点击每一个对象,工具将自动高亮并选中该对象,方便用户进行进一步操作。

  4. 递归查找: 通过递归遍历 Prefab 的子节点,确保我们不会遗漏任何对象。每一个带有指定脚本的对象都会被保存到结果列表中。

五、工具的优势

  • 快速查找: 在复杂的项目中,Prefab 中的对象可能非常多,手动查找是低效的。这个工具能快速帮助开发者找到指定脚本。
  • 结果交互: 找到的对象直接在编辑器中展示,点击即可选中,并进行定位,非常方便。

六、总结

本文介绍的 "Prefab Script Finder" 是一个实用的 Unity Editor 工具,能够帮助开发者快速在 Prefab 中查找附带特定脚本的对象。通过这种方式,我们可以减少开发过程中的重复劳动,快速定位问题所在,并提高整体开发效率。


原文地址:https://blog.csdn.net/qq_41973169/article/details/142358356

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!