com.alicizax.kybernetik.ani.../Editor/GUI/Object Editors/Animancer Component/NamedAnimancerComponentEditor.cs
陈思海 3e7c253249 init
2025-01-08 15:26:57 +08:00

279 lines
10 KiB
C#

// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] A custom Inspector for <see cref="NamedAnimancerComponent"/>s.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/NamedAnimancerComponentEditor
///
[CustomEditor(typeof(NamedAnimancerComponent), true), CanEditMultipleObjects]
public class NamedAnimancerComponentEditor : AnimancerComponentEditor
{
/************************************************************************************************************************/
/// <inheritdoc/>
protected override bool DoOverridePropertyGUI(string path, SerializedProperty property, GUIContent label)
{
switch (path)
{
case "_PlayAutomatically":
if (ShouldShowAnimationFields())
DoDefaultAnimationField(property);
return true;
case "_Animations":
if (ShouldShowAnimationFields())
DoAnimationsField(property);
return true;
default:
return base.DoOverridePropertyGUI(path, property, label);
}
}
/************************************************************************************************************************/
/// <summary>
/// The <see cref="NamedAnimancerComponent.PlayAutomatically"/> and
/// <see cref="NamedAnimancerComponent.Animations"/> fields are only used on startup, so we don't need to show
/// them in Play Mode after the object is already enabled.
/// </summary>
private bool ShouldShowAnimationFields()
{
if (!EditorApplication.isPlayingOrWillChangePlaymode)
return true;
for (int i = 0; i < Targets.Length; i++)
if (!Targets[i].IsGraphInitialized)
return true;
return false;
}
/************************************************************************************************************************/
private void DoDefaultAnimationField(SerializedProperty playAutomatically)
{
var area = AnimancerGUI.LayoutSingleLineRect();
var playAutomaticallyWidth = EditorGUIUtility.labelWidth + AnimancerGUI.ToggleWidth;
var playAutomaticallyArea = AnimancerGUI.StealFromLeft(ref area, playAutomaticallyWidth);
using (var label = PooledGUIContent.Acquire(playAutomatically))
EditorGUI.PropertyField(playAutomaticallyArea, playAutomatically, label);
SerializedProperty firstElement;
AnimationClip clip;
var animations = serializedObject.FindProperty("_Animations");
if (animations.arraySize > 0)
{
firstElement = animations.GetArrayElementAtIndex(0);
clip = (AnimationClip)firstElement.objectReferenceValue;
EditorGUI.BeginProperty(area, null, firstElement);
}
else
{
firstElement = null;
clip = null;
EditorGUI.BeginProperty(area, null, animations);
}
EditorGUI.BeginChangeCheck();
var indentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
clip = AnimancerGUI.DoObjectFieldGUI(area, GUIContent.none, clip, true);
EditorGUI.indentLevel = indentLevel;
if (EditorGUI.EndChangeCheck())
{
if (clip != null)
{
if (firstElement == null)
{
animations.arraySize = 1;
firstElement = animations.GetArrayElementAtIndex(0);
}
firstElement.objectReferenceValue = clip;
}
else
{
if (firstElement == null || animations.arraySize == 1)
animations.arraySize = 0;
else
firstElement.objectReferenceValue = clip;
}
}
EditorGUI.EndProperty();
}
/************************************************************************************************************************/
private ReorderableList _Animations;
private static int _RemoveAnimationIndex;
private void DoAnimationsField(SerializedProperty property)
{
GUILayout.Space(AnimancerGUI.StandardSpacing - 1);
_Animations ??= new(property.serializedObject, property.Copy())
{
drawHeaderCallback = DrawAnimationsHeader,
drawElementCallback = DrawAnimationElement,
elementHeight = AnimancerGUI.LineHeight,
onRemoveCallback = RemoveSelectedElement,
};
_RemoveAnimationIndex = -1;
GUILayout.BeginVertical();
_Animations.DoLayoutList();
GUILayout.EndVertical();
if (_RemoveAnimationIndex >= 0)
property.DeleteArrayElementAtIndex(_RemoveAnimationIndex);
HandleDragAndDropToAddAnimations(GUILayoutUtility.GetLastRect(), property);
}
/************************************************************************************************************************/
private SerializedProperty _AnimationsArraySize;
private void DrawAnimationsHeader(Rect area)
{
var labelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth -= 6;
area.width += 5;
var property = _Animations.serializedProperty;
using (var label = PooledGUIContent.Acquire(property))
{
var propertyLabel = EditorGUI.BeginProperty(area, label, property);
if (_AnimationsArraySize == null)
{
_AnimationsArraySize = property.Copy();
_AnimationsArraySize.Next(true);
_AnimationsArraySize.Next(true);
}
EditorGUI.PropertyField(area, _AnimationsArraySize, propertyLabel);
EditorGUI.EndProperty();
}
EditorGUIUtility.labelWidth = labelWidth;
}
/************************************************************************************************************************/
private static readonly HashSet<Object>
PreviousAnimations = new();
private void DrawAnimationElement(Rect area, int index, bool isActive, bool isFocused)
{
if (index == 0)
PreviousAnimations.Clear();
var labelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth -= 20;
var element = _Animations.serializedProperty.GetArrayElementAtIndex(index);
var color = GUI.color;
var animation = element.objectReferenceValue;
if (animation == null || PreviousAnimations.Contains(animation))
GUI.color = AnimancerGUI.WarningFieldColor;
else
PreviousAnimations.Add(animation);
EditorGUI.BeginChangeCheck();
EditorGUI.ObjectField(area, element, GUIContent.none);
if (EditorGUI.EndChangeCheck() && element.objectReferenceValue == null)
_RemoveAnimationIndex = index;
GUI.color = color;
EditorGUIUtility.labelWidth = labelWidth;
}
/************************************************************************************************************************/
private static void RemoveSelectedElement(ReorderableList list)
{
var property = list.serializedProperty;
var element = property.GetArrayElementAtIndex(list.index);
// Deleting a non-null element sets it to null, so we make sure it's null to actually remove it.
if (element.objectReferenceValue != null)
element.objectReferenceValue = null;
property.DeleteArrayElementAtIndex(list.index);
if (list.index >= property.arraySize - 1)
list.index = property.arraySize - 1;
}
/************************************************************************************************************************/
private static DragAndDropHandler<object> _DropToAddAnimations;
private static SerializedProperty _DropToAddAnimationsProperty;
private static void HandleDragAndDropToAddAnimations(Rect area, SerializedProperty property)
{
_DropToAddAnimationsProperty = property;
_DropToAddAnimations ??= (obj, isDrop) =>
{
using (ListPool<AnimationClip>.Instance.Acquire(out var clips))
{
clips.GatherFromSource(obj);
var anyValid = false;
for (int i = 0; i < clips.Count; i++)
{
var clip = clips[i];
if (clip.legacy)
continue;
if (!isDrop)
return true;
anyValid = true;
var targetProperty = _DropToAddAnimationsProperty;
var index = targetProperty.arraySize;
targetProperty.arraySize = index + 1;
var element = targetProperty.GetArrayElementAtIndex(index);
element.objectReferenceValue = clip;
targetProperty.serializedObject.ApplyModifiedProperties();
}
return anyValid;
}
};
_DropToAddAnimations.Handle(area);
}
/************************************************************************************************************************/
}
}
#endif