DirectionalCharacter.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
  3. using Animancer.Units;
  4. using System.Collections.Generic;
  5. using UnityEngine;
  6. namespace Animancer.Samples.Sprites
  7. {
  8. /// <summary>
  9. /// A more complex version of the <see cref="DirectionalBasics"/> which adds
  10. /// running and pushing animations as well as the ability to actually move around.
  11. /// </summary>
  12. ///
  13. /// <remarks>
  14. /// <strong>Sample:</strong>
  15. /// <see href="https://kybernetik.com.au/animancer/docs/samples/sprites/character">
  16. /// Directional Character</see>
  17. /// </remarks>
  18. ///
  19. /// https://kybernetik.com.au/animancer/api/Animancer.Samples.Sprites/DirectionalCharacter
  20. ///
  21. [AddComponentMenu(Strings.SamplesMenuPrefix + "Sprites - Directional Character")]
  22. [AnimancerHelpUrl(typeof(DirectionalCharacter))]
  23. public class DirectionalCharacter : MonoBehaviour
  24. {
  25. /************************************************************************************************************************/
  26. #if UNITY_PHYSICS_2D
  27. /************************************************************************************************************************/
  28. [Header("Physics")]
  29. [SerializeField] private CapsuleCollider2D _Collider;
  30. [SerializeField] private Rigidbody2D _Rigidbody;
  31. [SerializeField, MetersPerSecond] private float _WalkSpeed = 1;
  32. [SerializeField, MetersPerSecond] private float _RunSpeed = 2;
  33. [Header("Animations")]
  34. [SerializeField] private AnimancerComponent _Animancer;
  35. [SerializeField] private DirectionalAnimationSet _Idle;
  36. [SerializeField] private DirectionalAnimationSet _Walk;
  37. [SerializeField] private DirectionalAnimationSet _Run;
  38. [SerializeField] private DirectionalAnimationSet _Push;
  39. [SerializeField] private Vector2 _Facing = Vector2.down;
  40. private Vector2 _Movement;
  41. private DirectionalAnimationSet _CurrentAnimationSet;
  42. private static readonly TimeSynchronizer<AnimationGroup>
  43. TimeSynchronizer = new();
  44. public enum AnimationGroup
  45. {
  46. Other,
  47. Movement,
  48. }
  49. /************************************************************************************************************************/
  50. protected virtual void Awake()
  51. {
  52. _CurrentAnimationSet = _Idle;
  53. }
  54. /************************************************************************************************************************/
  55. protected virtual void Update()
  56. {
  57. _Movement = SampleInput.WASD;
  58. if (_Movement != Vector2.zero)
  59. {
  60. // Snap the movement to the exact directions we have animations for.
  61. // When using DirectionalAnimationSets this means the character will only move up/right/down/left.
  62. // But DirectionalAnimationSet8s will allow diagonal movement as well.
  63. _Movement = _CurrentAnimationSet.Snap(_Movement);
  64. _Movement = Vector2.ClampMagnitude(_Movement, 1);
  65. _Facing = _Movement;
  66. UpdateMovementState();
  67. }
  68. else
  69. {
  70. Play(_Idle, AnimationGroup.Other);
  71. }
  72. }
  73. /************************************************************************************************************************/
  74. private void Play(DirectionalAnimationSet animations, AnimationGroup group)
  75. {
  76. // Store the current time.
  77. TimeSynchronizer.StoreTime(_Animancer);
  78. _CurrentAnimationSet = animations;
  79. _Animancer.Play(animations.GetClip(_Facing));
  80. // If the new animation is in the synchronization group, give it the same time the previous animation had.
  81. TimeSynchronizer.SyncTime(_Animancer, group);
  82. }
  83. /************************************************************************************************************************/
  84. // Pre-allocate a list of contacts so Unity doesn't need to allocate a new one every time.
  85. // It's static because every character can use the same list one at a time.
  86. private static readonly List<ContactPoint2D> Contacts = new();
  87. private void UpdateMovementState()
  88. {
  89. int contactCount = _Collider.GetContacts(Contacts);
  90. for (int i = 0; i < contactCount; i++)
  91. {
  92. // If we're moving directly towards an object (or within 30 degrees of it), we are pushing it.
  93. if (Vector2.Angle(Contacts[i].normal, _Movement) > 180 - 30)
  94. {
  95. Play(_Push, AnimationGroup.Movement);
  96. return;
  97. }
  98. }
  99. DirectionalAnimationSet animations = SampleInput.LeftShiftHold ? _Run : _Walk;
  100. Play(animations, AnimationGroup.Movement);
  101. }
  102. /************************************************************************************************************************/
  103. protected virtual void FixedUpdate()
  104. {
  105. // Determine the desired speed based on the current animation.
  106. float speed = _CurrentAnimationSet == _Run ? _RunSpeed : _WalkSpeed;
  107. _Rigidbody.velocity = _Movement * speed;
  108. }
  109. /************************************************************************************************************************/
  110. #if UNITY_EDITOR
  111. /************************************************************************************************************************/
  112. /// <summary>[Editor-Only]
  113. /// Sets the character's starting sprite in Edit Mode so you can see it while working in the scene.
  114. /// </summary>
  115. /// <remarks>Called in Edit Mode whenever this script is loaded or a value is changed in the Inspector.</remarks>
  116. protected virtual void OnValidate()
  117. {
  118. if (_Idle != null)
  119. _Idle.GetClip(_Facing).EditModePlay(_Animancer);
  120. }
  121. /************************************************************************************************************************/
  122. #endif
  123. /************************************************************************************************************************/
  124. #else
  125. /************************************************************************************************************************/
  126. protected virtual void Awake()
  127. {
  128. SampleReadMe.LogMissingPhysics3DModuleError(this);
  129. }
  130. /************************************************************************************************************************/
  131. #endif
  132. /************************************************************************************************************************/
  133. }
  134. }