CharacterMovement.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 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;
  5. using UnityEngine;
  6. using static Animancer.Validate;
  7. namespace Animancer.Examples.AnimatorControllers.GameKit
  8. {
  9. /// <summary>The stats and logic for moving a <see cref="Character"/>.</summary>
  10. /// <example><see href="https://kybernetik.com.au/animancer/docs/examples/animator-controllers/3d-game-kit">3D Game Kit</see></example>
  11. /// https://kybernetik.com.au/animancer/api/Animancer.Examples.AnimatorControllers.GameKit/CharacterMovement
  12. ///
  13. public sealed class CharacterMovement : MonoBehaviour
  14. {
  15. /************************************************************************************************************************/
  16. [SerializeField] private Character _Character;
  17. [SerializeField] private CharacterController _CharacterController;
  18. [SerializeField] private bool _FullMovementControl = true;
  19. /************************************************************************************************************************/
  20. [SerializeField, MetersPerSecond(Rule = Value.IsNotNegative)]
  21. private float _MaxSpeed = 8;
  22. public float MaxSpeed => _MaxSpeed;
  23. [SerializeField, MetersPerSecondPerSecond(Rule = Value.IsNotNegative)]
  24. private float _Acceleration = 20;
  25. public float Acceleration => _Acceleration;
  26. [SerializeField, MetersPerSecondPerSecond(Rule = Value.IsNotNegative)]
  27. private float _Deceleration = 25;
  28. public float Deceleration => _Deceleration;
  29. [SerializeField, DegreesPerSecond(Rule = Value.IsNotNegative)]
  30. private float _MinTurnSpeed = 400;
  31. public float MinTurnSpeed => _MinTurnSpeed;
  32. [SerializeField, DegreesPerSecond(Rule = Value.IsNotNegative)]
  33. private float _MaxTurnSpeed = 1200;
  34. public float MaxTurnSpeed => _MaxTurnSpeed;
  35. [SerializeField, MetersPerSecondPerSecond(Rule = Value.IsNotNegative)]
  36. private float _Gravity = 20;
  37. public float Gravity => _Gravity;
  38. [SerializeField, Multiplier(Rule = Value.IsNotNegative)]
  39. private float _StickingGravityProportion = 0.3f;
  40. public float StickingGravityProportion => _StickingGravityProportion;
  41. /************************************************************************************************************************/
  42. public bool IsGrounded { get; private set; }
  43. public Material GroundMaterial { get; private set; }
  44. /************************************************************************************************************************/
  45. public void UpdateSpeedControl()
  46. {
  47. var movement = _Character.Parameters.MovementDirection;
  48. _Character.Parameters.DesiredForwardSpeed = movement.magnitude * MaxSpeed;
  49. var deltaSpeed = movement != default ? Acceleration : Deceleration;
  50. _Character.Parameters.ForwardSpeed = Mathf.MoveTowards(
  51. _Character.Parameters.ForwardSpeed,
  52. _Character.Parameters.DesiredForwardSpeed,
  53. deltaSpeed * Time.deltaTime);
  54. }
  55. /************************************************************************************************************************/
  56. public float CurrentTurnSpeed
  57. {
  58. get
  59. {
  60. return Mathf.Lerp(
  61. MaxTurnSpeed,
  62. MinTurnSpeed,
  63. _Character.Parameters.ForwardSpeed / _Character.Parameters.DesiredForwardSpeed);
  64. }
  65. }
  66. /************************************************************************************************************************/
  67. public bool GetTurnAngles(Vector3 direction, out float currentAngle, out float targetAngle)
  68. {
  69. if (direction == default)
  70. {
  71. currentAngle = float.NaN;
  72. targetAngle = float.NaN;
  73. return false;
  74. }
  75. var transform = this.transform;
  76. currentAngle = transform.eulerAngles.y;
  77. targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
  78. return true;
  79. }
  80. /************************************************************************************************************************/
  81. public void TurnTowards(float currentAngle, float targetAngle, float speed)
  82. {
  83. currentAngle = Mathf.MoveTowardsAngle(currentAngle, targetAngle, speed * Time.deltaTime);
  84. transform.eulerAngles = new Vector3(0, currentAngle, 0);
  85. }
  86. public void TurnTowards(Vector3 direction, float speed)
  87. {
  88. if (GetTurnAngles(direction, out var currentAngle, out var targetAngle))
  89. TurnTowards(currentAngle, targetAngle, speed);
  90. }
  91. /************************************************************************************************************************/
  92. private void OnAnimatorMove()
  93. {
  94. var movement = GetRootMotion();
  95. CheckGround(ref movement);
  96. UpdateGravity(ref movement);
  97. _CharacterController.Move(movement);
  98. IsGrounded = _CharacterController.isGrounded;
  99. transform.rotation *= _Character.Animancer.Animator.deltaRotation;
  100. }
  101. /************************************************************************************************************************/
  102. private Vector3 GetRootMotion()
  103. {
  104. var motion = _Character.StateMachine.CurrentState.RootMotion;
  105. if (!_FullMovementControl ||// If Full Movement Control is disabled in the Inspector.
  106. !_Character.StateMachine.CurrentState.FullMovementControl)// Or the current state does not want it.
  107. return motion;// Return the raw Root Motion.
  108. // If the Brain is not trying to move, we do not move.
  109. var direction = _Character.Parameters.MovementDirection;
  110. direction.y = 0;
  111. if (direction == default)
  112. return default;
  113. // Otherwise calculate the Root Motion only in the specified direction.
  114. direction.Normalize();
  115. var magnitude = Vector3.Dot(direction, motion);
  116. return direction * magnitude;
  117. }
  118. /************************************************************************************************************************/
  119. private void CheckGround(ref Vector3 movement)
  120. {
  121. if (!_CharacterController.isGrounded)
  122. return;
  123. const float GroundedRayDistance = 1f;
  124. var ray = new Ray(transform.position + GroundedRayDistance * 0.5f * Vector3.up, -Vector3.up);
  125. if (Physics.Raycast(ray, out var hit, GroundedRayDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
  126. {
  127. // Rotate the movement to lie along the ground vector.
  128. movement = Vector3.ProjectOnPlane(movement, hit.normal);
  129. // Store the current walking surface so the correct audio is played.
  130. var groundRenderer = hit.collider.GetComponentInChildren<Renderer>();
  131. GroundMaterial = groundRenderer ? groundRenderer.sharedMaterial : null;
  132. }
  133. else
  134. {
  135. GroundMaterial = null;
  136. }
  137. }
  138. /************************************************************************************************************************/
  139. private void UpdateGravity(ref Vector3 movement)
  140. {
  141. if (_CharacterController.isGrounded && _Character.StateMachine.CurrentState.StickToGround)
  142. _Character.Parameters.VerticalSpeed = -Gravity * StickingGravityProportion;
  143. else
  144. _Character.Parameters.VerticalSpeed -= Gravity * Time.deltaTime;
  145. movement.y += _Character.Parameters.VerticalSpeed * Time.deltaTime;
  146. }
  147. /************************************************************************************************************************/
  148. // Ignore these Animation Events because the attack animations will only start when we tell them to, so it
  149. // would be silly to use additional events for something we already directly caused. That sort of thing is only
  150. // necessary in Animator Controllers because they run their own logic to decide what they want to do.
  151. private void MeleeAttackStart(int throwing = 0) { }
  152. private void MeleeAttackEnd() { }
  153. /************************************************************************************************************************/
  154. }
  155. }