// Animancer // Copyright 2020 Kybernetik // using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Playables; using Object = UnityEngine.Object; namespace Animancer { /// [Pro-Only] /// An which plays a . /// public sealed class PlayableAssetState : AnimancerState { /************************************************************************************************************************/ #region Fields and Properties /************************************************************************************************************************/ /// The which this state plays. private PlayableAsset _Asset; /// The which this state plays. public PlayableAsset Asset { get { return _Asset; } set { if (ReferenceEquals(_Asset, value)) return; if (ReferenceEquals(_Key, _Asset)) Key = value; if (_Playable.IsValid()) Root._Graph.DestroyPlayable(_Playable); CreatePlayable(value); SetWeightDirty(); } } /// The which this state plays. public override Object MainObject { get { return _Asset; } set { _Asset = (PlayableAsset)value; } } /************************************************************************************************************************/ /// The . public override float Length { get { return (float)_Asset.duration; } } /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Methods /************************************************************************************************************************/ /// /// Constructs a new to play the `asset` without connecting it to the /// . You must call or it /// will not actually do anything. /// public PlayableAssetState(AnimancerPlayable root, PlayableAsset asset) : base(root) { CreatePlayable(asset); } /// /// Constructs a new to play the `asset` and connects it to a new port on the /// `layer`s . /// public PlayableAssetState(AnimancerLayer layer, PlayableAsset asset) : this(layer.Root, asset) { layer.AddChild(this); } /// /// Constructs a new to play the `asset` and connects it to the `parent`s /// at the specified `index`. /// public PlayableAssetState(AnimancerNode parent, int index, PlayableAsset asset) : this(parent.Root, asset) { SetParent(parent, index); } /************************************************************************************************************************/ private void CreatePlayable(PlayableAsset asset) { if (asset == null) throw new ArgumentNullException("asset"); _Asset = asset; _Playable = asset.CreatePlayable(Root._Graph, Root.Component.gameObject); } /************************************************************************************************************************/ /// /// Returns a string describing the type of this state and the name of the . /// public override string ToString() { if (_Asset != null) return string.Concat(base.ToString(), " (", _Asset.name, ")"); else return base.ToString() + " (null)"; } /************************************************************************************************************************/ /// Destroys the . public override void Destroy() { _Asset = null; base.Destroy(); } /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Transition /************************************************************************************************************************/ /// /// A serializable which can create a when /// passed into . /// /// /// Unfortunately the tool used to generate this documentation does not currently support nested types with /// identical names, so only one Transition class will actually have a documentation page. /// [Serializable] public class Transition : Transition, IAnimationClipCollection { /************************************************************************************************************************/ [SerializeField, Tooltip("The asset to play")] private PlayableAsset _Asset; /// [] The asset to play. public PlayableAsset Asset { get { return _Asset; } set { _Asset = value; } } /// /// The will be used as the for the created state to /// be registered with. /// public override object Key { get { return _Asset; } } /************************************************************************************************************************/ [SerializeField, Tooltip(Strings.ProOnlyTag + "How fast the animation plays (1x = normal speed, 2x = double speed)")] private float _Speed = 1; /// [] /// Determines how fast the animation plays (1x = normal speed, 2x = double speed). /// public override float Speed { get { return _Speed; } set { _Speed = value; } } /************************************************************************************************************************/ [SerializeField, Tooltip(Strings.ProOnlyTag + "If enabled, the animation's time will start at this value when played")] private float _NormalizedStartTime = float.NaN; /// [] /// Determines what to start the animation at. /// /// The default value is which indicates that this value is not used so the /// animation will continue from its current time. /// public override float NormalizedStartTime { get { return _NormalizedStartTime; } set { _NormalizedStartTime = value; } } /// /// If this transition will set the , then it needs to use /// . /// public override FadeMode FadeMode { get { return float.IsNaN(_NormalizedStartTime) ? FadeMode.FixedSpeed : FadeMode.FromStart; } } /************************************************************************************************************************/ /// [] /// The maximum amount of time the animation is expected to take (in seconds). /// public override float MaximumDuration { get { return _Asset != null ? (float)_Asset.duration : 0; } } /************************************************************************************************************************/ /// /// Creates and returns a new connected to the `layer`. /// /// This method also assigns it as the . /// public override PlayableAssetState CreateState(AnimancerLayer layer) { return State = new PlayableAssetState(layer, _Asset); } /************************************************************************************************************************/ /// /// Called by to apply the /// and . /// public override void Apply(AnimancerState state) { base.Apply(state); 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); } /************************************************************************************************************************/ /// Gathers all the animations associated with this object. void IAnimationClipCollection.GatherAnimationClips(ICollection clips) { clips.GatherFromAsset(_Asset); } /************************************************************************************************************************/ #if UNITY_EDITOR /************************************************************************************************************************/ /// [Editor-Only] Draws the Inspector GUI for a . [UnityEditor.CustomPropertyDrawer(typeof(Transition), true)] public class Drawer : Editor.TransitionDrawer { /************************************************************************************************************************/ /// Constructs a new . public Drawer() : base("_Asset") { } /************************************************************************************************************************/ } /************************************************************************************************************************/ #endif /************************************************************************************************************************/ } /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ } }