// Animancer // Copyright 2020 Kybernetik // #if UNITY_EDITOR using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace Animancer.Editor { /// <summary>[Editor-Only] Draws the Inspector GUI for an <see cref="IAnimancerComponent.Playable"/>.</summary> public sealed class AnimancerPlayableDrawer { /************************************************************************************************************************/ /// <summary>Only get <see cref="AnimancerPlayable.IsGraphPlaying"/> during <see cref="EventType.Layout"/>.</summary> private bool _IsGraphPlaying; /// <summary>A lazy list of information about the layers currently being displayed.</summary> private readonly List<AnimancerLayerDrawer> LayerInfos = new List<AnimancerLayerDrawer>(); /// <summary>The number of elements in <see cref="LayerInfos"/> that are currently being used.</summary> private int _LayerCount; /************************************************************************************************************************/ /// <summary>Draws the GUI of the <see cref="IAnimancerComponent.Playable"/> if there is only one target.</summary> public void DoGUI(IAnimancerComponent[] targets) { if (targets.Length != 1) return; DoGUI(targets[0]); } /************************************************************************************************************************/ /// <summary>Draws the GUI of the <see cref="IAnimancerComponent.Playable"/>.</summary> public void DoGUI(IAnimancerComponent target) { if (!target.IsPlayableInitialised) { DoPlayableNotInitialisedGUI(target); return; } EditorGUI.BeginChangeCheck(); // Gather the during the layout event and use the same ones during subsequent events to avoid GUI errors // in case they change (they shouldn't, but this is also more efficient). if (Event.current.type == EventType.Layout) { AnimancerLayerDrawer.GatherLayerEditors(target.Playable, LayerInfos, out _LayerCount); _IsGraphPlaying = target.Playable.IsGraphPlaying; } if (!_IsGraphPlaying) { AnimancerGUI.BeginVerticalBox(GUI.skin.box); _IsGraphPlaying = EditorGUILayout.Toggle("Is Graph Playing", _IsGraphPlaying); AnimancerGUI.EndVerticalBox(GUI.skin.box); if (_IsGraphPlaying) target.Playable.UnpauseGraph(); } for (int i = 0; i < _LayerCount; i++) { LayerInfos[i].DoGUI(target); } DoLayerWeightWarningGUI(); if (AnimancerLayerDrawer.ShowUpdatingNodes) target.Playable.DoUpdateListGUI(); if (EditorGUI.EndChangeCheck() && !_IsGraphPlaying) target.Playable.Evaluate(); } /************************************************************************************************************************/ private void DoPlayableNotInitialisedGUI(IAnimancerComponent target) { if (!EditorApplication.isPlaying || target.Animator == null || EditorUtility.IsPersistent(target.Animator)) return; EditorGUILayout.HelpBox("Playable is not initialised." + " It will be initialised automatically when something needs it, such as playing an animation.", MessageType.Info); if (AnimancerGUI.TryUseClickEventInLastRect(1)) { var menu = new GenericMenu(); menu.AddItem(new GUIContent("Initialise"), false, () => target.Playable.Evaluate()); AnimancerEditorUtilities.AddDocumentationLink(menu, "Layer Documentation", "/docs/manual/blending/layers"); menu.ShowAsContext(); } } /************************************************************************************************************************/ private void DoLayerWeightWarningGUI() { for (int i = 0; i < _LayerCount; i++) { var layer = LayerInfos[i].Target; if (layer.Weight == 1 && !layer.IsAdditive && layer._Mask == null && Mathf.Approximately(layer.GetTotalWeight(), 1)) return; } EditorGUILayout.HelpBox( "There are no Override layers at weight 1, which will likely give undesirable results." + " Click here for more information.", MessageType.Warning); if (AnimancerGUI.TryUseClickEventInLastRect()) EditorUtility.OpenWithDefaultApp(Strings.DocsURLs.Fading); } /************************************************************************************************************************/ } } #endif