// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik // using Animancer.Units; using System; using UnityEngine; using Object = UnityEngine.Object; namespace Animancer { /// /// A serializable which can create a particular type of /// when passed into . /// /// /// Documentation: Transitions /// /// https://kybernetik.com.au/animancer/api/Animancer/AnimancerTransition_1 /// [Serializable] public abstract class AnimancerTransition : ITransition, ITransitionDetailed, ITransitionWithEvents, ICopyable> where TState : AnimancerState { /************************************************************************************************************************/ [SerializeField] [Tooltip(Strings.Tooltips.FadeDuration)] [AnimationTime(AnimationTimeAttribute.Units.Seconds, Rule = Validate.Value.IsNotNegative)] [DefaultFadeValue] private float _FadeDuration = AnimancerPlayable.DefaultFadeDuration; /// /// [] /// Thrown when setting the value to a negative number. public float FadeDuration { get => _FadeDuration; set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FadeDuration)} must not be negative"); _FadeDuration = value; } } /************************************************************************************************************************/ /// /// Returns false unless overridden. public virtual bool IsLooping => false; /// public virtual float NormalizedStartTime { get => float.NaN; set { } } /// /// Returns 1 unless overridden. public virtual float Speed { get => 1; set { } } /// public abstract float MaximumDuration { get; } /************************************************************************************************************************/ [SerializeField, Tooltip(Strings.ProOnlyTag + "Events which will be triggered as the animation plays")] private AnimancerEvent.Sequence.Serializable _Events; /// /// This property returns the . /// /// is null. If this transition was created in code (using the new /// keyword rather than being deserialized by Unity) you will need to null-check and/or assign a new /// sequence to the before accessing this property. /// public AnimancerEvent.Sequence Events { get { if (_Events == null) _Events = new AnimancerEvent.Sequence.Serializable(); return _Events.Events; } } /// public ref AnimancerEvent.Sequence.Serializable SerializedEvents => ref _Events; /************************************************************************************************************************/ /// /// The state that was created by this object. Specifically, this is the state that was most recently /// passed into (usually by ). /// /// You can use or /// to get or create the state for a /// specific object. /// /// is simply a shorthand for casting this to . /// public AnimancerState BaseState { get; private set; } /************************************************************************************************************************/ private TState _State; /// /// The state that was created by this object. Specifically, this is the state that was most recently /// passed into (usually by ). /// /// /// /// You can use or /// to get or create the state for a /// specific object. /// /// This property is shorthand for casting the to . /// /// /// /// The is not actually a . This should only /// happen if a different type of state was created by something else and registered using the /// , causing this to pass that /// state into instead of calling to make the correct type of /// state. /// public TState State { get { if (_State == null) _State = (TState)BaseState; return _State; } protected set { BaseState = _State = value; } } /************************************************************************************************************************/ /// /// Returns true unless overridden. public virtual bool IsValid => true; /// The which the created state will be registered with. /// Returns this unless overridden. public virtual object Key => this; /// /// Returns unless overridden. public virtual FadeMode FadeMode => FadeMode.FixedSpeed; /// public abstract TState CreateState(); /// AnimancerState ITransition.CreateState() => CreateState(); /************************************************************************************************************************/ /// public virtual void Apply(AnimancerState state) { state.Events = _Events; #if UNITY_ASSERTIONS if (state.HasEvents) state.Events.SetShouldNotModifyReason("it was created by a Transition." + $"\n\nTransitions give the played {nameof(AnimancerState)} a reference to their Events," + $" meaning that any modifications to the state.Events will also affect the transition.Events" + $" and persist when that Transition is played in the future. This is a common source of logic bugs" + $" so when a Transition is played it marks its Events as no longer expecting to be modified."); #endif BaseState = state; if (_State != state) _State = null; } /************************************************************************************************************************/ /// The that the created state will have. public virtual Object MainObject { get; } /// The display name of this transition. public virtual string Name { get { var mainObject = MainObject; return mainObject != null ? mainObject.name : null; } } /// Returns the and type of this transition. public override string ToString() { var type = GetType().FullName; var name = Name; if (name != null) return $"{name} ({type})"; else return type; } /************************************************************************************************************************/ /// public virtual void CopyFrom(AnimancerTransition copyFrom) { if (copyFrom == null) { _FadeDuration = AnimancerPlayable.DefaultFadeDuration; _Events = default; return; } _FadeDuration = copyFrom._FadeDuration; _Events = copyFrom._Events.Clone(); } /************************************************************************************************************************/ /// Applies the given details to the `state`. public static void ApplyDetails(AnimancerState state, float speed, float normalizedStartTime) { if (!float.IsNaN(speed)) state.Speed = speed; if (!float.IsNaN(normalizedStartTime)) state.NormalizedTime = normalizedStartTime; else if (state.Weight == 0) state.NormalizedTime = AnimancerEvent.Sequence.GetDefaultNormalizedStartTime(speed); } /************************************************************************************************************************/ } }