// 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