// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik // #if UNITY_EDITOR using System; using UnityEditor; using UnityEngine; namespace Animancer.Editor { /// [Editor-Only] A custom Inspector for s. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/BaseAnimancerComponentEditor /// public abstract class BaseAnimancerComponentEditor : UnityEditor.Editor { /************************************************************************************************************************/ [NonSerialized] private IAnimancerComponent[] _Targets; /// casted to . public IAnimancerComponent[] Targets => _Targets; /// The drawer for the . private readonly AnimancerPlayableDrawer PlayableDrawer = new AnimancerPlayableDrawer(); /************************************************************************************************************************/ /// Initializes this . protected virtual void OnEnable() { var targets = this.targets; _Targets = new IAnimancerComponent[targets.Length]; GatherTargets(); } /************************************************************************************************************************/ /// /// Copies the into the array. /// private void GatherTargets() { for (int i = 0; i < _Targets.Length; i++) _Targets[i] = (IAnimancerComponent)targets[i]; } /************************************************************************************************************************/ /// Called by the Unity editor to draw the custom Inspector GUI elements. public override void OnInspectorGUI() { _LastRepaintTime = EditorApplication.timeSinceStartup; // Normally the targets wouldn't change after OnEnable, but the trick AnimancerComponent.Reset uses to // swap the type of an existing component when a new one is added causes the old target to be destroyed. GatherTargets(); serializedObject.Update(); var area = GUILayoutUtility.GetRect(0, 0); DoOtherFieldsGUI(); PlayableDrawer.DoGUI(_Targets); area.yMax = GUILayoutUtility.GetLastRect().yMax; AnimancerLayerDrawer.HandleDragAndDropAnimations(area, _Targets[0], 0); serializedObject.ApplyModifiedProperties(); } /************************************************************************************************************************/ [NonSerialized] private double _LastRepaintTime = double.NegativeInfinity; /// /// If we have only one object selected and are in Play Mode, we need to constantly repaint to keep the /// Inspector up to date with the latest details. /// public override bool RequiresConstantRepaint() { if (_Targets.Length != 1) return false; var target = _Targets[0]; if (!target.IsPlayableInitialized) { if (!EditorApplication.isPlaying || target.Animator == null || target.Animator.runtimeAnimatorController == null) return false; } if (AnimancerPlayableDrawer.RepaintConstantly) return true; return EditorApplication.timeSinceStartup > _LastRepaintTime + AnimancerSettings.InspectorRepaintInterval; } /************************************************************************************************************************/ /// Draws the rest of the Inspector fields after the Animator field. protected void DoOtherFieldsGUI() { var property = serializedObject.GetIterator(); if (!property.NextVisible(true)) return; do { var path = property.propertyPath; if (path == "m_Script") continue; using (ObjectPool.Disposable.AcquireContent(out var label, property)) { // Let the target try to override. if (DoOverridePropertyGUI(path, property, label)) continue; // Otherwise draw the property normally. EditorGUILayout.PropertyField(property, label, true); } } while (property.NextVisible(false)); } /************************************************************************************************************************/ /// [Editor-Only] /// Draws any custom GUI for the `property`. /// The return value indicates whether the GUI should replace the regular call to /// or /// not. True = GUI was drawn, so don't draw the regular GUI. False = Draw the regular GUI. /// protected virtual bool DoOverridePropertyGUI(string path, SerializedProperty property, GUIContent label) => false; /************************************************************************************************************************/ } } #endif