// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik // using System; using UnityEngine; using Object = UnityEngine.Object; #if UNITY_EDITOR using UnityEditor; #endif namespace Animancer { /// /// https://kybernetik.com.au/animancer/api/Animancer/MixerTransition2DAsset #if !UNITY_EDITOR [System.Obsolete(Validate.ProOnlyMessage)] #endif [CreateAssetMenu(menuName = Strings.MenuPrefix + "Mixer Transition/2D", order = Strings.AssetMenuOrder + 4)] [HelpURL(Strings.DocsURLs.APIDocumentation + "/" + nameof(MixerTransition2DAsset))] public class MixerTransition2DAsset : AnimancerTransitionAsset { /// [Serializable] public new class UnShared : UnShared>, ManualMixerState.ITransition2D { } } /// /// https://kybernetik.com.au/animancer/api/Animancer/MixerTransition2D [Serializable] #if ! UNITY_EDITOR [System.Obsolete(Validate.ProOnlyMessage)] #endif public class MixerTransition2D : MixerTransition, Vector2>, ManualMixerState.ITransition2D, ICopyable { /************************************************************************************************************************/ /// A type of that can be created by a . public enum MixerType { /// Cartesian, /// Directional, } [SerializeField] private MixerType _Type; /// [] /// The type of that this transition will create. /// public ref MixerType Type => ref _Type; /************************************************************************************************************************/ /// /// Creates and returns a new or /// depending on the . /// /// /// Note that using methods like will also call /// , so if you call this method manually you may want to call that method /// as well. Or you can just use . /// /// This method also assigns it as the . /// public override MixerState CreateState() { switch (_Type) { case MixerType.Cartesian: State = new CartesianMixerState(); break; case MixerType.Directional: State = new DirectionalMixerState(); break; default: throw new ArgumentOutOfRangeException(nameof(_Type)); } InitializeState(); return State; } /************************************************************************************************************************/ /// public virtual void CopyFrom(MixerTransition2D copyFrom) { CopyFrom((MixerTransition, Vector2>)copyFrom); if (copyFrom == null) { _Type = default; return; } _Type = copyFrom._Type; } /************************************************************************************************************************/ #region Drawer #if UNITY_EDITOR /************************************************************************************************************************/ /// [CustomPropertyDrawer(typeof(MixerTransition2D), true)] public class Drawer : MixerTransitionDrawer { /************************************************************************************************************************/ /// /// Creates a new using the a wider `thresholdWidth` than usual to accomodate /// both the X and Y values. /// public Drawer() : base(StandardThresholdWidth * 2 + 20) { } /************************************************************************************************************************/ #region Threshold Calculation Functions /************************************************************************************************************************/ /// protected override void AddThresholdFunctionsToMenu(GenericMenu menu) { AddCalculateThresholdsFunction(menu, "From Velocity/XY", (state, threshold) => { if (AnimancerUtilities.TryGetAverageVelocity(state, out var velocity)) return new Vector2(velocity.x, velocity.y); else return new Vector2(float.NaN, float.NaN); }); AddCalculateThresholdsFunction(menu, "From Velocity/XZ", (state, threshold) => { if (AnimancerUtilities.TryGetAverageVelocity(state, out var velocity)) return new Vector2(velocity.x, velocity.z); else return new Vector2(float.NaN, float.NaN); }); AddCalculateThresholdsFunctionPerAxis(menu, "From Speed", (state, threshold) => AnimancerUtilities.TryGetAverageVelocity(state, out var velocity) ? velocity.magnitude : float.NaN); AddCalculateThresholdsFunctionPerAxis(menu, "From Velocity X", (state, threshold) => AnimancerUtilities.TryGetAverageVelocity(state, out var velocity) ? velocity.x : float.NaN); AddCalculateThresholdsFunctionPerAxis(menu, "From Velocity Y", (state, threshold) => AnimancerUtilities.TryGetAverageVelocity(state, out var velocity) ? velocity.y : float.NaN); AddCalculateThresholdsFunctionPerAxis(menu, "From Velocity Z", (state, threshold) => AnimancerUtilities.TryGetAverageVelocity(state, out var velocity) ? velocity.z : float.NaN); AddCalculateThresholdsFunctionPerAxis(menu, "From Angular Speed (Rad)", (state, threshold) => AnimancerUtilities.TryGetAverageAngularSpeed(state, out var speed) ? speed : float.NaN); AddCalculateThresholdsFunctionPerAxis(menu, "From Angular Speed (Deg)", (state, threshold) => AnimancerUtilities.TryGetAverageAngularSpeed(state, out var speed) ? speed * Mathf.Rad2Deg : float.NaN); AddPropertyModifierFunction(menu, "Initialize 4 Directions", Initialize4Directions); AddPropertyModifierFunction(menu, "Initialize 8 Directions", Initialize8Directions); } /************************************************************************************************************************/ private void Initialize4Directions(SerializedProperty property) { var oldSpeedCount = CurrentSpeeds.arraySize; CurrentAnimations.arraySize = CurrentThresholds.arraySize = CurrentSpeeds.arraySize = 5; CurrentThresholds.GetArrayElementAtIndex(0).vector2Value = default; CurrentThresholds.GetArrayElementAtIndex(1).vector2Value = Vector2.up; CurrentThresholds.GetArrayElementAtIndex(2).vector2Value = Vector2.right; CurrentThresholds.GetArrayElementAtIndex(3).vector2Value = Vector2.down; CurrentThresholds.GetArrayElementAtIndex(4).vector2Value = Vector2.left; InitializeSpeeds(oldSpeedCount); var type = property.FindPropertyRelative(nameof(_Type)); type.enumValueIndex = (int)MixerType.Directional; } /************************************************************************************************************************/ private void Initialize8Directions(SerializedProperty property) { var oldSpeedCount = CurrentSpeeds.arraySize; CurrentAnimations.arraySize = CurrentThresholds.arraySize = CurrentSpeeds.arraySize = 9; CurrentThresholds.GetArrayElementAtIndex(0).vector2Value = default; CurrentThresholds.GetArrayElementAtIndex(1).vector2Value = Vector2.up; CurrentThresholds.GetArrayElementAtIndex(2).vector2Value = new Vector2(1, 1); CurrentThresholds.GetArrayElementAtIndex(3).vector2Value = Vector2.right; CurrentThresholds.GetArrayElementAtIndex(4).vector2Value = new Vector2(1, -1); CurrentThresholds.GetArrayElementAtIndex(5).vector2Value = Vector2.down; CurrentThresholds.GetArrayElementAtIndex(6).vector2Value = new Vector2(-1, -1); CurrentThresholds.GetArrayElementAtIndex(7).vector2Value = Vector2.left; CurrentThresholds.GetArrayElementAtIndex(8).vector2Value = new Vector2(-1, 1); InitializeSpeeds(oldSpeedCount); var type = property.FindPropertyRelative(nameof(_Type)); type.enumValueIndex = (int)MixerType.Directional; } /************************************************************************************************************************/ private void AddCalculateThresholdsFunction(GenericMenu menu, string label, Func calculateThreshold) { var functionState = CurrentAnimations == null || CurrentThresholds == null ? Editor.MenuFunctionState.Disabled : Editor.MenuFunctionState.Normal; AddPropertyModifierFunction(menu, label, functionState, property => { GatherSubProperties(property); if (CurrentAnimations == null || CurrentThresholds == null) return; var count = CurrentAnimations.arraySize; for (int i = 0; i < count; i++) { var state = CurrentAnimations.GetArrayElementAtIndex(i).objectReferenceValue; if (state == null) continue; var threshold = CurrentThresholds.GetArrayElementAtIndex(i); var value = calculateThreshold(state, threshold.vector2Value); if (!Editor.AnimancerEditorUtilities.IsNaN(value)) threshold.vector2Value = value; } }); } /************************************************************************************************************************/ private void AddCalculateThresholdsFunctionPerAxis(GenericMenu menu, string label, Func calculateThreshold) { AddCalculateThresholdsFunction(menu, "X/" + label, 0, calculateThreshold); AddCalculateThresholdsFunction(menu, "Y/" + label, 1, calculateThreshold); } private void AddCalculateThresholdsFunction(GenericMenu menu, string label, int axis, Func calculateThreshold) { AddPropertyModifierFunction(menu, label, (property) => { var count = CurrentAnimations.arraySize; for (int i = 0; i < count; i++) { var state = CurrentAnimations.GetArrayElementAtIndex(i).objectReferenceValue; if (state == null) continue; var threshold = CurrentThresholds.GetArrayElementAtIndex(i); var value = threshold.vector2Value; var newValue = calculateThreshold(state, value[axis]); if (!float.IsNaN(newValue)) value[axis] = newValue; threshold.vector2Value = value; } }); } /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ } /************************************************************************************************************************/ #endif #endregion /************************************************************************************************************************/ } }