SoloAnimationEditor.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR
  3. using System;
  4. using UnityEngine;
  5. using Object = UnityEngine.Object;
  6. namespace Animancer.Editor
  7. {
  8. /// <summary>[Editor-Only] A custom Inspector for <see cref="SoloAnimation"/>.</summary>
  9. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/SoloAnimationEditor
  10. [UnityEditor.CustomEditor(typeof(SoloAnimation), true), UnityEditor.CanEditMultipleObjects]
  11. public class SoloAnimationEditor : UnityEditor.Editor
  12. {
  13. /************************************************************************************************************************/
  14. /// <summary>The <see cref="UnityEditor.Editor.target"/>.</summary>
  15. [field: NonSerialized]
  16. public SoloAnimation Target { get; private set; }
  17. /// <summary>The <see cref="UnityEditor.Editor.targets"/>.</summary>
  18. [field: NonSerialized]
  19. public Object[] Targets { get; private set; }
  20. /// <summary>The animator referenced by each target.</summary>
  21. [NonSerialized]
  22. private Animator[] _Animators;
  23. /// <summary>A <see cref="UnityEditor.SerializedObject"/> encapsulating the <see cref="_Animators"/>.</summary>
  24. [NonSerialized]
  25. private UnityEditor.SerializedObject _SerializedAnimator;
  26. /// <summary>The <see cref="Animator.keepAnimatorStateOnDisable"/> property.</summary>
  27. [NonSerialized]
  28. private UnityEditor.SerializedProperty _KeepStateOnDisable;
  29. /// <summary>The backing field of the <see cref="Animator.keepAnimatorStateOnDisable"/> property.</summary>
  30. private const string KeeyStateOnDisableField = "m_KeepAnimatorStateOnDisable";
  31. /************************************************************************************************************************/
  32. /// <summary>Initializes the targets.</summary>
  33. protected virtual void OnEnable()
  34. {
  35. Target = (SoloAnimation)target;
  36. Targets = targets;
  37. }
  38. /************************************************************************************************************************/
  39. /// <inheritdoc/>
  40. public override void OnInspectorGUI()
  41. {
  42. DoSerializedFieldsGUI();
  43. RefreshSerializedAnimator();
  44. DoStopOnDisableGUI();
  45. DoRuntimeDetailsGUI();
  46. }
  47. /************************************************************************************************************************/
  48. /// <summary>Draws the target's serialized fields.</summary>
  49. private void DoSerializedFieldsGUI()
  50. {
  51. serializedObject.Update();
  52. var property = serializedObject.GetIterator();
  53. property.NextVisible(true);
  54. if (property.name != "m_Script")
  55. UnityEditor.EditorGUILayout.PropertyField(property, true);
  56. while (property.NextVisible(false))
  57. {
  58. UnityEditor.EditorGUILayout.PropertyField(property, true);
  59. }
  60. serializedObject.ApplyModifiedProperties();
  61. }
  62. /************************************************************************************************************************/
  63. /// <summary>Ensures that the cached references relating to the target's <see cref="Animator"/> are correct.</summary>
  64. private void RefreshSerializedAnimator()
  65. {
  66. AnimancerUtilities.SetLength(ref _Animators, Targets.Length);
  67. var dirty = false;
  68. var hasAll = true;
  69. for (int i = 0; i < _Animators.Length; i++)
  70. {
  71. var animator = (Targets[i] as SoloAnimation).Animator;
  72. if (_Animators[i] != animator)
  73. {
  74. _Animators[i] = animator;
  75. dirty = true;
  76. }
  77. if (animator == null)
  78. hasAll = false;
  79. }
  80. if (!dirty)
  81. return;
  82. OnDisable();
  83. if (!hasAll)
  84. return;
  85. _SerializedAnimator = new(_Animators);
  86. _KeepStateOnDisable = _SerializedAnimator.FindProperty(KeeyStateOnDisableField);
  87. }
  88. /************************************************************************************************************************/
  89. /// <summary>
  90. /// Draws a toggle inverted from the <see cref="Animator.keepAnimatorStateOnDisable"/> field.
  91. /// </summary>
  92. private void DoStopOnDisableGUI()
  93. {
  94. var area = AnimancerGUI.LayoutSingleLineRect();
  95. using (var label = PooledGUIContent.Acquire("Stop On Disable",
  96. "If true, disabling this object will stop and rewind the animation." +
  97. " Otherwise it will simply be paused so it can resume from there when re-enabled."))
  98. {
  99. if (_KeepStateOnDisable != null)
  100. {
  101. _KeepStateOnDisable.serializedObject.Update();
  102. var content = UnityEditor.EditorGUI.BeginProperty(area, label, _KeepStateOnDisable);
  103. _KeepStateOnDisable.boolValue = !UnityEditor.EditorGUI.Toggle(area, content, !_KeepStateOnDisable.boolValue);
  104. UnityEditor.EditorGUI.EndProperty();
  105. _KeepStateOnDisable.serializedObject.ApplyModifiedProperties();
  106. }
  107. else
  108. {
  109. label.tooltip = $"Unable to locate field: {nameof(Animator)}.{KeeyStateOnDisableField}";
  110. using (new UnityEditor.EditorGUI.DisabledScope(true))
  111. UnityEditor.EditorGUI.Toggle(area, label, false);
  112. }
  113. }
  114. }
  115. /************************************************************************************************************************/
  116. /// <summary>Draws the target's runtime details.</summary>
  117. private void DoRuntimeDetailsGUI()
  118. {
  119. if (Targets.Length != 1)
  120. return;
  121. if (!UnityEditor.EditorApplication.isPlaying &&
  122. !Target.ApplyInEditMode)
  123. return;
  124. AnimancerGUI.BeginVerticalBox(GUI.skin.box);
  125. if (!Target.IsInitialized)
  126. {
  127. GUILayout.Label("Not Initialized");
  128. }
  129. else
  130. {
  131. UnityEditor.EditorGUILayout.LabelField("Playable Graph", "Not Serialized");
  132. UnityEditor.EditorGUI.BeginChangeCheck();
  133. var isPlaying = UnityEditor.EditorGUILayout.Toggle("Is Playing", Target.IsPlaying);
  134. if (UnityEditor.EditorGUI.EndChangeCheck())
  135. Target.IsPlaying = isPlaying;
  136. UnityEditor.EditorGUI.BeginChangeCheck();
  137. var time = UnityEditor.EditorGUILayout.FloatField("Time", Target.Time);
  138. if (UnityEditor.EditorGUI.EndChangeCheck())
  139. {
  140. Target.Time = time;
  141. Target.Evaluate();
  142. }
  143. time = AnimancerUtilities.Wrap01(Target.NormalizedTime);
  144. if (time == 0 && Target.Time != 0)
  145. time = 1;
  146. UnityEditor.EditorGUI.BeginChangeCheck();
  147. time = UnityEditor.EditorGUILayout.Slider("Normalized Time", time, 0, 1);
  148. if (UnityEditor.EditorGUI.EndChangeCheck())
  149. {
  150. Target.NormalizedTime = time;
  151. Target.Evaluate();
  152. }
  153. }
  154. AnimancerGUI.EndVerticalBox(GUI.skin.box);
  155. Repaint();
  156. }
  157. /************************************************************************************************************************/
  158. /// <summary>Cleans up cached references relating to the target's <see cref="Animator"/>.</summary>
  159. protected virtual void OnDisable()
  160. {
  161. if (_SerializedAnimator != null)
  162. {
  163. _SerializedAnimator.Dispose();
  164. _SerializedAnimator = null;
  165. _KeepStateOnDisable = null;
  166. }
  167. }
  168. /************************************************************************************************************************/
  169. }
  170. }
  171. #endif