在 Unity 開發過程中,缺失的 Sprite 引用(特別是在 UI 元素上)可能導致程序運行時出現問題,尤其是在使用 Image
組件時。當你擁有多個 Prefab 和大量的 UI 資源時,手動檢查每個 Prefab 是否缺失了 Source Image
變得十分繁瑣。
為了提高開發效率,今天我們來編寫一個 Unity 編輯器腳本,通過查找 Prefab 中是否存在缺失的 Image
組件的 Sprite
,幫助你快速定位缺失的資源。
目標
該腳本的主要目標是:
- 遍歷選中的文件夾中的 Prefab 文件。
- 查找每個 Prefab 內的?
Image
?組件。 - 如果?
Image
?組件的?Sprite
?為空,則標記該節點為缺失,并記錄其路徑。 - 在編輯器窗口中顯示這些缺失的節點,供開發者定位和修復。
腳本解析?
1. 初始化窗口
我們首先通過 EditorWindow
創建一個自定義的 Unity 編輯器窗口。
public class MissingImageSpriteFinder : EditorWindow
{private Vector2 scrollPos;private List<ResultData> resultList = new List<ResultData>();private class ResultData{public GameObject prefab;public List<string> nodePaths = new List<string>();}
}
?這里,我們創建了一個 ResultData
類來存儲每個 Prefab 和包含缺失 Sprite
的節點路徑。在 MissingImageSpriteFinder
類中,我們定義了一個 resultList
來存儲所有找到的結果。
2. 添加菜單項
我們通過 MenuItem
特性將功能添加到 Unity 編輯器的菜單中,方便開發者直接點擊執行。
[MenuItem("Assets/Find Missing SourceImage", false, 49)]
public static void FindMissingImages()
{var window = GetWindow<MissingImageSpriteFinder>("Find Missing SourceImage Result");window.Search();
}
?此方法會在 Unity 編輯器中創建一個新的菜單項 查找 Missing 的 SourceImage
,點擊該菜單項時,會打開 MissingImageSpriteFinder
窗口并開始搜索缺失的 Sprite
。
3. 搜索邏輯
在 Search
方法中,我們首先清空結果列表,然后獲取選中的文件夾路徑,遍歷其中的 Prefab 文件。
private void Search()
{resultList.Clear();string[] selectedGuids = Selection.assetGUIDs;foreach (string guid in selectedGuids){string path = AssetDatabase.GUIDToAssetPath(guid);if (!AssetDatabase.IsValidFolder(path)) continue;string[] prefabPaths = Directory.GetFiles(path, "*.prefab", SearchOption.AllDirectories);foreach (string prefabPath in prefabPaths){GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);if (prefab == null) continue;Transform[] children = prefab.GetComponentsInChildren<Transform>(true);List<string> missingNodes = new List<string>();foreach (Transform t in children){Image img = t.GetComponent<Image>();if (img != null && IsMissingReference(img, "m_Sprite")){string nodePath = GetTransformPath(t, prefab.transform);missingNodes.Add(nodePath);}}if (missingNodes.Count > 0){resultList.Add(new ResultData{prefab = prefab,nodePaths = missingNodes});}}}Repaint();
}
在這個方法中,我們做了以下幾件事:
- 獲取當前選中的文件夾路徑。
- 查找該文件夾及其子文件夾中的所有 Prefab 文件。
- 對每個 Prefab 文件進行處理,查找所有子節點中的?
Image
?組件。 - 如果?
Image
?組件的?Sprite
?屬性為空,則認為是缺失的,并記錄該節點的路徑。
4. 界面顯示
在 OnGUI
方法中,我們定義了自定義窗口的顯示邏輯。
private void OnGUI()
{GUILayout.Label("查找結果", EditorStyles.boldLabel);if (resultList.Count == 0){EditorGUILayout.HelpBox("未找到缺失的 SourceImage。", MessageType.Info);return;}scrollPos = EditorGUILayout.BeginScrollView(scrollPos);foreach (var result in resultList){EditorGUILayout.BeginVertical("box");EditorGUILayout.ObjectField("Prefab", result.prefab, typeof(GameObject), false);EditorGUILayout.LabelField("包含 Missing Sprite 的節點路徑:");foreach (var path in result.nodePaths){EditorGUILayout.LabelField(" - " + path);}if (GUILayout.Button("定位 Prefab", GUILayout.Width(100))){Selection.activeObject = result.prefab;EditorGUIUtility.PingObject(result.prefab);}EditorGUILayout.EndVertical();}EditorGUILayout.EndScrollView();
}
- 首先顯示了一個標題標簽。
- 如果沒有找到缺失的?
Sprite
,會顯示一條提示消息。 - 如果有找到缺失的?
Sprite
,則在滾動視圖中列出每個 Prefab 和其包含缺失?Sprite
?的節點路徑。 - 每個 Prefab 后面有一個按鈕,點擊后會自動選中該 Prefab 并在場景中高亮顯示。
5. 輔助方法
IsMissingReference
和 GetTransformPath
方法分別用于判斷 Image
組件的 Sprite
是否為空,并獲取節點相對 Prefab 的路徑。
private bool IsMissingReference(Object obj, string propertyName)
{SerializedObject so = new SerializedObject(obj);SerializedProperty sp = so.FindProperty(propertyName);return sp != null && sp.objectReferenceValue == null && sp.objectReferenceInstanceIDValue != 0;
}private string GetTransformPath(Transform current, Transform root)
{List<string> path = new List<string>();while (current != null && current != root){path.Insert(0, current.name);current = current.parent;}return string.Join("/", path);
}
IsMissingReference
?通過檢查?Image
?組件的?m_Sprite
?屬性是否為空,判斷是否為缺失的引用。GetTransformPath
?通過遍歷節點的父節點,構建從根節點到當前節點的路徑。
完整代碼
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using System.Collections.Generic;
using System.IO;public class MissingImageSpriteFinder : EditorWindow
{private Vector2 scrollPos;private List<ResultData> resultList = new List<ResultData>();private class ResultData{public GameObject prefab;public List<string> nodePaths = new List<string>();}[MenuItem("Assets/Find Missing SourceImage", false, 49)]public static void FindMissingImages(){var window = GetWindow<MissingImageSpriteFinder>("Find Missing SourceImage Result");window.Search();}private void Search(){resultList.Clear();string[] selectedGuids = Selection.assetGUIDs;foreach (string guid in selectedGuids){string path = AssetDatabase.GUIDToAssetPath(guid);if (!AssetDatabase.IsValidFolder(path)) continue;string[] prefabPaths = Directory.GetFiles(path, "*.prefab", SearchOption.AllDirectories);foreach (string prefabPath in prefabPaths){GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);if (prefab == null) continue;Transform[] children = prefab.GetComponentsInChildren<Transform>(true);List<string> missingNodes = new List<string>();foreach (Transform t in children){Image img = t.GetComponent<Image>();if (img != null && IsMissingReference(img, "m_Sprite")){string nodePath = GetTransformPath(t, prefab.transform);missingNodes.Add(nodePath);}}if (missingNodes.Count > 0){resultList.Add(new ResultData{prefab = prefab,nodePaths = missingNodes});}}}Repaint();}private void OnGUI(){GUILayout.Label("查找結果", EditorStyles.boldLabel);if (resultList.Count == 0){EditorGUILayout.HelpBox("未找到缺失的 SourceImage。", MessageType.Info);return;}scrollPos = EditorGUILayout.BeginScrollView(scrollPos);foreach (var result in resultList){EditorGUILayout.BeginVertical("box");EditorGUILayout.ObjectField("Prefab", result.prefab, typeof(GameObject), false);EditorGUILayout.LabelField("包含 Missing Sprite 的節點路徑:");foreach (var path in result.nodePaths){EditorGUILayout.LabelField(" - " + path);}if (GUILayout.Button("定位 Prefab", GUILayout.Width(100))){Selection.activeObject = result.prefab;EditorGUIUtility.PingObject(result.prefab);}EditorGUILayout.EndVertical();}EditorGUILayout.EndScrollView();}// 判斷是否是 Missing 的引用private bool IsMissingReference(Object obj, string propertyName){SerializedObject so = new SerializedObject(obj);SerializedProperty sp = so.FindProperty(propertyName);return sp != null && sp.objectReferenceValue == null && sp.objectReferenceInstanceIDValue != 0;}// 獲取節點相對 prefab 的路徑private string GetTransformPath(Transform current, Transform root){List<string> path = new List<string>();while (current != null && current != root){path.Insert(0, current.name);current = current.parent;}return string.Join("/", path);}
}
效果預覽
右鍵選中文件夾 選擇Find Missing SourceImage
效果如下
總結
通過編寫這個自定義的 Unity 編輯器擴展,你可以高效地查找和修復缺失的 Image
組件的 Sprite
引用。它能夠自動掃描選定的文件夾中的所有 Prefab,并定位其中的缺失引用,大大節省了手動檢查的時間。
如果你的項目中包含大量的 UI Prefab 和資源,這個工具將是你提高生產力的重要助手。希望這篇博客對你有所幫助,快去試試吧!