You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity自定义Script Finder工具搜索脚本结果不全问题排查

问题排查与解决方案

核心问题原因

  1. 单个GameObject仅记录首个遍历到的脚本
    代码中if (!gameObjectAndScriptNames.Any(x => x.GameObject == script.gameObject))的判断逻辑,导致一个GameObject即便挂载多个自定义脚本,也只会被添加到结果列表一次,且仅保存第一个被遍历到的脚本名称。例如:若某个GameObject同时挂载了GenerateGuid和另一个自定义脚本,它在列表中的脚本名会是后者,搜索Guid时自然不会被匹配到。

  2. 静态列表未清空复用
    gameObjectAndScriptNames是静态集合,每次打开窗口时不会自动清空,旧的搜索数据会残留,可能干扰新的检索结果。

  3. 未覆盖非激活对象
    FindObjectsOfType<MonoBehaviour>()默认仅检索激活状态GameObject上的组件,若挂载GenerateGuid的GameObject处于非激活状态,会被直接忽略。

修复后的代码

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

public class GameObjectAndScriptName
{
    public GameObject GameObject;
    public string ScriptName;

    public GameObjectAndScriptName(GameObject gameObject, string scriptName)
    {
        GameObject = gameObject;
        ScriptName = scriptName;
    }
}

public class ScriptFinder : EditorWindow
{
    ScriptFinder window;
    List<GameObjectAndScriptName> gameObjectAndScriptNames = new List<GameObjectAndScriptName>();
    Vector2 scrollPos;
    List<string> test = new List<string>();

    [MenuItem("EditorUtility/Script Finder")]
    static void OpenScriptFinder()
    {
        var window = (ScriptFinder)EditorWindow.GetWindow(typeof(ScriptFinder), false, "Script Finder");
        window.minSize = new Vector2(700, 700);
        window.maxSize = new Vector2(700, 700);
        window.RefreshScriptList();
        window.Traverse();
    }

    void RefreshScriptList()
    {
        // 每次刷新先清空列表
        gameObjectAndScriptNames.Clear();
        // 查找所有MonoBehaviour,包括非激活对象
        MonoBehaviour[] scripts = UnityEngine.Object.FindObjectsOfType<MonoBehaviour>(true);
        foreach (MonoBehaviour script in scripts)
        {
            Type scriptType = script.GetType();
            var scope = scriptType.Namespace;
            // 过滤Unity命名空间下的内置脚本
            if (scope == null || !scope.StartsWith("Unity"))
            {
                // 每个脚本都单独添加,不再按GameObject去重
                gameObjectAndScriptNames.Add(new GameObjectAndScriptName(script.gameObject, scriptType.Name));
            }
        }
    }

    void Traverse()
    {
        test.Clear();
        // 查找所有Transform,包括非激活对象
        foreach (Transform t in UnityEngine.Object.FindObjectsOfType<Transform>(true))
        {
            test.Add(t.name);
        }
    }

    string searchString;
    void OnGUI()
    {
        int counter = 0;
        GUI.color = Color.white;
        GUILayout.Space(30);
        
        // 搜索栏
        searchString = GUILayout.TextField(searchString, GUI.skin.FindStyle("ToolbarSeachTextField"));
        if (GUILayout.Button("", GUI.skin.FindStyle("ToolbarSeachCancelButton")))
        {
            searchString = "";
            GUI.FocusControl(null);
        }

        GUILayout.Space(15);

        // 样式设置
        GUIStyle TextFieldStyles = new GUIStyle(EditorStyles.textField);
        GUI.contentColor = Color.white;
        GUI.color = Color.white;
        TextFieldStyles.normal.textColor = Color.white;

        // 统计信息
        EditorStyles.label.normal.textColor = Color.yellow;
        EditorGUILayout.LabelField($"         Overall number of GameObjects in Hierarchy: {test.Count}");
        EditorGUILayout.Space(10);
        EditorGUILayout.LabelField("         GameObject                                                                                 Script");
        GUILayout.Space(5);
        EditorStyles.label.normal.textColor = Color.white;

        // 滚动视图
        scrollPos = EditorGUILayout.BeginScrollView(scrollPos, false, false);

        foreach (GameObjectAndScriptName item in gameObjectAndScriptNames)
        {
            EditorGUILayout.BeginHorizontal();

            if (!string.IsNullOrEmpty(searchString))
            {
                if (item.ScriptName.Contains(searchString, StringComparison.CurrentCultureIgnoreCase))
                {
                    EditorGUIUtility.labelWidth = 25;
                    EditorGUILayout.ObjectField(counter.ToString(), item.GameObject, typeof(GameObject), true);
                    counter++;
                    EditorGUILayout.TextField(item.ScriptName);
                }
            }
            else
            {
                EditorGUIUtility.labelWidth = 25;
                EditorGUILayout.ObjectField(counter.ToString(), item.GameObject, typeof(GameObject), true);
                EditorGUILayout.TextField(item.ScriptName);
                counter++;
            }

            EditorGUILayout.EndHorizontal();
        }

        GUILayout.EndScrollView();

        // 添加刷新按钮,支持手动更新列表
        if (GUILayout.Button("Refresh Script List"))
        {
            RefreshScriptList();
            Traverse();
        }
    }
}

关键修复点说明

  1. 取消GameObject去重,记录所有脚本实例
    移除了按GameObject去重的判断逻辑,每个脚本实例都单独添加到列表中,确保挂载同一脚本的不同GameObject都会被检索到,同时一个GameObject挂载的多个脚本也会分别显示。

  2. 移除静态成员,每次打开窗口初始化新列表
    将gameObjectAndScriptNames和test改为非静态成员,避免旧数据残留;同时添加RefreshScriptList方法,每次打开窗口或手动点击刷新时清空并重建列表。

  3. 支持检索非激活对象
    调用FindObjectsOfType<T>(true)重载方法,传入true参数以包含非激活状态的GameObject及其组件。

  4. 优化搜索逻辑
    将searchString != null改为!string.IsNullOrEmpty(searchString),避免空字符串导致的异常;整理冗余代码,移除无意义的遍历逻辑。

内容的提问来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 11:37:08