// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
#if UNITY_EDITOR
using System;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor
{
/// [Editor-Only] A custom Inspector for .
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/SoloAnimationEditor
[UnityEditor.CustomEditor(typeof(SoloAnimation), true), UnityEditor.CanEditMultipleObjects]
public class SoloAnimationEditor : UnityEditor.Editor
{
/************************************************************************************************************************/
/// The .
[field: NonSerialized]
public SoloAnimation Target { get; private set; }
/// The .
[field: NonSerialized]
public Object[] Targets { get; private set; }
/// The animator referenced by each target.
[NonSerialized]
private Animator[] _Animators;
/// A encapsulating the .
[NonSerialized]
private UnityEditor.SerializedObject _SerializedAnimator;
/// The property.
[NonSerialized]
private UnityEditor.SerializedProperty _KeepStateOnDisable;
/// The backing field of the property.
private const string KeeyStateOnDisableField = "m_KeepAnimatorStateOnDisable";
/************************************************************************************************************************/
/// Initializes the targets.
protected virtual void OnEnable()
{
Target = (SoloAnimation)target;
Targets = targets;
}
/************************************************************************************************************************/
///
public override void OnInspectorGUI()
{
DoSerializedFieldsGUI();
RefreshSerializedAnimator();
DoStopOnDisableGUI();
DoRuntimeDetailsGUI();
}
/************************************************************************************************************************/
/// Draws the target's serialized fields.
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();
}
/************************************************************************************************************************/
/// Ensures that the cached references relating to the target's are correct.
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);
}
/************************************************************************************************************************/
///
/// Draws a toggle inverted from the field.
///
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);
}
}
}
/************************************************************************************************************************/
/// Draws the target's runtime details.
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();
}
/************************************************************************************************************************/
/// Cleans up cached references relating to the target's .
protected virtual void OnDisable()
{
if (_SerializedAnimator != null)
{
_SerializedAnimator.Dispose();
_SerializedAnimator = null;
_KeepStateOnDisable = null;
}
}
/************************************************************************************************************************/
}
}
#endif