123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- #if UNITY_EDITOR && UNITY_IMGUI
- using System;
- using UnityEditor;
- using UnityEngine;
- using static Animancer.Editor.AnimancerGUI;
- namespace Animancer.Editor.Previews
- {
- /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/TransitionPreviewWindow
- partial class TransitionPreviewWindow
- {
- /// <summary>Animation details for the <see cref="TransitionPreviewWindow"/>.</summary>
- /// <remarks>
- /// <strong>Documentation:</strong>
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/transitions#previews">
- /// Previews</see>
- /// </remarks>
- [Serializable]
- internal class Animations
- {
- /************************************************************************************************************************/
- public const string
- PreviousAnimationKey = "Previous Animation",
- NextAnimationKey = "Next Animation";
- /************************************************************************************************************************/
- [NonSerialized] private AnimationClip[] _OtherAnimations;
- [SerializeField]
- private AnimationClip _PreviousAnimation;
- public AnimationClip PreviousAnimation => _PreviousAnimation;
- [SerializeField]
- private AnimationClip _NextAnimation;
- public AnimationClip NextAnimation => _NextAnimation;
- /************************************************************************************************************************/
- private static AnimancerPreviewObject PreviewObject
- => _Instance._Scene.PreviewObject;
- /************************************************************************************************************************/
- public void DoGUI()
- {
- GUILayout.BeginVertical(GUI.skin.box);
- EditorGUILayout.LabelField("Preview Details", "(Not Serialized)");
- var previewObject = PreviewObject;
- AnimancerPreviewObjectGUI.DoModelGUI(previewObject);
- using (var label = PooledGUIContent.Acquire("Previous Animation",
- "The animation for the preview to play before the target transition"))
- {
- DoAnimationFieldGUI(label, ref _PreviousAnimation, (clip) => _PreviousAnimation = clip);
- }
- var graph = previewObject.Graph;
- DoCurrentAnimationGUI(graph);
- using (var label = PooledGUIContent.Acquire("Next Animation",
- "The animation for the preview to play after the target transition"))
- {
- DoAnimationFieldGUI(label, ref _NextAnimation, (clip) => _NextAnimation = clip);
- }
- if (graph != null)
- {
- using (new EditorGUI.DisabledScope(!Transition.IsValid()))
- {
- GUILayout.BeginHorizontal();
- GUILayout.FlexibleSpace();
- if (graph.IsGraphPlaying)
- {
- if (CompactMiniButton(AnimancerIcons.PauseIcon))
- graph.PauseGraph();
- }
- else
- {
- if (CompactMiniButton(AnimancerIcons.StepBackwardIcon))
- StepBackward();
- if (CompactMiniButton(AnimancerIcons.PlayIcon))
- PlaySequence(graph);
- if (CompactMiniButton(AnimancerIcons.StepForwardIcon))
- StepForward();
- }
- GUILayout.FlexibleSpace();
- GUILayout.EndHorizontal();
- }
- }
- GUILayout.EndVertical();
- }
- /************************************************************************************************************************/
- public void GatherAnimations()
- {
- AnimationGatherer.GatherFromGameObject(
- PreviewObject.OriginalObject.gameObject,
- ref _OtherAnimations,
- true);
- if (_OtherAnimations.Length > 0 &&
- (_PreviousAnimation == null || _NextAnimation == null))
- {
- var defaultClip = _OtherAnimations[0];
- var defaultClipIsIdle = false;
- for (int i = 0; i < _OtherAnimations.Length; i++)
- {
- var clip = _OtherAnimations[i];
- if (defaultClipIsIdle && clip.name.Length > defaultClip.name.Length)
- continue;
- if (clip.name.IndexOf("idle", StringComparison.CurrentCultureIgnoreCase) >= 0)
- {
- defaultClip = clip;
- break;
- }
- }
- if (_PreviousAnimation == null)
- _PreviousAnimation = defaultClip;
- if (_NextAnimation == null)
- _NextAnimation = defaultClip;
- }
- }
- /************************************************************************************************************************/
- private void DoAnimationFieldGUI(GUIContent label, ref AnimationClip clip, Action<AnimationClip> setClip)
- {
- var showDropdown = !_OtherAnimations.IsNullOrEmpty();
- var area = LayoutSingleLineRect();
- if (DoDropdownObjectFieldGUI(area, label, showDropdown, ref clip))
- {
- var menu = new GenericMenu();
- menu.AddItem(new("None"), clip == null, () => setClip(null));
- for (int i = 0; i < _OtherAnimations.Length; i++)
- {
- var animation = _OtherAnimations[i];
- menu.AddItem(new(animation.name), animation == clip, () => setClip(animation));
- }
- menu.ShowAsContext();
- }
- }
- /************************************************************************************************************************/
- private void DoCurrentAnimationGUI(AnimancerGraph animancer)
- {
- string text;
- if (animancer != null)
- {
- var transition = Transition;
- if (transition.IsValid() && transition.Key != null)
- text = animancer.States.GetOrCreate(transition).ToString();
- else
- text = transition?.ToString();
- }
- else
- {
- text = _Instance._TransitionProperty.Property.GetFriendlyPath();
- }
- if (text != null)
- EditorGUILayout.LabelField("Current Animation", text);
- }
- /************************************************************************************************************************/
- private void PlaySequence(AnimancerGraph animancer)
- {
- if (_PreviousAnimation != null && _PreviousAnimation.length > 0)
- {
- PreviewObject.Graph.Stop();
- var fromState = animancer.States.GetOrCreate(PreviousAnimationKey, _PreviousAnimation, true);
- animancer.Layers[0].Play(fromState);
- OnPlayAnimation();
- fromState.TimeD = 0;
- var warnings = OptionalWarning.UnsupportedEvents.DisableTemporarily();
- fromState.Events(this).EndEvent = new(1 / fromState.Length, PlayTransition);
- warnings.Enable();
- }
- else
- {
- PlayTransition();
- }
- PreviewObject.Graph.UnpauseGraph();
- }
- private void PlayTransition()
- {
- var transition = Transition;
- var animancer = PreviewObject.Graph;
- animancer.States.TryGet(transition, out var oldState);
- var targetState = animancer.Layers[0].Play(transition);
- OnPlayAnimation();
- if (oldState != null && oldState != targetState)
- oldState.Destroy();
- var warnings = OptionalWarning.UnsupportedEvents.DisableTemporarily();
- targetState.Events(this).OnEnd = () =>
- {
- if (_NextAnimation != null)
- {
- var fadeDuration = AnimancerEvent.GetFadeOutDuration(
- targetState,
- AnimancerGraph.DefaultFadeDuration);
- PlayOther(NextAnimationKey, _NextAnimation, 0, fadeDuration);
- OnPlayAnimation();
- }
- else
- {
- animancer.Layers[0].IncrementCommandCount();
- }
- };
- warnings.Enable();
- }
- /************************************************************************************************************************/
- public void OnPlayAnimation()
- {
- var animancer = PreviewObject.Graph;
- if (animancer == null ||
- animancer.States.Current == null)
- return;
- var state = animancer.States.Current;
- state.RecreatePlayableRecursive();
- var events = state.SharedEvents;
- if (events != null)
- {
- var warnings = OptionalWarning.UnsupportedEvents | OptionalWarning.ProOnly;
- warnings = warnings.DisableTemporarily();
- var normalizedEndTime = events.NormalizedEndTime;
- state.Events(this).NormalizedEndTime = normalizedEndTime;
- warnings.Enable();
- }
- }
- /************************************************************************************************************************/
- private void StepBackward()
- => StepTime(-TransitionPreviewSettings.FrameStep);
- private void StepForward()
- => StepTime(TransitionPreviewSettings.FrameStep);
- private void StepTime(float timeOffset)
- {
- if (!TryShowTransitionPaused(out _, out _, out var state))
- return;
- var length = state.Length;
- if (length != 0)
- timeOffset /= length;
- NormalizedTime += timeOffset;
- }
- /************************************************************************************************************************/
- [SerializeField]
- private float _NormalizedTime;
- public float NormalizedTime
- {
- get => _NormalizedTime;
- set
- {
- if (!value.IsFinite())
- return;
- _NormalizedTime = value;
- if (!TryShowTransitionPaused(out var animancer, out var transition, out var state))
- return;
- var length = state.Length;
- var speed = state.Speed;
- var time = value * length;
- var fadeDuration = transition.FadeDuration * Math.Abs(speed);
- var startTime = TimelineGUI.GetStartTime(transition.NormalizedStartTime, speed, length);
- var normalizedEndTime = state.NormalizedEndTime;
- var endTime = normalizedEndTime * length;
- var fadeOutEnd = TimelineGUI.GetFadeOutEnd(speed, endTime, length);
- if (speed < 0)
- {
- time = length - time;
- startTime = length - startTime;
- value = 1 - value;
- normalizedEndTime = 1 - normalizedEndTime;
- endTime = length - endTime;
- fadeOutEnd = length - fadeOutEnd;
- }
- if (time < startTime)// Previous animation.
- {
- if (_PreviousAnimation != null)
- {
- PlayOther(PreviousAnimationKey, _PreviousAnimation, value);
- value = 0;
- }
- }
- else if (time < startTime + fadeDuration)// Fade from previous animation to the target.
- {
- if (_PreviousAnimation != null)
- {
- var fromState = PlayOther(PreviousAnimationKey, _PreviousAnimation, value);
- state.IsPlaying = true;
- state.Weight = (time - startTime) / fadeDuration;
- fromState.Weight = 1 - state.Weight;
- }
- }
- else if (_NextAnimation != null)
- {
- if (value < normalizedEndTime)
- {
- // Just the main state.
- }
- else
- {
- var toState = PlayOther(NextAnimationKey, _NextAnimation, value - normalizedEndTime);
- if (time < fadeOutEnd)// Fade from the target transition to the next animation.
- {
- state.IsPlaying = true;
- toState.Weight = (time - endTime) / (fadeOutEnd - endTime);
- state.Weight = 1 - toState.Weight;
- }
- // Else just the next animation.
- }
- }
- if (speed < 0)
- value = 1 - value;
- state.MoveTime(state.Weight > 0 ? value : 0, true);
- animancer.Evaluate();
- RepaintEverything();
- }
- }
- /************************************************************************************************************************/
- private bool TryShowTransitionPaused(
- out AnimancerGraph animancer, out ITransitionDetailed transition, out AnimancerState state)
- {
- animancer = PreviewObject.Graph;
- transition = Transition;
- if (animancer == null || !transition.IsValid())
- {
- state = null;
- return false;
- }
- state = animancer.Layers[0].Play(transition, 0);
- OnPlayAnimation();
- animancer.PauseGraph();
- return true;
- }
- /************************************************************************************************************************/
- private AnimancerState PlayOther(
- object key,
- AnimationClip animation,
- float normalizedTime,
- float fadeDuration = 0)
- {
- var animancer = PreviewObject.Graph;
- var state = animancer.States.GetOrCreate(key, animation, true);
- state = animancer.Layers[0].Play(state, fadeDuration);
- OnPlayAnimation();
- normalizedTime *= state.Length;
- state.Time = normalizedTime.IsFinite() ? normalizedTime : 0;
- return state;
- }
- /************************************************************************************************************************/
- internal class WindowMatchStateTime : Updatable
- {
- /************************************************************************************************************************/
- public override void Update()
- {
- if (_Instance == null ||
- !AnimancerGraph.Current.IsGraphPlaying)
- return;
- var transition = Transition;
- if (transition == null)
- return;
- if (AnimancerGraph.Current.States.TryGet(transition, out var state))
- _Instance._Animations._NormalizedTime = state.NormalizedTime;
- }
- /************************************************************************************************************************/
- public override string ToString()
- => nameof(WindowMatchStateTime);
- /************************************************************************************************************************/
- }
- /************************************************************************************************************************/
- }
- }
- }
- #endif
|