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