DirectionalAnimationSet.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. namespace Animancer
  6. {
  7. /// <summary>A set of up/right/down/left animations.</summary>
  8. /// <remarks>
  9. /// <strong>Documentation:</strong>
  10. /// <see href="https://kybernetik.com.au/animancer/docs/manual/playing/directional-sets">
  11. /// Directional Animation Sets</see>
  12. /// </remarks>
  13. /// https://kybernetik.com.au/animancer/api/Animancer/DirectionalAnimationSet
  14. ///
  15. [CreateAssetMenu(
  16. menuName = Strings.MenuPrefix + "Directional Animation Set/4 Directions",
  17. order = Strings.AssetMenuOrder + 3)]
  18. [AnimancerHelpUrl(typeof(DirectionalAnimationSet))]
  19. public class DirectionalAnimationSet : ScriptableObject,
  20. IAnimationClipSource
  21. {
  22. /************************************************************************************************************************/
  23. [SerializeField]
  24. private AnimationClip _Up;
  25. /// <summary>[<see cref="SerializeField"/>] The animation facing up (0, 1).</summary>
  26. /// <exception cref="ArgumentException"><see cref="AllowSetClips"/> was not called before setting this value.</exception>
  27. public AnimationClip Up
  28. {
  29. get => _Up;
  30. set
  31. {
  32. AssertCanSetClips();
  33. _Up = value;
  34. AnimancerUtilities.SetDirty(this);
  35. }
  36. }
  37. /************************************************************************************************************************/
  38. [SerializeField]
  39. private AnimationClip _Right;
  40. /// <summary>[<see cref="SerializeField"/>] The animation facing right (1, 0).</summary>
  41. /// <exception cref="ArgumentException"><see cref="AllowSetClips"/> was not called before setting this value.</exception>
  42. public AnimationClip Right
  43. {
  44. get => _Right;
  45. set
  46. {
  47. AssertCanSetClips();
  48. _Right = value;
  49. AnimancerUtilities.SetDirty(this);
  50. }
  51. }
  52. /************************************************************************************************************************/
  53. [SerializeField]
  54. private AnimationClip _Down;
  55. /// <summary>[<see cref="SerializeField"/>] The animation facing down (0, -1).</summary>
  56. /// <exception cref="ArgumentException"><see cref="AllowSetClips"/> was not called before setting this value.</exception>
  57. public AnimationClip Down
  58. {
  59. get => _Down;
  60. set
  61. {
  62. AssertCanSetClips();
  63. _Down = value;
  64. AnimancerUtilities.SetDirty(this);
  65. }
  66. }
  67. /************************************************************************************************************************/
  68. [SerializeField]
  69. private AnimationClip _Left;
  70. /// <summary>[<see cref="SerializeField"/>] The animation facing left (-1, 0).</summary>
  71. /// <exception cref="ArgumentException"><see cref="AllowSetClips"/> was not called before setting this value.</exception>
  72. public AnimationClip Left
  73. {
  74. get => _Left;
  75. set
  76. {
  77. AssertCanSetClips();
  78. _Left = value;
  79. AnimancerUtilities.SetDirty(this);
  80. }
  81. }
  82. /************************************************************************************************************************/
  83. #if UNITY_ASSERTIONS
  84. private bool _AllowSetClips;
  85. #endif
  86. /// <summary>[Assert-Only]
  87. /// Determines whether the <see cref="AnimationClip"/> properties are allowed to be set.
  88. /// </summary>
  89. [System.Diagnostics.Conditional(Strings.Assertions)]
  90. public void AllowSetClips(bool allow = true)
  91. {
  92. #if UNITY_ASSERTIONS
  93. _AllowSetClips = allow;
  94. #endif
  95. }
  96. /// <summary>[Assert-Only]
  97. /// Throws an <see cref="ArgumentException"/> if <see cref="AllowSetClips"/> wasn't called.
  98. /// </summary>
  99. [System.Diagnostics.Conditional(Strings.Assertions)]
  100. public void AssertCanSetClips()
  101. {
  102. #if UNITY_ASSERTIONS
  103. AnimancerUtilities.Assert(_AllowSetClips,
  104. $"{nameof(AllowSetClips)}() must be called before attempting to set any of" +
  105. $" the animations in a {nameof(DirectionalAnimationSet)}" +
  106. $" to ensure that they are not changed accidentally.");
  107. #endif
  108. }
  109. /************************************************************************************************************************/
  110. /// <summary>Returns the animation closest to the specified `direction`.</summary>
  111. public virtual AnimationClip GetClip(Vector2 direction)
  112. {
  113. if (direction.x >= 0)
  114. {
  115. if (direction.y >= 0)
  116. return direction.x > direction.y ? _Right : _Up;
  117. else
  118. return direction.x > -direction.y ? _Right : _Down;
  119. }
  120. else
  121. {
  122. if (direction.y >= 0)
  123. return direction.x < -direction.y ? _Left : _Up;
  124. else
  125. return direction.x < direction.y ? _Left : _Down;
  126. }
  127. }
  128. /************************************************************************************************************************/
  129. #region Directions
  130. /************************************************************************************************************************/
  131. /// <summary>The number of animations in this set.</summary>
  132. public virtual int ClipCount
  133. => 4;
  134. /************************************************************************************************************************/
  135. /// <summary>Up, Right, Down, or Left.</summary>
  136. /// <remarks>
  137. /// <strong>Documentation:</strong>
  138. /// <see href="https://kybernetik.com.au/animancer/docs/manual/playing/directional-sets">
  139. /// Directional Animation Sets</see>
  140. /// </remarks>
  141. /// https://kybernetik.com.au/animancer/api/Animancer/Direction
  142. ///
  143. public enum Direction
  144. {
  145. /// <summary><see cref="Vector2.up"/>.</summary>
  146. Up,
  147. /// <summary><see cref="Vector2.right"/>.</summary>
  148. Right,
  149. /// <summary><see cref="Vector2.down"/>.</summary>
  150. Down,
  151. /// <summary><see cref="Vector2.left"/>.</summary>
  152. Left,
  153. }
  154. /************************************************************************************************************************/
  155. /// <summary>Returns the name of the specified `direction`.</summary>
  156. protected virtual string GetDirectionName(int direction)
  157. => ((Direction)direction).ToString();
  158. /************************************************************************************************************************/
  159. /// <summary>Returns the animation associated with the specified `direction`.</summary>
  160. public AnimationClip GetClip(Direction direction)
  161. => direction switch
  162. {
  163. Direction.Up => _Up,
  164. Direction.Right => _Right,
  165. Direction.Down => _Down,
  166. Direction.Left => _Left,
  167. _ => throw AnimancerUtilities.CreateUnsupportedArgumentException(direction),
  168. };
  169. /// <summary>Returns the animation associated with the specified `direction`.</summary>
  170. public virtual AnimationClip GetClip(int direction)
  171. => GetClip((Direction)direction);
  172. /************************************************************************************************************************/
  173. /// <summary>Sets the animation associated with the specified `direction`.</summary>
  174. public void SetClip(Direction direction, AnimationClip clip)
  175. {
  176. switch (direction)
  177. {
  178. case Direction.Up: Up = clip; break;
  179. case Direction.Right: Right = clip; break;
  180. case Direction.Down: Down = clip; break;
  181. case Direction.Left: Left = clip; break;
  182. default: throw AnimancerUtilities.CreateUnsupportedArgumentException(direction);
  183. }
  184. }
  185. /// <summary>Sets the animation associated with the specified `direction`.</summary>
  186. public virtual void SetClip(int direction, AnimationClip clip) => SetClip((Direction)direction, clip);
  187. /************************************************************************************************************************/
  188. /// <summary>[Editor-Only]
  189. /// Attempts to assign the `clip` to one of this set's fields based on its name and
  190. /// returns the direction index of that field (or -1 if it was unable to determine the direction).
  191. /// </summary>
  192. public virtual int SetClipByName(AnimationClip clip)
  193. {
  194. var name = clip.name;
  195. int bestDirection = -1;
  196. int bestDirectionIndex = -1;
  197. var directionCount = ClipCount;
  198. for (int i = 0; i < directionCount; i++)
  199. {
  200. var index = name.LastIndexOf(GetDirectionName(i));
  201. if (bestDirectionIndex < index)
  202. {
  203. bestDirectionIndex = index;
  204. bestDirection = i;
  205. }
  206. }
  207. if (bestDirection >= 0)
  208. SetClip(bestDirection, clip);
  209. return bestDirection;
  210. }
  211. /************************************************************************************************************************/
  212. #region Conversion
  213. /************************************************************************************************************************/
  214. /// <summary>Returns a vector representing the specified `direction`.</summary>
  215. public static Vector2 DirectionToVector(Direction direction)
  216. => direction switch
  217. {
  218. Direction.Up => Vector2.up,
  219. Direction.Right => Vector2.right,
  220. Direction.Down => Vector2.down,
  221. Direction.Left => Vector2.left,
  222. _ => throw AnimancerUtilities.CreateUnsupportedArgumentException(direction),
  223. };
  224. /// <summary>Returns a vector representing the specified `direction`.</summary>
  225. public virtual Vector2 GetDirection(int direction)
  226. => DirectionToVector((Direction)direction);
  227. /************************************************************************************************************************/
  228. /// <summary>Returns the direction closest to the specified `vector`.</summary>
  229. public static Direction VectorToDirection(Vector2 vector)
  230. {
  231. if (vector.x >= 0)
  232. {
  233. if (vector.y >= 0)
  234. return vector.x > vector.y ? Direction.Right : Direction.Up;
  235. else
  236. return vector.x > -vector.y ? Direction.Right : Direction.Down;
  237. }
  238. else
  239. {
  240. if (vector.y >= 0)
  241. return vector.x < -vector.y ? Direction.Left : Direction.Up;
  242. else
  243. return vector.x < vector.y ? Direction.Left : Direction.Down;
  244. }
  245. }
  246. /************************************************************************************************************************/
  247. /// <summary>Returns a copy of the `vector` pointing in the closest direction this set type has an animation for.</summary>
  248. public static Vector2 SnapVectorToDirection(Vector2 vector)
  249. {
  250. var magnitude = vector.magnitude;
  251. var direction = VectorToDirection(vector);
  252. vector = DirectionToVector(direction) * magnitude;
  253. return vector;
  254. }
  255. /// <summary>Returns a copy of the `vector` pointing in the closest direction this set has an animation for.</summary>
  256. public virtual Vector2 Snap(Vector2 vector)
  257. => SnapVectorToDirection(vector);
  258. /************************************************************************************************************************/
  259. #endregion
  260. /************************************************************************************************************************/
  261. #region Collections
  262. /************************************************************************************************************************/
  263. /// <summary>Adds all animations from this set to the `clips`, starting from the specified `index`.</summary>
  264. public void AddClips(AnimationClip[] clips, int index)
  265. {
  266. var count = ClipCount;
  267. for (int i = 0; i < count; i++)
  268. clips[index + i] = GetClip(i);
  269. }
  270. /// <summary>[<see cref="IAnimationClipSource"/>] Adds all animations from this set to the `clips`.</summary>
  271. public void GetAnimationClips(List<AnimationClip> clips)
  272. {
  273. var count = ClipCount;
  274. for (int i = 0; i < count; i++)
  275. clips.Add(GetClip(i));
  276. }
  277. /************************************************************************************************************************/
  278. /// <summary>
  279. /// Adds unit vectors corresponding to each of the animations in this set to the `directions`, starting from
  280. /// the specified `index`.
  281. /// </summary>
  282. public void AddDirections(Vector2[] directions, int index)
  283. {
  284. var count = ClipCount;
  285. for (int i = 0; i < count; i++)
  286. directions[index + i] = GetDirection(i);
  287. }
  288. /************************************************************************************************************************/
  289. /// <summary>Calls <see cref="AddClips"/> and <see cref="AddDirections"/>.</summary>
  290. public void AddClipsAndDirections(AnimationClip[] clips, Vector2[] directions, int index)
  291. {
  292. AddClips(clips, index);
  293. AddDirections(directions, index);
  294. }
  295. /************************************************************************************************************************/
  296. #endregion
  297. /************************************************************************************************************************/
  298. #endregion
  299. /************************************************************************************************************************/
  300. }
  301. }