介绍

Unity 2018 中如果 Prefab 中存在 Missing Component 会导致无法保存 Prefab,必须修正脚本引用才可以保存。

1
2
3
The Prefab file contains an invalid script. Applying
is not possible. Enter Prefab Mode and remove or
recover the script.

在缺失脚本的组件上可以看到以下提示:

1
2
3
The associated script can not be loaded.
Please fix any compile errors
and assign a valid script.

网上搜索到的查找丢失脚本的代码大部分都是找到存在丢失脚本的 Prefab,而无法找到 Prefab 中是哪一个结点上的脚本组件丢失了。

这个项目就是一个写得不错的项目,而且一直在维护。

推荐使用这个工具找到项目内所有丢失脚本的 Prefab,再配合下面的工具找到丢失脚本的结点。

环境

  • Unity 2018.4.0f2

使用

只需要将以下代码保存到 Editor 目录即可使用,执行 Tools/Find Missing References 后会在 Console 中列出所有存在丢失脚本的结点,点击后可以在 Hierarchy 视图跳转到对应的结点上。

注意:工具会同时找到所有丢失引用的组件。

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
using System.Collections.Generic;
using System.Text;
using UnityEditor;
using UnityEngine;

public static class FindMissingReferences
{
    [MenuItem("Tools/Find Missing References")]
    public static void Find()
    {
        var go = Selection.activeGameObject;
        if (go == null)
        {
            Debug.LogError("Please select GameObject first");
            return;
        }

        var prefabType = PrefabUtility.GetPrefabAssetType(go);
        if (prefabType == PrefabAssetType.Regular || prefabType == PrefabAssetType.Variant)
        {
            AssetDatabase.OpenAsset(go);
            EditorApplication.ExecuteMenuItem("Window/General/Hierarchy");
            go = Selection.activeGameObject;
        }

        var queue = new Queue<Transform>();
        queue.Enqueue(go.transform);

        while (queue.Count > 0)
        {
            var transform = queue.Dequeue();
            FindMissingReferencesInTransform(transform);

            for (int i = 0; i < transform.childCount; i++)
            {
                queue.Enqueue(transform.GetChild(i));
            }
        }
    }

    private static void FindMissingReferencesInTransform(Transform transform)
    {
        var components = transform.GetComponents<Component>();

        foreach (var component in components)
        {
            if (component == null)
            {
                ShowMissingScript(transform);
            }
            else
            {
                var so = new SerializedObject(component);
                var sp = so.GetIterator();

                while (sp.NextVisible(true))
                {
                    if (sp.propertyType != SerializedPropertyType.ObjectReference)
                    {
                        continue;
                    }

                    if (sp.objectReferenceValue == null && sp.objectReferenceInstanceIDValue != 0)
                    {
                        ShowMissingReference(transform, component, sp.name);
                    }
                }
            }
        }
    }

    private static void ShowMissingScript(Transform transform)
    {
        Debug.LogError("Missing script found on: " + GetGameObjectPath(transform), transform.gameObject);
    }

    private static void ShowMissingReference(Transform transform, Component component, string propertyName)
    {
        string message = string.Format("Missing reference found in: {0}, Component: {1}, Property : {2}",
            GetGameObjectPath(transform), component, propertyName);
        Debug.LogWarning(message, transform.gameObject);
    }

    private static string GetGameObjectPath(Transform transform)
    {
        var result = new StringBuilder(transform.name);
        while (transform.parent != null)
        {
            transform = transform.parent;
            result.Insert(0, transform.name + "/");
        }

        return result.ToString();
    }
}