ThirdPersonCharacter.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. using UnityEngine;
  2. namespace UnityStandardAssets.Characters.ThirdPerson.PunDemos
  3. {
  4. [RequireComponent(typeof(Rigidbody))]
  5. [RequireComponent(typeof(CapsuleCollider))]
  6. [RequireComponent(typeof(Animator))]
  7. public class ThirdPersonCharacter : MonoBehaviour
  8. {
  9. [SerializeField] float m_MovingTurnSpeed = 360;
  10. [SerializeField] float m_StationaryTurnSpeed = 180;
  11. [SerializeField] float m_JumpPower = 12f;
  12. [Range(1f, 4f)][SerializeField] float m_GravityMultiplier = 2f;
  13. [SerializeField] float m_RunCycleLegOffset = 0.2f; //specific to the character in sample assets, will need to be modified to work with others
  14. [SerializeField] float m_MoveSpeedMultiplier = 1f;
  15. [SerializeField] float m_AnimSpeedMultiplier = 1f;
  16. [SerializeField] float m_GroundCheckDistance = 0.1f;
  17. Rigidbody m_Rigidbody;
  18. Animator m_Animator;
  19. bool m_IsGrounded;
  20. float m_OrigGroundCheckDistance;
  21. const float k_Half = 0.5f;
  22. float m_TurnAmount;
  23. float m_ForwardAmount;
  24. Vector3 m_GroundNormal;
  25. float m_CapsuleHeight;
  26. Vector3 m_CapsuleCenter;
  27. CapsuleCollider m_Capsule;
  28. bool m_Crouching;
  29. void Start()
  30. {
  31. m_Animator = GetComponent<Animator>();
  32. m_Rigidbody = GetComponent<Rigidbody>();
  33. m_Capsule = GetComponent<CapsuleCollider>();
  34. m_CapsuleHeight = m_Capsule.height;
  35. m_CapsuleCenter = m_Capsule.center;
  36. m_Rigidbody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY | RigidbodyConstraints.FreezeRotationZ;
  37. m_OrigGroundCheckDistance = m_GroundCheckDistance;
  38. }
  39. public void Move(Vector3 move, bool crouch, bool jump)
  40. {
  41. // convert the world relative moveInput vector into a local-relative
  42. // turn amount and forward amount required to head in the desired
  43. // direction.
  44. if (move.magnitude > 1f) move.Normalize();
  45. move = transform.InverseTransformDirection(move);
  46. CheckGroundStatus();
  47. move = Vector3.ProjectOnPlane(move, m_GroundNormal);
  48. m_TurnAmount = Mathf.Atan2(move.x, move.z);
  49. m_ForwardAmount = move.z;
  50. ApplyExtraTurnRotation();
  51. // control and velocity handling is different when grounded and airborne:
  52. if (m_IsGrounded)
  53. {
  54. HandleGroundedMovement(crouch, jump);
  55. }
  56. else
  57. {
  58. HandleAirborneMovement();
  59. }
  60. ScaleCapsuleForCrouching(crouch);
  61. PreventStandingInLowHeadroom();
  62. // send input and other state parameters to the animator
  63. UpdateAnimator(move);
  64. }
  65. void ScaleCapsuleForCrouching(bool crouch)
  66. {
  67. if (m_IsGrounded && crouch)
  68. {
  69. if (m_Crouching) return;
  70. m_Capsule.height = m_Capsule.height / 2f;
  71. m_Capsule.center = m_Capsule.center / 2f;
  72. m_Crouching = true;
  73. }
  74. else
  75. {
  76. Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up);
  77. float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half;
  78. if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength, Physics.AllLayers, QueryTriggerInteraction.Ignore))
  79. {
  80. m_Crouching = true;
  81. return;
  82. }
  83. m_Capsule.height = m_CapsuleHeight;
  84. m_Capsule.center = m_CapsuleCenter;
  85. m_Crouching = false;
  86. }
  87. }
  88. void PreventStandingInLowHeadroom()
  89. {
  90. // prevent standing up in crouch-only zones
  91. if (!m_Crouching)
  92. {
  93. Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up);
  94. float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half;
  95. if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength, Physics.AllLayers, QueryTriggerInteraction.Ignore))
  96. {
  97. m_Crouching = true;
  98. }
  99. }
  100. }
  101. void UpdateAnimator(Vector3 move)
  102. {
  103. // update the animator parameters
  104. m_Animator.SetFloat("Forward", m_ForwardAmount, 0.1f, Time.deltaTime);
  105. m_Animator.SetFloat("Turn", m_TurnAmount, 0.1f, Time.deltaTime);
  106. m_Animator.SetBool("Crouch", m_Crouching);
  107. m_Animator.SetBool("OnGround", m_IsGrounded);
  108. if (!m_IsGrounded)
  109. {
  110. m_Animator.SetFloat("Jump", m_Rigidbody.velocity.y);
  111. }
  112. // calculate which leg is behind, so as to leave that leg trailing in the jump animation
  113. // (This code is reliant on the specific run cycle offset in our animations,
  114. // and assumes one leg passes the other at the normalized clip times of 0.0 and 0.5)
  115. float runCycle =
  116. Mathf.Repeat(
  117. m_Animator.GetCurrentAnimatorStateInfo(0).normalizedTime + m_RunCycleLegOffset, 1);
  118. float jumpLeg = (runCycle < k_Half ? 1 : -1) * m_ForwardAmount;
  119. if (m_IsGrounded)
  120. {
  121. m_Animator.SetFloat("JumpLeg", jumpLeg);
  122. }
  123. // the anim speed multiplier allows the overall speed of walking/running to be tweaked in the inspector,
  124. // which affects the movement speed because of the root motion.
  125. if (m_IsGrounded && move.magnitude > 0)
  126. {
  127. m_Animator.speed = m_AnimSpeedMultiplier;
  128. }
  129. else
  130. {
  131. // don't use that while airborne
  132. m_Animator.speed = 1;
  133. }
  134. }
  135. void HandleAirborneMovement()
  136. {
  137. // apply extra gravity from multiplier:
  138. Vector3 extraGravityForce = (Physics.gravity * m_GravityMultiplier) - Physics.gravity;
  139. m_Rigidbody.AddForce(extraGravityForce);
  140. m_GroundCheckDistance = m_Rigidbody.velocity.y < 0 ? m_OrigGroundCheckDistance : 0.01f;
  141. }
  142. void HandleGroundedMovement(bool crouch, bool jump)
  143. {
  144. // check whether conditions are right to allow a jump:
  145. if (jump && !crouch && m_Animator.GetCurrentAnimatorStateInfo(0).IsName("Grounded"))
  146. {
  147. // jump!
  148. m_Rigidbody.velocity = new Vector3(m_Rigidbody.velocity.x, m_JumpPower, m_Rigidbody.velocity.z);
  149. m_IsGrounded = false;
  150. m_Animator.applyRootMotion = false;
  151. m_GroundCheckDistance = 0.1f;
  152. }
  153. }
  154. void ApplyExtraTurnRotation()
  155. {
  156. // help the character turn faster (this is in addition to root rotation in the animation)
  157. float turnSpeed = Mathf.Lerp(m_StationaryTurnSpeed, m_MovingTurnSpeed, m_ForwardAmount);
  158. transform.Rotate(0, m_TurnAmount * turnSpeed * Time.deltaTime, 0);
  159. }
  160. public void OnAnimatorMove()
  161. {
  162. // we implement this function to override the default root motion.
  163. // this allows us to modify the positional speed before it's applied.
  164. if (m_IsGrounded && Time.deltaTime > 0)
  165. {
  166. Vector3 v = (m_Animator.deltaPosition * m_MoveSpeedMultiplier) / Time.deltaTime;
  167. // we preserve the existing y part of the current velocity.
  168. v.y = m_Rigidbody.velocity.y;
  169. m_Rigidbody.velocity = v;
  170. }
  171. }
  172. void CheckGroundStatus()
  173. {
  174. RaycastHit hitInfo;
  175. #if UNITY_EDITOR
  176. // helper to visualise the ground check ray in the scene view
  177. Debug.DrawLine(transform.position + (Vector3.up * 0.1f), transform.position + (Vector3.up * 0.1f) + (Vector3.down * m_GroundCheckDistance));
  178. #endif
  179. // 0.1f is a small offset to start the ray from inside the character
  180. // it is also good to note that the transform position in the sample assets is at the base of the character
  181. if (Physics.Raycast(transform.position + (Vector3.up * 0.1f), Vector3.down, out hitInfo, m_GroundCheckDistance))
  182. {
  183. m_GroundNormal = hitInfo.normal;
  184. m_IsGrounded = true;
  185. m_Animator.applyRootMotion = true;
  186. }
  187. else
  188. {
  189. m_IsGrounded = false;
  190. m_GroundNormal = Vector3.up;
  191. m_Animator.applyRootMotion = false;
  192. }
  193. }
  194. }
  195. }