RigidbodyFirstPersonController.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. using System;
  2. using UnityEngine;
  3. using UnityStandardAssets.CrossPlatformInput;
  4. namespace UnityStandardAssets.Characters.FirstPerson
  5. {
  6. [RequireComponent(typeof (Rigidbody))]
  7. [RequireComponent(typeof (CapsuleCollider))]
  8. public class RigidbodyFirstPersonController : MonoBehaviour
  9. {
  10. [Serializable]
  11. public class MovementSettings
  12. {
  13. public float ForwardSpeed = 8.0f; // Speed when walking forward
  14. public float BackwardSpeed = 4.0f; // Speed when walking backwards
  15. public float StrafeSpeed = 4.0f; // Speed when walking sideways
  16. public float RunMultiplier = 2.0f; // Speed when sprinting
  17. public KeyCode RunKey = KeyCode.LeftShift;
  18. public float JumpForce = 30f;
  19. public AnimationCurve SlopeCurveModifier = new AnimationCurve(new Keyframe(-90.0f, 1.0f), new Keyframe(0.0f, 1.0f), new Keyframe(90.0f, 0.0f));
  20. [HideInInspector] public float CurrentTargetSpeed = 8f;
  21. #if !MOBILE_INPUT
  22. private bool m_Running;
  23. #endif
  24. public void UpdateDesiredTargetSpeed(Vector2 input)
  25. {
  26. if (input == Vector2.zero) return;
  27. if (input.x > 0 || input.x < 0)
  28. {
  29. //strafe
  30. CurrentTargetSpeed = StrafeSpeed;
  31. }
  32. if (input.y < 0)
  33. {
  34. //backwards
  35. CurrentTargetSpeed = BackwardSpeed;
  36. }
  37. if (input.y > 0)
  38. {
  39. //forwards
  40. //handled last as if strafing and moving forward at the same time forwards speed should take precedence
  41. CurrentTargetSpeed = ForwardSpeed;
  42. }
  43. #if !MOBILE_INPUT
  44. if (Input.GetKey(RunKey))
  45. {
  46. CurrentTargetSpeed *= RunMultiplier;
  47. m_Running = true;
  48. }
  49. else
  50. {
  51. m_Running = false;
  52. }
  53. #endif
  54. }
  55. #if !MOBILE_INPUT
  56. public bool Running
  57. {
  58. get { return m_Running; }
  59. }
  60. #endif
  61. }
  62. [Serializable]
  63. public class AdvancedSettings
  64. {
  65. public float groundCheckDistance = 0.01f; // distance for checking if the controller is grounded ( 0.01f seems to work best for this )
  66. public float stickToGroundHelperDistance = 0.5f; // stops the character
  67. public float slowDownRate = 20f; // rate at which the controller comes to a stop when there is no input
  68. public bool airControl; // can the user control the direction that is being moved in the air
  69. [Tooltip("set it to 0.1 or more if you get stuck in wall")]
  70. public float shellOffset; //reduce the radius by that ratio to avoid getting stuck in wall (a value of 0.1f is nice)
  71. }
  72. public Camera cam;
  73. public MovementSettings movementSettings = new MovementSettings();
  74. public MouseLook mouseLook = new MouseLook();
  75. public AdvancedSettings advancedSettings = new AdvancedSettings();
  76. private Rigidbody m_RigidBody;
  77. private CapsuleCollider m_Capsule;
  78. private float m_YRotation;
  79. private Vector3 m_GroundContactNormal;
  80. private bool m_Jump, m_PreviouslyGrounded, m_Jumping, m_IsGrounded;
  81. public Vector3 Velocity
  82. {
  83. get { return m_RigidBody.velocity; }
  84. }
  85. public bool Grounded
  86. {
  87. get { return m_IsGrounded; }
  88. }
  89. public bool Jumping
  90. {
  91. get { return m_Jumping; }
  92. }
  93. public bool Running
  94. {
  95. get
  96. {
  97. #if !MOBILE_INPUT
  98. return movementSettings.Running;
  99. #else
  100. return false;
  101. #endif
  102. }
  103. }
  104. private void Start()
  105. {
  106. m_RigidBody = GetComponent<Rigidbody>();
  107. m_Capsule = GetComponent<CapsuleCollider>();
  108. mouseLook.Init (transform, cam.transform);
  109. }
  110. private void Update()
  111. {
  112. RotateView();
  113. if (CrossPlatformInputManager.GetButtonDown("Jump") && !m_Jump)
  114. {
  115. m_Jump = true;
  116. }
  117. }
  118. private void FixedUpdate()
  119. {
  120. GroundCheck();
  121. Vector2 input = GetInput();
  122. if ((Mathf.Abs(input.x) > float.Epsilon || Mathf.Abs(input.y) > float.Epsilon) && (advancedSettings.airControl || m_IsGrounded))
  123. {
  124. // always move along the camera forward as it is the direction that it being aimed at
  125. Vector3 desiredMove = cam.transform.forward*input.y + cam.transform.right*input.x;
  126. desiredMove = Vector3.ProjectOnPlane(desiredMove, m_GroundContactNormal).normalized;
  127. desiredMove.x = desiredMove.x*movementSettings.CurrentTargetSpeed;
  128. desiredMove.z = desiredMove.z*movementSettings.CurrentTargetSpeed;
  129. desiredMove.y = desiredMove.y*movementSettings.CurrentTargetSpeed;
  130. if (m_RigidBody.velocity.sqrMagnitude <
  131. (movementSettings.CurrentTargetSpeed*movementSettings.CurrentTargetSpeed))
  132. {
  133. m_RigidBody.AddForce(desiredMove*SlopeMultiplier(), ForceMode.Impulse);
  134. }
  135. }
  136. if (m_IsGrounded)
  137. {
  138. m_RigidBody.drag = 5f;
  139. if (m_Jump)
  140. {
  141. m_RigidBody.drag = 0f;
  142. m_RigidBody.velocity = new Vector3(m_RigidBody.velocity.x, 0f, m_RigidBody.velocity.z);
  143. m_RigidBody.AddForce(new Vector3(0f, movementSettings.JumpForce, 0f), ForceMode.Impulse);
  144. m_Jumping = true;
  145. }
  146. if (!m_Jumping && Mathf.Abs(input.x) < float.Epsilon && Mathf.Abs(input.y) < float.Epsilon && m_RigidBody.velocity.magnitude < 1f)
  147. {
  148. m_RigidBody.Sleep();
  149. }
  150. }
  151. else
  152. {
  153. m_RigidBody.drag = 0f;
  154. if (m_PreviouslyGrounded && !m_Jumping)
  155. {
  156. StickToGroundHelper();
  157. }
  158. }
  159. m_Jump = false;
  160. }
  161. private float SlopeMultiplier()
  162. {
  163. float angle = Vector3.Angle(m_GroundContactNormal, Vector3.up);
  164. return movementSettings.SlopeCurveModifier.Evaluate(angle);
  165. }
  166. private void StickToGroundHelper()
  167. {
  168. RaycastHit hitInfo;
  169. if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,
  170. ((m_Capsule.height/2f) - m_Capsule.radius) +
  171. advancedSettings.stickToGroundHelperDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
  172. {
  173. if (Mathf.Abs(Vector3.Angle(hitInfo.normal, Vector3.up)) < 85f)
  174. {
  175. m_RigidBody.velocity = Vector3.ProjectOnPlane(m_RigidBody.velocity, hitInfo.normal);
  176. }
  177. }
  178. }
  179. private Vector2 GetInput()
  180. {
  181. Vector2 input = new Vector2
  182. {
  183. x = CrossPlatformInputManager.GetAxis("Horizontal"),
  184. y = CrossPlatformInputManager.GetAxis("Vertical")
  185. };
  186. movementSettings.UpdateDesiredTargetSpeed(input);
  187. return input;
  188. }
  189. private void RotateView()
  190. {
  191. //avoids the mouse looking if the game is effectively paused
  192. if (Mathf.Abs(Time.timeScale) < float.Epsilon) return;
  193. // get the rotation before it's changed
  194. float oldYRotation = transform.eulerAngles.y;
  195. mouseLook.LookRotation (transform, cam.transform);
  196. if (m_IsGrounded || advancedSettings.airControl)
  197. {
  198. // Rotate the rigidbody velocity to match the new direction that the character is looking
  199. Quaternion velRotation = Quaternion.AngleAxis(transform.eulerAngles.y - oldYRotation, Vector3.up);
  200. m_RigidBody.velocity = velRotation*m_RigidBody.velocity;
  201. }
  202. }
  203. /// sphere cast down just beyond the bottom of the capsule to see if the capsule is colliding round the bottom
  204. private void GroundCheck()
  205. {
  206. m_PreviouslyGrounded = m_IsGrounded;
  207. RaycastHit hitInfo;
  208. if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,
  209. ((m_Capsule.height/2f) - m_Capsule.radius) + advancedSettings.groundCheckDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
  210. {
  211. m_IsGrounded = true;
  212. m_GroundContactNormal = hitInfo.normal;
  213. }
  214. else
  215. {
  216. m_IsGrounded = false;
  217. m_GroundContactNormal = Vector3.up;
  218. }
  219. if (!m_PreviouslyGrounded && m_IsGrounded && m_Jumping)
  220. {
  221. m_Jumping = false;
  222. }
  223. }
  224. }
  225. }