123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- #if UNITY_EDITOR
- using System;
- using UnityEditor;
- using UnityEngine;
- namespace Animancer.Editor.Previews
- {
- /// <summary>[Editor-Only] Utility for playing through transition previews.</summary>
- /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/TransitionPreviewPlayer
- [Serializable]
- public class TransitionPreviewPlayer : IDisposable
- {
- /************************************************************************************************************************/
- [SerializeReference] private ITransition _FromTransition;
- [SerializeReference] private ITransition _ToTransition;
- /// <summary>The animation to play first.</summary>
- public ref ITransition FromTransition
- => ref _FromTransition;
- /// <summary>The animation to transition into.</summary>
- public ref ITransition ToTransition
- => ref _ToTransition;
- /************************************************************************************************************************/
- private AnimancerGraph _Graph;
- /// <summary>The graph used to play the animations.</summary>
- public AnimancerGraph Graph
- {
- get => _Graph;
- set
- {
- _Graph = value;
- Evaluate();
- }
- }
- /************************************************************************************************************************/
- /// <summary>
- /// The minimum amount of time to play the <see cref="FromTransition"/>
- /// before the <see cref="ToTransition"/> starts (in seconds).
- /// </summary>
- public float FromDuration { get; set; }
- /// <summary>
- /// The minimum amount of time to continue playing
- /// after the <see cref="ToTransition"/> started (in seconds).
- /// </summary>
- public float ToDuration { get; set; }
- /// <summary>The speed at which the preview plays.</summary>
- public float Speed { get; set; } = 1;
- /************************************************************************************************************************/
- private float _FadeDuration = float.NaN;
- /// <summary>The <see cref="ITransition.FadeDuration"/>.</summary>
- /// <remarks><see cref="float.NaN"/> uses the value from the <see cref="ToTransition"/>.</remarks>
- public float FadeDuration
- {
- get => float.IsNaN(_FadeDuration) && ToTransition.IsValid()
- ? ToTransition.FadeDuration
- : _FadeDuration;
- set => _FadeDuration = value;
- }
- /************************************************************************************************************************/
- /// <summary>The lowest allowed <see cref="CurrentTime"/>.</summary>
- /// <remarks>
- /// This is the lower of the negative <see cref="FromDuration"/>
- /// or the negative duration of the <see cref="FromTransition"/>.
- /// </remarks>
- public float MinTime { get; private set; }
- /// <summary>The highest allowed <see cref="CurrentTime"/>.</summary>
- /// <remarks>
- /// This is the higher of the <see cref="ToDuration"/> or <see cref="FadeDuration"/>
- /// or the duration of the <see cref="ToTransition"/>.
- /// </remarks>
- public float MaxTime { get; private set; }
- /************************************************************************************************************************/
- /// <summary>Recalculated the <see cref="MinTime"/> and <see cref="MaxTime"/>.</summary>
- public void RecalculateTimeBounds()
- {
- MinTime = -FromDuration;
- if (_FromTransition.IsValid() &&
- AnimancerUtilities.TryCalculateDuration(_FromTransition, out var duration))
- MinTime = Math.Min(MinTime, -duration);
- MaxTime = ToDuration;
- var fadeDuration = FadeDuration;
- if (!float.IsNaN(fadeDuration))
- MaxTime = Math.Max(ToDuration, fadeDuration);
- if (_ToTransition.IsValid() &&
- AnimancerUtilities.TryCalculateDuration(_ToTransition, out duration))
- MaxTime = Math.Max(MaxTime, duration);
- }
- /************************************************************************************************************************/
- /// <summary>Converts normalized time to seconds.</summary>
- public float LerpTimeUnclamped(float normalizedTime)
- => Mathf.LerpUnclamped(MinTime, MaxTime, normalizedTime);
- /// <summary>Converts seconds to normalized time.</summary>
- public float InverseLerpTimeUnclamped(float time)
- => AnimancerUtilities.InverseLerpUnclamped(MinTime, MaxTime, time);
- /************************************************************************************************************************/
- private float _CurrentTime = float.NaN;
- /// <summary>The amount of time that has passed since the <see cref="ToTransition"/> started (in seconds).</summary>
- /// <remarks>This value goes from the <see cref="MinTime"/> to the <see cref="MaxTime"/>.</remarks>
- public float CurrentTime
- {
- get => float.IsNaN(_CurrentTime)
- ? MinTime
- : _CurrentTime;
- set
- {
- _CurrentTime = value;
- Evaluate();
- }
- }
- /// <summary>The amount of time that has passed since the <see cref="ToTransition"/> started (normalized).</summary>
- /// <remarks>0 is at the <see cref="MinTime"/> and 1 is at the <see cref="MaxTime"/>.</remarks>
- public float NormalizedTime
- {
- get => InverseLerpTimeUnclamped(CurrentTime);
- set => CurrentTime = LerpTimeUnclamped(value);
- }
- /************************************************************************************************************************/
- private bool _IsPlaying;
- /// <summary>Is the preview currently playing?</summary>
- public bool IsPlaying
- {
- get => _IsPlaying;
- set
- {
- if (_IsPlaying == value)
- return;
- _IsPlaying = value;
- if (_IsPlaying)
- {
- _LastUpdateTime = TimeSinceStartup;
- EditorApplication.update += Update;
- }
- else
- {
- EditorApplication.update -= Update;
- }
- }
- }
- /// <summary>Cleans up this player.</summary>
- public void Dispose()
- => IsPlaying = false;
- /************************************************************************************************************************/
- /// <summary><see cref="EditorApplication.timeSinceStartup"/></summary>
- private static double TimeSinceStartup
- => EditorApplication.timeSinceStartup;
- private double _LastUpdateTime;
- /// <summary>Updates the preview time while playing.</summary>
- private void Update()
- {
- if (Graph == null || !Graph.IsValidOrDispose())
- {
- EditorApplication.update -= Update;
- return;
- }
- var time = TimeSinceStartup;
- var deltaTime = time - _LastUpdateTime;
- _LastUpdateTime = time;
- CurrentTime += ((float)deltaTime) * Speed;
- AnimancerGUI.RepaintEverything();
- }
- /************************************************************************************************************************/
- /// <summary>Applies the animations at the <see cref="CurrentTime"/>.</summary>
- private void Evaluate()
- {
- if (Graph == null)
- return;
- Graph.PauseGraph();
- Graph.Stop();
- if (_FromTransition.IsValid())
- {
- if (_ToTransition.IsValid())
- {
- Apply(CurrentTime, _FromTransition, _ToTransition);
- }
- else
- {
- var minTime = MinTime;
- Apply(CurrentTime - minTime, -minTime, _FromTransition);
- }
- }
- else
- {
- if (_ToTransition.IsValid())
- Apply(CurrentTime, MaxTime, _ToTransition);
- else
- return;
- }
- Graph.Evaluate();
- }
- /************************************************************************************************************************/
- /// <summary>Applies the animations at the `currentTime`.</summary>
- private void Apply(float currentTime, ITransition from, ITransition to)
- {
- var layer = Graph.Layers[0];
- // Playing From.
- if (currentTime < 0)
- {
- var state = layer.Play(from);
- state.Time += state.NormalizedEndTime * state.Length + currentTime;
- return;
- }
- var maxTime = MaxTime;
- // Fading.
- if (currentTime < maxTime)
- {
- var state = layer.Play(from);
- state.Time += state.NormalizedEndTime * state.Length + currentTime;
- state = layer.Play(to, FadeDuration, to.FadeMode);
- state.Time += currentTime;
- var fade = state.FadeGroup;
- if (fade != null)
- {
- fade.NormalizedTime += currentTime * fade.FadeSpeed;
- fade.ApplyWeights();
- }
- }
- // Playing To.
- else
- {
- var state = layer.Play(to);
- state.Time += currentTime;
- // Finished.
- if (currentTime >= maxTime)
- {
- _CurrentTime = MinTime;
- state.Time = _CurrentTime;
- }
- }
- }
- /************************************************************************************************************************/
- /// <summary>Applies the animation at the `currentTime`.</summary>
- private void Apply(float currentTime, float endTime, ITransition transition)
- {
- var state = Graph.Layers[0].Play(transition);
- if (currentTime < endTime)// Playing.
- {
- state.Time = currentTime;
- }
- else// Finished.
- {
- _CurrentTime = MinTime;
- state.Time = _CurrentTime;
- }
- }
- /************************************************************************************************************************/
- }
- }
- #endif
|