2025-05-09 15:40:34 +08:00

222 lines
8.2 KiB
C#

// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
#if UNITY_EDITOR
using System;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] A custom Inspector for <see cref="SoloAnimation"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/SoloAnimationEditor
[UnityEditor.CustomEditor(typeof(SoloAnimation), true), UnityEditor.CanEditMultipleObjects]
public class SoloAnimationEditor : UnityEditor.Editor
{
/************************************************************************************************************************/
/// <summary>The <see cref="UnityEditor.Editor.target"/>.</summary>
[field: NonSerialized]
public SoloAnimation Target { get; private set; }
/// <summary>The <see cref="UnityEditor.Editor.targets"/>.</summary>
[field: NonSerialized]
public Object[] Targets { get; private set; }
/// <summary>The animator referenced by each target.</summary>
[NonSerialized]
private Animator[] _Animators;
/// <summary>A <see cref="UnityEditor.SerializedObject"/> encapsulating the <see cref="_Animators"/>.</summary>
[NonSerialized]
private UnityEditor.SerializedObject _SerializedAnimator;
/// <summary>The <see cref="Animator.keepAnimatorStateOnDisable"/> property.</summary>
[NonSerialized]
private UnityEditor.SerializedProperty _KeepStateOnDisable;
/// <summary>The backing field of the <see cref="Animator.keepAnimatorStateOnDisable"/> property.</summary>
private const string KeeyStateOnDisableField = "m_KeepAnimatorStateOnDisable";
/************************************************************************************************************************/
/// <summary>Initializes the targets.</summary>
protected virtual void OnEnable()
{
Target = (SoloAnimation)target;
Targets = targets;
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnInspectorGUI()
{
DoSerializedFieldsGUI();
RefreshSerializedAnimator();
DoStopOnDisableGUI();
DoRuntimeDetailsGUI();
}
/************************************************************************************************************************/
/// <summary>Draws the target's serialized fields.</summary>
private void DoSerializedFieldsGUI()
{
serializedObject.Update();
var property = serializedObject.GetIterator();
property.NextVisible(true);
if (property.name != "m_Script")
UnityEditor.EditorGUILayout.PropertyField(property, true);
while (property.NextVisible(false))
{
UnityEditor.EditorGUILayout.PropertyField(property, true);
}
serializedObject.ApplyModifiedProperties();
}
/************************************************************************************************************************/
/// <summary>Ensures that the cached references relating to the target's <see cref="Animator"/> are correct.</summary>
private void RefreshSerializedAnimator()
{
AnimancerUtilities.SetLength(ref _Animators, Targets.Length);
var dirty = false;
var hasAll = true;
for (int i = 0; i < _Animators.Length; i++)
{
var animator = (Targets[i] as SoloAnimation).Animator;
if (_Animators[i] != animator)
{
_Animators[i] = animator;
dirty = true;
}
if (animator == null)
hasAll = false;
}
if (!dirty)
return;
OnDisable();
if (!hasAll)
return;
_SerializedAnimator = new(_Animators);
_KeepStateOnDisable = _SerializedAnimator.FindProperty(KeeyStateOnDisableField);
}
/************************************************************************************************************************/
/// <summary>
/// Draws a toggle inverted from the <see cref="Animator.keepAnimatorStateOnDisable"/> field.
/// </summary>
private void DoStopOnDisableGUI()
{
var area = AnimancerGUI.LayoutSingleLineRect();
using (var label = PooledGUIContent.Acquire("Stop On Disable",
"If true, disabling this object will stop and rewind the animation." +
" Otherwise it will simply be paused so it can resume from there when re-enabled."))
{
if (_KeepStateOnDisable != null)
{
_KeepStateOnDisable.serializedObject.Update();
var content = UnityEditor.EditorGUI.BeginProperty(area, label, _KeepStateOnDisable);
_KeepStateOnDisable.boolValue = !UnityEditor.EditorGUI.Toggle(area, content, !_KeepStateOnDisable.boolValue);
UnityEditor.EditorGUI.EndProperty();
_KeepStateOnDisable.serializedObject.ApplyModifiedProperties();
}
else
{
label.tooltip = $"Unable to locate field: {nameof(Animator)}.{KeeyStateOnDisableField}";
using (new UnityEditor.EditorGUI.DisabledScope(true))
UnityEditor.EditorGUI.Toggle(area, label, false);
}
}
}
/************************************************************************************************************************/
/// <summary>Draws the target's runtime details.</summary>
private void DoRuntimeDetailsGUI()
{
if (Targets.Length != 1)
return;
if (!UnityEditor.EditorApplication.isPlaying &&
!Target.ApplyInEditMode)
return;
AnimancerGUI.BeginVerticalBox(GUI.skin.box);
if (!Target.IsInitialized)
{
GUILayout.Label("Not Initialized");
}
else
{
UnityEditor.EditorGUILayout.LabelField("Playable Graph", "Not Serialized");
UnityEditor.EditorGUI.BeginChangeCheck();
var isPlaying = UnityEditor.EditorGUILayout.Toggle("Is Playing", Target.IsPlaying);
if (UnityEditor.EditorGUI.EndChangeCheck())
Target.IsPlaying = isPlaying;
UnityEditor.EditorGUI.BeginChangeCheck();
var time = UnityEditor.EditorGUILayout.FloatField("Time", Target.Time);
if (UnityEditor.EditorGUI.EndChangeCheck())
{
Target.Time = time;
Target.Evaluate();
}
time = AnimancerUtilities.Wrap01(Target.NormalizedTime);
if (time == 0 && Target.Time != 0)
time = 1;
UnityEditor.EditorGUI.BeginChangeCheck();
time = UnityEditor.EditorGUILayout.Slider("Normalized Time", time, 0, 1);
if (UnityEditor.EditorGUI.EndChangeCheck())
{
Target.NormalizedTime = time;
Target.Evaluate();
}
}
AnimancerGUI.EndVerticalBox(GUI.skin.box);
Repaint();
}
/************************************************************************************************************************/
/// <summary>Cleans up cached references relating to the target's <see cref="Animator"/>.</summary>
protected virtual void OnDisable()
{
if (_SerializedAnimator != null)
{
_SerializedAnimator.Dispose();
_SerializedAnimator = null;
_KeepStateOnDisable = null;
}
}
/************************************************************************************************************************/
}
}
#endif