134 lines
5.4 KiB
C#
Raw Normal View History

2025-09-08 14:51:28 +08:00
using UnityEditor;
using UnityEngine;
// Using a property drawer to allow any class to have a field of type GuidRefernce and still get good UX
// If you are writing your own inspector for a class that uses a GuidReference, drawing it with
// EditorLayout.PropertyField(prop) or similar will get this to show up automatically
[CustomPropertyDrawer(typeof(GuidReference))]
public class GuidReferenceDrawer : PropertyDrawer
{
SerializedProperty guidProp;
SerializedProperty sceneProp;
SerializedProperty nameProp;
// cache off GUI content to avoid creating garbage every frame in editor
GUIContent sceneLabel = new GUIContent("Containing Scene", "The target object is expected in this scene asset.");
GUIContent clearButtonGUI = new GUIContent("Clear", "Remove Cross Scene Reference");
// add an extra line to display source scene for targets
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return base.GetPropertyHeight(property, label) + EditorGUIUtility.singleLineHeight;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
guidProp = property.FindPropertyRelative("serializedGuid");
nameProp = property.FindPropertyRelative("cachedName");
sceneProp = property.FindPropertyRelative("cachedScene");
// Using BeginProperty / EndProperty on the parent property means that
// prefab override logic works on the entire property.
EditorGUI.BeginProperty(position, label, property);
position.height = EditorGUIUtility.singleLineHeight;
// Draw prefix label, returning the new rect we can draw in
var guidCompPosition = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
System.Guid currentGuid;
GameObject currentGO = null;
// working with array properties is a bit unwieldy
// you have to get the property at each index manually
byte[] byteArray = new byte[16];
int arraySize = guidProp.arraySize;
for( int i = 0; i < arraySize; ++i )
{
var byteProp = guidProp.GetArrayElementAtIndex(i);
byteArray[i] = (byte)byteProp.intValue;
}
currentGuid = new System.Guid(byteArray);
currentGO = GuidManager.ResolveGuid(currentGuid);
GuidComponent currentGuidComponent = currentGO != null ? currentGO.GetComponent<GuidComponent>() : null;
GuidComponent component = null;
if (currentGuid != System.Guid.Empty && currentGuidComponent == null)
{
// if our reference is set, but the target isn't loaded, we display the target and the scene it is in, and provide a way to clear the reference
float buttonWidth = 55.0f;
guidCompPosition.xMax -= buttonWidth;
bool guiEnabled = GUI.enabled;
GUI.enabled = false;
EditorGUI.LabelField(guidCompPosition, new GUIContent(nameProp.stringValue, "Target GameObject is not currently loaded."), EditorStyles.objectField);
GUI.enabled = guiEnabled;
Rect clearButtonRect = new Rect(guidCompPosition);
clearButtonRect.xMin = guidCompPosition.xMax;
clearButtonRect.xMax += buttonWidth;
if (GUI.Button(clearButtonRect, clearButtonGUI, EditorStyles.miniButton))
{
ClearPreviousGuid();
}
}
else
{
// if our object is loaded, we can simply use an object field directly
component = EditorGUI.ObjectField(guidCompPosition, currentGuidComponent, typeof(GuidComponent), true) as GuidComponent;
}
if (currentGuidComponent != null && component == null)
{
ClearPreviousGuid();
}
// if we have a valid reference, draw the scene name of the scene it lives in so users can find it
if (component != null)
{
nameProp.stringValue = component.name;
string scenePath = component.gameObject.scene.path;
sceneProp.objectReferenceValue = AssetDatabase.LoadAssetAtPath<SceneAsset>(scenePath);
// only update the GUID Prop if something changed. This fixes multi-edit on GUID References
if (component != currentGuidComponent)
{
byteArray = component.GetGuid().ToByteArray();
arraySize = guidProp.arraySize;
for (int i = 0; i < arraySize; ++i)
{
var byteProp = guidProp.GetArrayElementAtIndex(i);
byteProp.intValue = byteArray[i];
}
}
}
EditorGUI.indentLevel++;
position.y += EditorGUIUtility.singleLineHeight;
bool cachedGUIState = GUI.enabled;
GUI.enabled = false;
EditorGUI.ObjectField(position, sceneLabel, sceneProp.objectReferenceValue, typeof(SceneAsset), false);
GUI.enabled = cachedGUIState;
EditorGUI.indentLevel--;
EditorGUI.EndProperty();
}
void ClearPreviousGuid()
{
nameProp.stringValue = string.Empty;
sceneProp.objectReferenceValue = null;
int arraySize = guidProp.arraySize;
for (int i = 0; i < arraySize; ++i)
{
var byteProp = guidProp.GetArrayElementAtIndex(i);
byteProp.intValue = 0;
}
}
}