// Animancer // Copyright 2020 Kybernetik // using System; using System.Collections.Generic; using UnityEngine; namespace Animancer { /// <summary> /// A set of up/down/left/right animations with diagonals as well. /// </summary> [CreateAssetMenu(menuName = Strings.MenuPrefix + "Directional Animation Set/8 Directions", order = Strings.AssetMenuOrder + 11)] public class DirectionalAnimationSet8 : DirectionalAnimationSet { /************************************************************************************************************************/ [SerializeField] private AnimationClip _UpRight; /// <summary>[<see cref="SerializeField"/>] The animation facing diagonally up-right.</summary> public AnimationClip UpRight { get { return _UpRight; } } /// <summary>Sets the <see cref="UpRight"/> animation.</summary> /// <remarks>This is not simply a property setter because the animations will usually not need to be changed by scripts.</remarks> public void SetUpRight(AnimationClip clip) { _UpRight = clip; AnimancerUtilities.SetDirty(this); } /************************************************************************************************************************/ [SerializeField] private AnimationClip _DownRight; /// <summary>[<see cref="SerializeField"/>] The animation facing diagonally down-right.</summary> public AnimationClip DownRight { get { return _DownRight; } } /// <summary>Sets the <see cref="DownRight"/> animation.</summary> /// <remarks>This is not simply a property setter because the animations will usually not need to be changed by scripts.</remarks> public void SetDownRight(AnimationClip clip) { _DownRight = clip; AnimancerUtilities.SetDirty(this); } /************************************************************************************************************************/ [SerializeField] private AnimationClip _DownLeft; /// <summary>[<see cref="SerializeField"/>] The animation facing diagonally down-left.</summary> public AnimationClip DownLeft { get { return _DownLeft; } } /// <summary>Sets the <see cref="DownLeft"/> animation.</summary> /// <remarks>This is not simply a property setter because the animations will usually not need to be changed by scripts.</remarks> public void SetDownLeft(AnimationClip clip) { _DownLeft = clip; AnimancerUtilities.SetDirty(this); } /************************************************************************************************************************/ [SerializeField] private AnimationClip _UpLeft; /// <summary>[<see cref="SerializeField"/>] The animation facing diagonally up-left.</summary> public AnimationClip UpLeft { get { return _UpLeft; } } /// <summary>Sets the <see cref="UpLeft"/> animation.</summary> /// <remarks>This is not simply a property setter because the animations will usually not need to be changed by scripts.</remarks> public void SetUpLeft(AnimationClip clip) { _UpLeft = clip; AnimancerUtilities.SetDirty(this); } /************************************************************************************************************************/ /// <summary>Returns the animation closest to the specified `direction`.</summary> public override AnimationClip GetClip(Vector2 direction) { var angle = Mathf.Atan2(direction.y, direction.x); var octant = Mathf.RoundToInt(8 * angle / (2 * Mathf.PI) + 8) % 8; switch (octant) { case 0: return Right; case 1: return _UpRight; case 2: return Up; case 3: return _UpLeft; case 4: return Left; case 5: return _DownLeft; case 6: return Down; case 7: return _DownRight; default: throw new ArgumentOutOfRangeException("Invalid octant"); } } /************************************************************************************************************************/ #region Directions /************************************************************************************************************************/ /// <summary>Constants for each of the diagonal directions.</summary> public static class Diagonals { /************************************************************************************************************************/ /// <summary>1 / (Square Root of 2).</summary> public const float OneOverSqrt2 = 0.70710678118f; /// <summary> /// A vector with a magnitude of 1 pointing up to the right. /// <para></para> /// The value is approximately (0.707, 0.707). /// </summary> public static Vector2 UpRight { get { return new Vector2(OneOverSqrt2, OneOverSqrt2); } } /// <summary> /// A vector with a magnitude of 1 pointing down to the right. /// <para></para> /// The value is approximately (0.707, -0.707). /// </summary> public static Vector2 DownRight { get { return new Vector2(OneOverSqrt2, -OneOverSqrt2); } } /// <summary> /// A vector with a magnitude of 1 pointing down to the left. /// <para></para> /// The value is approximately (-0.707, -0.707). /// </summary> public static Vector2 DownLeft { get { return new Vector2(-OneOverSqrt2, -OneOverSqrt2); } } /// <summary> /// A vector with a magnitude of 1 pointing up to the left. /// <para></para> /// The value is approximately (-0.707, 0.707). /// </summary> public static Vector2 UpLeft { get { return new Vector2(-OneOverSqrt2, OneOverSqrt2); } } /************************************************************************************************************************/ } /************************************************************************************************************************/ /// <summary>The number of animations in this set.</summary> public override int ClipCount { get { return 8; } } /************************************************************************************************************************/ /// <summary>Up, Down, Left Right, or their diagonals.</summary> public new enum Direction { #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member. Up, Right, Down, Left, UpRight, DownRight, DownLeft, UpLeft, #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member. } /************************************************************************************************************************/ /// <summary>Returns the name of the specified `direction`.</summary> protected override string GetDirectionName(int direction) { return ((Direction)direction).ToString(); } /************************************************************************************************************************/ /// <summary>Returns the animation associated with the specified `direction`.</summary> public AnimationClip GetClip(Direction direction) { switch (direction) { case Direction.Up: return Up; case Direction.Right: return Right; case Direction.Down: return Down; case Direction.Left: return Left; case Direction.UpRight: return _UpRight; case Direction.DownRight: return _DownRight; case Direction.DownLeft: return _DownLeft; case Direction.UpLeft: return _UpLeft; default: throw new ArgumentException("Unhandled direction: " + direction); } } /// <summary>Returns the animation associated with the specified `direction`.</summary> public override AnimationClip GetClip(int direction) { return GetClip((Direction)direction); } /************************************************************************************************************************/ /// <summary>Sets the animation associated with the specified `direction`.</summary> public void SetClip(Direction direction, AnimationClip clip) { switch (direction) { case Direction.Up: SetUp(clip); break; case Direction.Right: SetRight(clip); break; case Direction.Down: SetDown(clip); break; case Direction.Left: SetLeft(clip); break; case Direction.UpRight: _UpRight = clip; break; case Direction.DownRight: _DownRight = clip; break; case Direction.DownLeft: _DownLeft = clip; break; case Direction.UpLeft: _UpLeft = clip; break; default: throw new ArgumentException("Unhandled direction: " + direction); } AnimancerUtilities.SetDirty(this); } /// <summary>Sets the animation associated with the specified `direction`.</summary> public override void SetClip(int direction, AnimationClip clip) { SetClip((Direction)direction, clip); } /************************************************************************************************************************/ /// <summary>Returns a vector representing the specified `direction`.</summary> public static Vector2 DirectionToVector(Direction direction) { switch (direction) { case Direction.Up: return Vector2.up; case Direction.Right: return Vector2.right; case Direction.Down: return Vector2.down; case Direction.Left: return Vector2.left; case Direction.UpRight: return Diagonals.UpRight; case Direction.DownRight: return Diagonals.DownRight; case Direction.DownLeft: return Diagonals.DownLeft; case Direction.UpLeft: return Diagonals.UpLeft; default: throw new ArgumentException("Unhandled direction: " + direction); } } /// <summary>Returns a vector representing the specified `direction`.</summary> public override Vector2 GetDirection(int direction) { return DirectionToVector((Direction)direction); } /************************************************************************************************************************/ /// <summary>Returns the direction closest to the specified `vector`.</summary> public new static Direction VectorToDirection(Vector2 vector) { var angle = Mathf.Atan2(vector.y, vector.x); var octant = Mathf.RoundToInt(8 * angle / (2 * Mathf.PI) + 8) % 8; switch (octant) { case 0: return Direction.Right; case 1: return Direction.UpRight; case 2: return Direction.Up; case 3: return Direction.UpLeft; case 4: return Direction.Left; case 5: return Direction.DownLeft; case 6: return Direction.Down; case 7: return Direction.DownRight; default: throw new ArgumentOutOfRangeException("Invalid octant"); } } /************************************************************************************************************************/ /// <summary>Returns a copy of the `vector` pointing in the closest direction this set type has an animation for.</summary> public new static Vector2 SnapVectorToDirection(Vector2 vector) { var magnitude = vector.magnitude; var direction = VectorToDirection(vector); vector = DirectionToVector(direction) * magnitude; return vector; } /// <summary>Returns a copy of the `vector` pointing in the closest direction this set has an animation for.</summary> public override Vector2 Snap(Vector2 vector) { return SnapVectorToDirection(vector); } /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Name Based Operations /************************************************************************************************************************/ #if UNITY_EDITOR /************************************************************************************************************************/ /// <summary> /// Attempts to assign the `clip` to one of this set's fields based on its name and returns the direction index /// of that field (or -1 if it was unable to determine the direction). /// </summary> public override int SetClipByName(AnimationClip clip) { var name = clip.name; var directionCount = ClipCount; for (int i = directionCount - 1; i >= 0; i--) { if (name.Contains(GetDirectionName(i))) { SetClip(i, clip); return i; } } return -1; } /************************************************************************************************************************/ #endif /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ } }