134 lines
5.4 KiB
C#
134 lines
5.4 KiB
C#
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|