BaseAnimancerComponentEditor.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. #if UNITY_EDITOR
  3. using System;
  4. using UnityEditor;
  5. using UnityEngine;
  6. namespace Animancer.Editor
  7. {
  8. /// <summary>[Editor-Only] A custom Inspector for <see cref="IAnimancerComponent"/>s.</summary>
  9. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/BaseAnimancerComponentEditor
  10. ///
  11. public abstract class BaseAnimancerComponentEditor : UnityEditor.Editor
  12. {
  13. /************************************************************************************************************************/
  14. [NonSerialized]
  15. private IAnimancerComponent[] _Targets;
  16. /// <summary><see cref="UnityEditor.Editor.targets"/> casted to <see cref="IAnimancerComponent"/>.</summary>
  17. public IAnimancerComponent[] Targets => _Targets;
  18. /// <summary>The drawer for the <see cref="IAnimancerComponent.Playable"/>.</summary>
  19. private readonly AnimancerPlayableDrawer
  20. PlayableDrawer = new AnimancerPlayableDrawer();
  21. /************************************************************************************************************************/
  22. /// <summary>Initializes this <see cref="UnityEditor.Editor"/>.</summary>
  23. protected virtual void OnEnable()
  24. {
  25. var targets = this.targets;
  26. _Targets = new IAnimancerComponent[targets.Length];
  27. GatherTargets();
  28. }
  29. /************************************************************************************************************************/
  30. /// <summary>
  31. /// Copies the <see cref="UnityEditor.Editor.targets"/> into the <see cref="_Targets"/> array.
  32. /// </summary>
  33. private void GatherTargets()
  34. {
  35. for (int i = 0; i < _Targets.Length; i++)
  36. _Targets[i] = (IAnimancerComponent)targets[i];
  37. }
  38. /************************************************************************************************************************/
  39. /// <summary>Called by the Unity editor to draw the custom Inspector GUI elements.</summary>
  40. public override void OnInspectorGUI()
  41. {
  42. _LastRepaintTime = EditorApplication.timeSinceStartup;
  43. // Normally the targets wouldn't change after OnEnable, but the trick AnimancerComponent.Reset uses to
  44. // swap the type of an existing component when a new one is added causes the old target to be destroyed.
  45. GatherTargets();
  46. serializedObject.Update();
  47. var area = GUILayoutUtility.GetRect(0, 0);
  48. DoOtherFieldsGUI();
  49. PlayableDrawer.DoGUI(_Targets);
  50. area.yMax = GUILayoutUtility.GetLastRect().yMax;
  51. AnimancerLayerDrawer.HandleDragAndDropAnimations(area, _Targets[0], 0);
  52. serializedObject.ApplyModifiedProperties();
  53. }
  54. /************************************************************************************************************************/
  55. [NonSerialized]
  56. private double _LastRepaintTime = double.NegativeInfinity;
  57. /// <summary>
  58. /// If we have only one object selected and are in Play Mode, we need to constantly repaint to keep the
  59. /// Inspector up to date with the latest details.
  60. /// </summary>
  61. public override bool RequiresConstantRepaint()
  62. {
  63. if (_Targets.Length != 1)
  64. return false;
  65. var target = _Targets[0];
  66. if (!target.IsPlayableInitialized)
  67. {
  68. if (!EditorApplication.isPlaying ||
  69. target.Animator == null ||
  70. target.Animator.runtimeAnimatorController == null)
  71. return false;
  72. }
  73. if (AnimancerPlayableDrawer.RepaintConstantly)
  74. return true;
  75. return EditorApplication.timeSinceStartup > _LastRepaintTime + AnimancerSettings.InspectorRepaintInterval;
  76. }
  77. /************************************************************************************************************************/
  78. /// <summary>Draws the rest of the Inspector fields after the Animator field.</summary>
  79. protected void DoOtherFieldsGUI()
  80. {
  81. var property = serializedObject.GetIterator();
  82. if (!property.NextVisible(true))
  83. return;
  84. do
  85. {
  86. var path = property.propertyPath;
  87. if (path == "m_Script")
  88. continue;
  89. using (ObjectPool.Disposable.AcquireContent(out var label, property))
  90. {
  91. // Let the target try to override.
  92. if (DoOverridePropertyGUI(path, property, label))
  93. continue;
  94. // Otherwise draw the property normally.
  95. EditorGUILayout.PropertyField(property, label, true);
  96. }
  97. }
  98. while (property.NextVisible(false));
  99. }
  100. /************************************************************************************************************************/
  101. /// <summary>[Editor-Only]
  102. /// Draws any custom GUI for the `property`.
  103. /// The return value indicates whether the GUI should replace the regular call to
  104. /// <see cref="EditorGUILayout.PropertyField(SerializedProperty, GUIContent, bool, GUILayoutOption[])"/> or
  105. /// not. True = GUI was drawn, so don't draw the regular GUI. False = Draw the regular GUI.
  106. /// </summary>
  107. protected virtual bool DoOverridePropertyGUI(string path, SerializedProperty property, GUIContent label) => false;
  108. /************************************************************************************************************************/
  109. }
  110. }
  111. #endif