引用 by https://github.com/yasirkula/UnityAssetUsageDetector/
马克下
using UnityEngine;
using UnityEditor;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System;
using Object = UnityEngine.Object;
namespace AssetUsageDetectorNamespace
{
public enum Phase { Setup, Processing, Complete };
// Delegate to get the value of a variable (either field or property)
public delegate object VariableGetVal( object obj );
#region Helper Classes
// Custom class to hold the results for a single scene or Assets folder
public class ReferenceHolder
{
// Custom struct to hold a single path to a reference
public struct ReferencePath
{
public readonly ReferenceNode startNode;
public readonly int[] pathLinksToFollow;
public ReferencePath( ReferenceNode startNode, int[] pathIndices )
{
this.startNode = startNode;
pathLinksToFollow = pathIndices;
}
}
private string title;
private bool clickable;
private List
references;
private List
shortestPathsToReferences;
public int NumberOfReferences { get { return references.Count; } }
public ReferenceHolder( string title, bool clickable )
{
this.title = title;
this.clickable = clickable;
references = new List
();
shortestPathsToReferences = null;
}
// Add a reference to the list
public void AddReference( ReferenceNode node )
{
references.Add( node );
}
// Add all the Object's in this container to the set
public void AddObjectsTo( HashSet
objectsSet )
{
CalculateShortestPathsToReferences();
for( int i = 0; i < shortestPathsToReferences.Count; i++ )
{
Object obj = shortestPathsToReferences[i].startNode.nodeObject as Object;
if( obj != null )
objectsSet.Add( obj );
}
}
// Add all the GameObject's in this container to the set
public void AddGameObjectsTo( HashSet
gameObjectsSet )
{
CalculateShortestPathsToReferences();
for( int i = 0; i < shortestPathsToReferences.Count; i++ )
{
Object obj = shortestPathsToReferences[i].startNode.nodeObject as Object;
if( obj != null )
{
if( obj is GameObject )
gameObjectsSet.Add( (GameObject) obj );
else if( obj is Component )
gameObjectsSet.Add( ( (Component) obj ).gameObject );
}
}
}
// Calculate shortest unique paths to the references
public void CalculateShortestPathsToReferences()
{
if( shortestPathsToReferences != null )
return;
shortestPathsToReferences = new List
( 32 ); for( int i = 0; i < references.Count; i++ ) references[i].CalculateShortestPaths( shortestPathsToReferences ); } // Draw the results found for this container public void DrawOnGUI( bool drawFullPaths ) { Color c = GUI.color; GUI.color = Color.cyan; if( GUILayout.Button( title, AssetUsageDetector.BoxGUIStyle, AssetUsageDetector.GL_EXPAND_WIDTH, AssetUsageDetector.GL_HEIGHT_40 ) && clickable ) { // If the container (scene, usually) is clicked, highlight it on Project view EditorGUIUtility.PingObject( AssetDatabase.LoadAssetAtPath
( title ) ); Selection.activeObject = AssetDatabase.LoadAssetAtPath
( title ); } GUI.color = Color.yellow; if( drawFullPaths ) { for( int i = 0; i < references.Count; i++ ) { if( references[i].nodeObject == null ) continue; GUILayout.Space( 5 ); references[i].DrawOnGUIRecursively(); } } else { if( shortestPathsToReferences == null ) CalculateShortestPathsToReferences(); for( int i = 0; i < shortestPathsToReferences.Count; i++ ) { ReferencePath path = shortestPathsToReferences[i]; if( path.startNode.nodeObject == null ) continue; GUILayout.Space( 5 ); GUILayout.BeginHorizontal(); path.startNode.DrawOnGUI( null ); ReferenceNode currentNode = path.startNode; for( int j = 0; j < path.pathLinksToFollow.Length; j++ ) { ReferenceNode.Link link = currentNode[path.pathLinksToFollow[j]]; link.targetNode.DrawOnGUI( link.description ); currentNode = link.targetNode; } GUILayout.EndHorizontal(); } } GUI.color = c; GUILayout.Space( 10 ); } } // Custom class to hold an object in the path to a reference as a node public class ReferenceNode { public struct Link { public readonly ReferenceNode targetNode; public readonly string description; public Link( ReferenceNode targetNode, string description ) { this.targetNode = targetNode; this.description = description; } } public object nodeObject; private readonly List
links; public int NumberOfOutgoingLinks { get { return links.Count; } } public Link this[int index] { get { return links[index]; } } public ReferenceNode() { links = new List
( 2 ); } public ReferenceNode( object obj ) : this() { nodeObject = obj; } // Add a one-way connection to another node public void AddLinkTo( ReferenceNode nextNode, string description = null ) { if( nextNode != null ) { if( !string.IsNullOrEmpty( description ) ) description = "[" + description + "]"; links.Add( new Link( nextNode, description ) ); } } // Clear this node so that it can be reused later public void Clear() { nodeObject = null; links.Clear(); } // Calculate shortest unique paths that start with this node public void CalculateShortestPaths( List
currentPaths ) { CalculateShortestPaths( currentPaths, new List
( 8 ), new List
( 8 ) { -1 }, 0 ); } // Just some boring calculations to find the shortest unique paths recursively private void CalculateShortestPaths( List
shortestPaths, List
currentPath, List
currentPathIndices, int latestObjectIndexInPath ) { if( nodeObject == null ) return; int currentIndex = currentPath.Count; currentPath.Add( this ); if( links.Count == 0 ) { // Check if the path to the reference is unique (not discovered so far) bool isUnique = true; for( int i = 0; i < shortestPaths.Count; i++ ) { if( shortestPaths[i].startNode == currentPath[latestObjectIndexInPath] && shortestPaths[i].pathLinksToFollow.Length == currentPathIndices.Count - latestObjectIndexInPath - 1 ) { int j = latestObjectIndexInPath + 1; for( int k = 0; j < currentPathIndices.Count; j++, k++ ) { if( shortestPaths[i].pathLinksToFollow[k] != currentPathIndices[j] ) break; } if( j == currentPathIndices.Count ) { isUnique = false; break; } } } // Don't allow duplicate shortest paths if( isUnique ) { int[] pathIndices = new int[currentPathIndices.Count - latestObjectIndexInPath - 1]; for( int i = latestObjectIndexInPath + 1, j = 0; i < currentPathIndices.Count; i++, j++ ) pathIndices[j] = currentPathIndices[i]; shortestPaths.Add( new ReferenceHolder.ReferencePath( currentPath[latestObjectIndexInPath], pathIndices ) ); } } else { if( nodeObject is Object ) latestObjectIndexInPath = currentIndex; for( int i = 0; i < links.Count; i++ ) { currentPathIndices.Add( i ); links[i].targetNode.CalculateShortestPaths( shortestPaths, currentPath, currentPathIndices, latestObjectIndexInPath ); currentPathIndices.RemoveAt( currentIndex + 1 ); } } currentPath.RemoveAt( currentIndex ); } // Draw all the paths that start with this node on GUI recursively public void DrawOnGUIRecursively( string linkToPrevNodeDescription = null ) { GUILayout.BeginHorizontal(); DrawOnGUI( linkToPrevNodeDescription ); if( links.Count > 0 ) { GUILayout.BeginVertical(); for( int i = 0; i < links.Count; i++ ) { ReferenceNode next = links[i].targetNode; if( next.nodeObject != null ) next.DrawOnGUIRecursively( links[i].description ); } GUILayout.EndVertical(); } GUILayout.EndHorizontal(); } // Draw only this node on GUI public void DrawOnGUI( string linkToPrevNodeDescription ) { string label = GetNodeContent( linkToPrevNodeDescription ); if( GUILayout.Button( label, AssetUsageDetector.BoxGUIStyle, AssetUsageDetector.GL_EXPAND_HEIGHT ) ) { // If a reference is clicked, highlight it (either on Hierarchy view or Project view) Object unityObject = nodeObject as Object; if( unityObject != null ) { EditorGUIUtility.PingObject( unityObject ); Selection.activeObject = unityObject; } } if( AssetUsageDetector.showTooltips && Event.current.type == EventType.Repaint && GUILayoutUtility.GetLastRect().Contains( Event.current.mousePosition ) ) AssetUsageDetector.tooltip = label; } // Get the string representation of this node private string GetNodeContent( string linkToPrevNodeDescription = null ) { string result = string.Empty; if( !string.IsNullOrEmpty( linkToPrevNodeDescription ) ) result = linkToPrevNodeDescription + "\n"; Object unityObject = nodeObject as Object; if( unityObject != null ) result += unityObject.name + " (" + unityObject.GetType() + ")"; else result += nodeObject.GetType() + " object"; return result; } } // Custom struct to hold a variable, its important properties and its getter function public struct VariableGetterHolder { public readonly string name; public readonly bool isProperty; public readonly bool isSerializable; private readonly VariableGetVal getter; public VariableGetterHolder( FieldInfo fieldInfo, VariableGetVal getter, bool isSerializable ) { name = fieldInfo.Name; isProperty = false; this.isSerializable = isSerializable; this.getter = getter; } public VariableGetterHolder( PropertyInfo propertyInfo, VariableGetVal getter, bool isSerializable ) { name = propertyInfo.Name; isProperty = true; this.isSerializable = isSerializable; this.getter = getter; } public object Get( object obj ) { return getter( obj ); } } // Credit: http://stackoverflow.com/questions/724143/how-do-i-create-a-delegate-for-a-net-property public interface IPropertyAccessor { object GetValue( object source ); } // A wrapper class for properties to get their values more efficiently public class PropertyWrapper
: IPropertyAccessor where TObject : class { private readonly Func
getter; public PropertyWrapper( MethodInfo getterMethod ) { getter = (Func
) Delegate.CreateDelegate( typeof( Func
), getterMethod ); } public object GetValue( object obj ) { try { return getter( (TObject) obj ); } catch { // Property getters may return various kinds of exceptions // if their backing fields are not initialized (yet) return null; } } } #endregion #region Extension Functions public static class AssetUsageDetectorExtensions { // Get a unique-ish string hash code for an object public static string Hash( this object obj ) { if( obj is Object ) return obj.GetHashCode() + obj.GetType().Name + ( (Object) obj ).name; return obj.GetHashCode() + obj.GetType().Name; } // Check if object depends on any of the references public static bool HasAnyReferenceTo( this Object obj, HashSet
references ) { Object[] dependencies = EditorUtility.CollectDependencies( new Object[] { obj } ); for( int i = 0; i < dependencies.Length; i++ ) { if( references.Contains( dependencies[i] ) ) return true; } return false; } // Check if the field is serializable public static bool IsSerializable( this FieldInfo fieldInfo ) { // see Serialization Rules: https://docs.unity3d.com/Manual/script-Serialization.html Type fieldType = fieldInfo.FieldType; if( fieldType.IsDerivedFrom( typeof( Object ) ) ) return true; if( fieldType.IsArray ) { if( fieldType.GetArrayRank() != 1 ) return false; fieldType = fieldType.GetElementType(); } else if( fieldType.IsGenericType ) { if( fieldType.GetGenericTypeDefinition() != typeof( List<> ) ) return false; fieldType = fieldType.GetGenericArguments()[0]; } if( fieldType.IsGenericType || fieldInfo.IsInitOnly || ( ( !fieldInfo.IsPublic || fieldInfo.IsNotSerialized ) && !Attribute.IsDefined( fieldInfo, typeof( SerializeField ) ) ) ) return false; if( Attribute.IsDefined( fieldType, typeof( SerializableAttribute ), false ) ) return true; return false; } // Check if the type is a common Unity type (let's call them primitives) public static bool IsPrimitiveUnityType( this Type type ) { return type.IsPrimitive || type == typeof( string ) || type == typeof( Vector3 ) || type =