// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik //
using UnityEngine;
using System;
#if UNITY_EDITOR
using Animancer.Editor;
using UnityEditor;
#endif
namespace Animancer
{
/// https://kybernetik.com.au/animancer/api/Animancer/AnimancerTransitionAssetBase
///
public partial class AnimancerTransitionAssetBase
{
/************************************************************************************************************************/
///
[Serializable]
public class UnShared : UnShared { }
///
/// An wrapper which stores its own
/// and to allow multiple objects to reference the same transition asset without
/// interfering with each other.
///
///
/// Documentation:
/// Transition Assets - UnShared
///
/// https://kybernetik.com.au/animancer/api/Animancer/UnShared_1
///
[Serializable]
public class UnShared : ITransition, ITransitionWithEvents, IWrapper
where TAsset : AnimancerTransitionAssetBase
{
/************************************************************************************************************************/
[SerializeField]
private TAsset _Asset;
/// The wrapped by this object.
public TAsset Asset
{
get
{
AssertAsset();
return _Asset;
}
set
{
_Asset = value;
BaseState = null;
ClearCachedEvents();
}
}
///
object IWrapper.WrappedObject => _Asset;
/// The wrapped by this object.
public ITransition BaseTransition => _Asset.GetTransition();
/************************************************************************************************************************/
/// Can this transition create a valid ?
public virtual bool IsValid
{
get
{
AssertAsset();
return _Asset.IsValid();
}
}
/************************************************************************************************************************/
/// Is the assigned (i.e. not null)?
public bool HasAsset => _Asset != null;
/************************************************************************************************************************/
/// [Assert-Conditional] Logs an error if the is null.
[System.Diagnostics.Conditional(Strings.Assertions)]
private void AssertAsset()
{
if (_Asset == null)
Debug.LogError($"{GetType().Name}.{nameof(Asset)} is not assigned." +
$" {nameof(HasAsset)} can be used to check without triggering this error.");
}
/************************************************************************************************************************/
private AnimancerState _BaseState;
///
/// 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 => _BaseState;
protected set
{
_BaseState = value;
OnSetBaseState();
}
}
/// Called when the is set.
protected virtual void OnSetBaseState() { }
/************************************************************************************************************************/
private AnimancerEvent.Sequence _Events;
///
/// This property stores a copy of the events from the .
public virtual AnimancerEvent.Sequence Events
{
get
{
if (_Events == null)
_Events = new AnimancerEvent.Sequence(SerializedEvents.GetEventsOptional());
return _Events;
}
}
///
public virtual ref AnimancerEvent.Sequence.Serializable SerializedEvents
{
get
{
AssertAsset();
return ref ((ITransitionWithEvents)_Asset.GetTransition()).SerializedEvents;
}
}
///
/// Clears the cached so that next time they are accessed they will be copied from the
/// again.
///
public void ClearCachedEvents()
{
_Events = null;
}
/************************************************************************************************************************/
/// Wraps and assigns the local .
public virtual void Apply(AnimancerState state)
{
BaseState = state;
_Asset.Apply(state);
if (_Events == null)
{
_Events = SerializedEvents.GetEventsOptional();
if (_Events == null)
return;
_Events = new AnimancerEvent.Sequence(_Events);
}
state.Events = _Events;
}
/************************************************************************************************************************/
///
public virtual object Key
{
get
{
AssertAsset();
return _Asset.Key;
}
}
///
public virtual float FadeDuration
{
get
{
AssertAsset();
return _Asset.FadeDuration;
}
}
///
public virtual FadeMode FadeMode
{
get
{
AssertAsset();
return _Asset.FadeMode;
}
}
///
AnimancerState ITransition.CreateState()
{
AssertAsset();
return BaseState = _Asset.CreateState();
}
/************************************************************************************************************************/
}
/************************************************************************************************************************/
///
[Serializable]
public class UnShared : UnShared, ITransition
where TAsset : AnimancerTransitionAsset
where TTransition : ITransition, IHasEvents
where TState : AnimancerState
{
/************************************************************************************************************************/
/// The wrapped by this object.
public TTransition Transition
{
get => Asset.Transition;
set => Asset.Transition = value;
}
/************************************************************************************************************************/
///
protected override void OnSetBaseState()
{
base.OnSetBaseState();
if (_State != BaseState)
_State = null;
}
/************************************************************************************************************************/
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;
}
}
/************************************************************************************************************************/
///
public override ref AnimancerEvent.Sequence.Serializable SerializedEvents
=> ref Asset.Transition.SerializedEvents;
/************************************************************************************************************************/
///
public virtual TState CreateState()
=> State = (TState)Asset.CreateState();
/************************************************************************************************************************/
}
/************************************************************************************************************************/
#if UNITY_EDITOR
/// [Editor-Only]
/// A for .
///
///
/// This class doesn't inherit from (which would let it draw the button to open the
/// ) because it would only show the Transition Asset reference field without
/// any of the actual values (fade duration, speed, etc.) and trying to redirect everything necessary to preview
/// the asset's transition would require significant additional complexity.
///
/// This issue can be avoided using the
///
/// Nested Inspectors in Inspector Gadgets by opening the asset's Inspector and previewing it directly.
///
[CustomPropertyDrawer(typeof(UnShared<>), true)]
public class UnSharedTransitionDrawer : PropertyDrawer
{
/************************************************************************************************************************/
///
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var height = AnimancerGUI.LineHeight;
if (property.propertyType == SerializedPropertyType.ManagedReference &&
property.isExpanded)
height += AnimancerGUI.LineHeight + AnimancerGUI.StandardSpacing;
return height;
}
/************************************************************************************************************************/
///
public override void OnGUI(Rect area, SerializedProperty property, GUIContent label)
{
if (property.propertyType == SerializedPropertyType.ManagedReference)
{
using (new TypeSelectionButton(area, property, true))
EditorGUI.PropertyField(area, property, label, true);
}
else
{
var transitionProperty = property.FindPropertyRelative("_Asset");
EditorGUI.PropertyField(area, transitionProperty, label, false);
}
}
/************************************************************************************************************************/
}
#endif
/************************************************************************************************************************/
}
}