// 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
/************************************************************************************************************************/
}
}