armaan_s92

fps controller

Nov 18th, 2025
19
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.68 KB | None | 0 0
  1. using UnityEngine;
  2. #if ENABLE_INPUT_SYSTEM
  3. using UnityEngine.InputSystem;
  4. #endif
  5. using AC;
  6.  
  7. namespace StarterAssets
  8. {
  9. [RequireComponent(typeof(CharacterController))]
  10. #if ENABLE_INPUT_SYSTEM
  11. [RequireComponent(typeof(UnityEngine.InputSystem.PlayerInput))]
  12. #endif
  13.  
  14. public class FirstPersonController : MonoBehaviour
  15. {
  16. const string PrefMouse = "MouseLookSensitivity";
  17. const string PrefController = "ControllerLookSensitivity";
  18. const string PrefInvertY = "InvertY";
  19.  
  20. [Header("Player")]
  21. [Tooltip("Move speed of the character in m/s")]
  22. public float MoveSpeed = 4.0f;
  23. [Tooltip("Sprint speed of the character in m/s")]
  24. public float SprintSpeed = 6.0f;
  25. [Tooltip("Rotation speed of the character")]
  26. public float RotationSpeed = 1.0f;
  27. [Tooltip("Acceleration and deceleration")]
  28. public float SpeedChangeRate = 10.0f;
  29. [Tooltip("Multiplier for controller look sensitivity")]
  30. public float ControllerLookMultiplier = 0.5f;
  31. public bool invertY = false;
  32. private const float YAxisReduction = 0.75f;
  33.  
  34.  
  35. [Header("Look")]
  36. [Tooltip("Multiplier for mouse look sensitivity")]
  37. [Range(0.1f, 3f)]
  38. public float MouseLookMultiplier = 1.0f;
  39.  
  40. [Space(10)]
  41. [Tooltip("The height the player can jump")]
  42. public float JumpHeight = 1.2f;
  43. [Tooltip("The character uses its own gravity value. The engine default is -9.81f")]
  44. public float Gravity = -15.0f;
  45.  
  46. [Space(10)]
  47. [Tooltip("Time required to pass before being able to jump again. Set to 0f to instantly jump again")]
  48. public float JumpTimeout = 0.1f;
  49. [Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]
  50. public float FallTimeout = 0.15f;
  51.  
  52. [Header("Player Grounded")]
  53. [Tooltip("If the character is grounded or not. Not part of the CharacterController built in grounded check")]
  54. public bool Grounded = true;
  55. [Tooltip("Useful for rough ground")]
  56. public float GroundedOffset = -0.14f;
  57. [Tooltip("The radius of the grounded check. Should match the radius of the CharacterController")]
  58. public float GroundedRadius = 0.5f;
  59. [Tooltip("What layers the character uses as ground")]
  60. public LayerMask GroundLayers;
  61.  
  62. [Header("Cinemachine")]
  63. [Tooltip("The follow target set in the Cinemachine Virtual Camera that the camera will follow")]
  64. public GameObject CinemachineCameraTarget;
  65. [Tooltip("How far in degrees can you move the camera up")]
  66. public float TopClamp = 90.0f;
  67. [Tooltip("How far in degrees can you move the camera down")]
  68. public float BottomClamp = -90.0f;
  69.  
  70. // cinemachine
  71. private float _cinemachineTargetPitch;
  72.  
  73. // player
  74. private float _speed;
  75. private float _rotationVelocity;
  76. private float _verticalVelocity;
  77. private float _terminalVelocity = 53.0f;
  78.  
  79. // timeout deltatime
  80. private float _jumpTimeoutDelta;
  81. private float _fallTimeoutDelta;
  82.  
  83.  
  84. #if ENABLE_INPUT_SYSTEM
  85. private UnityEngine.InputSystem.PlayerInput _playerInput;
  86. #endif
  87.  
  88. private CharacterController _controller;
  89. private StarterAssetsInputs _input;
  90. private GameObject _mainCamera;
  91.  
  92. private const float _threshold = 0.01f;
  93.  
  94. private bool IsCurrentDeviceMouse
  95. {
  96. get
  97. {
  98. #if ENABLE_INPUT_SYSTEM
  99. return _playerInput.currentControlScheme == "KeyboardMouse";
  100. #else
  101. return false;
  102. #endif
  103. }
  104. }
  105.  
  106. private void Awake()
  107. {
  108.  
  109. if (_mainCamera == null)
  110. {
  111. _mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
  112. }
  113. }
  114.  
  115. private void Start()
  116. {
  117. _controller = GetComponent<CharacterController>();
  118. _input = GetComponent<StarterAssetsInputs>();
  119.  
  120. #if ENABLE_INPUT_SYSTEM
  121. _playerInput = GetComponent<UnityEngine.InputSystem.PlayerInput>();
  122. #else
  123. Debug.LogError("Starter Assets package is missing dependencies. Please use Tools/Starter Assets/Reinstall Dependencies to fix it");
  124. #endif
  125.  
  126.  
  127. _jumpTimeoutDelta = JumpTimeout;
  128. _fallTimeoutDelta = FallTimeout;
  129. }
  130.  
  131.  
  132. private void OnEnable()
  133. {
  134.  
  135. if (PlayerPrefs.HasKey(PrefInvertY))
  136. {
  137. invertY = PlayerPrefs.GetInt(PrefInvertY) == 1;
  138. }
  139. else
  140. {
  141. GVar invertVar = GlobalVariables.GetVariable("InvertY");
  142. if (invertVar != null)
  143. invertY = invertVar.BooleanValue;
  144. }
  145.  
  146.  
  147. if (PlayerPrefs.HasKey(PrefMouse))
  148. {
  149. float m = Mathf.Clamp(PlayerPrefs.GetFloat(PrefMouse), 0.1f, 4f);
  150. MouseLookMultiplier = Mathf.Round(m * 10f) / 10f;
  151. }
  152. if (PlayerPrefs.HasKey(PrefController))
  153. {
  154. float c = Mathf.Clamp(PlayerPrefs.GetFloat(PrefController), 0.1f, 1f);
  155. ControllerLookMultiplier = Mathf.Round(c * 100f) / 100f;
  156. }
  157. }
  158.  
  159.  
  160.  
  161. private void Update()
  162. {
  163. JumpAndGravity();
  164. GroundedCheck();
  165. Move();
  166. }
  167.  
  168. private void LateUpdate()
  169. {
  170. CameraRotation();
  171. }
  172.  
  173. private void GroundedCheck()
  174. {
  175.  
  176. Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z);
  177. Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers, QueryTriggerInteraction.Ignore);
  178. }
  179.  
  180. private void CameraRotation()
  181. {
  182.  
  183. if (_input.look.sqrMagnitude >= _threshold)
  184. {
  185.  
  186. float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1.0f : Time.deltaTime;
  187.  
  188. float deviceMultiplier = IsCurrentDeviceMouse ? MouseLookMultiplier : ControllerLookMultiplier;
  189.  
  190. float yInput = _input.look.y;
  191. if (!IsCurrentDeviceMouse && invertY)
  192. yInput = -yInput;
  193. _cinemachineTargetPitch += yInput * RotationSpeed * deltaTimeMultiplier * deviceMultiplier * YAxisReduction;
  194. _rotationVelocity = _input.look.x * RotationSpeed * deltaTimeMultiplier * deviceMultiplier;
  195.  
  196.  
  197. // clamp our pitch rotation
  198. _cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp);
  199.  
  200. // Update Cinemachine camera target pitch
  201. CinemachineCameraTarget.transform.localRotation = Quaternion.Euler(_cinemachineTargetPitch, 0.0f, 0.0f);
  202.  
  203. // rotate the player left and right
  204. transform.Rotate(Vector3.up * _rotationVelocity);
  205. }
  206. }
  207. public void SetMouseSensitivity(float value)
  208. {
  209. MouseLookMultiplier = value;
  210. }
  211.  
  212. public void SetInvertY(bool on)
  213. {
  214. invertY = on;
  215. PlayerPrefs.SetInt(PrefInvertY, on ? 1 : 0);
  216.  
  217.  
  218. GVar invertVar = GlobalVariables.GetVariable("InvertY");
  219. if (invertVar != null)
  220. invertVar.BooleanValue = on;
  221. }
  222.  
  223. private void Move()
  224. {
  225. // set target speed based on move speed, sprint speed and if sprint is pressed
  226. float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;
  227.  
  228. // a simplistic acceleration and deceleration designed to be easy to remove, replace, or iterate upon
  229.  
  230. // note: Vector2's == operator uses approximation so is not floating point error prone, and is cheaper than magnitude
  231. // if there is no input, set the target speed to 0
  232. if (_input.move == Vector2.zero) targetSpeed = 0.0f;
  233.  
  234. // a reference to the players current horizontal velocity
  235. float currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude;
  236.  
  237. float speedOffset = 0.1f;
  238. float inputMagnitude = _input.analogMovement ? _input.move.magnitude : 1f;
  239.  
  240. // accelerate or decelerate to target speed
  241. if (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset)
  242. {
  243. // creates curved result rather than a linear one giving a more organic speed change
  244. // note T in Lerp is clamped, so we don't need to clamp our speed
  245. _speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude, Time.deltaTime * SpeedChangeRate);
  246.  
  247. // round speed to 3 decimal places
  248. _speed = Mathf.Round(_speed * 1000f) / 1000f;
  249. }
  250. else
  251. {
  252. _speed = targetSpeed;
  253. }
  254.  
  255. // normalise input direction
  256. Vector3 inputDirection = new Vector3(_input.move.x, 0.0f, _input.move.y).normalized;
  257.  
  258. // note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
  259. // if there is a move input rotate player when the player is moving
  260. if (_input.move != Vector2.zero)
  261. {
  262. // move
  263. inputDirection = transform.right * _input.move.x + transform.forward * _input.move.y;
  264. }
  265.  
  266. // move the player
  267. _controller.Move(inputDirection.normalized * (_speed * Time.deltaTime) + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime);
  268. }
  269.  
  270. private void JumpAndGravity()
  271. {
  272. if (Grounded)
  273. {
  274. // reset the fall timeout timer
  275. _fallTimeoutDelta = FallTimeout;
  276.  
  277. // stop our velocity dropping infinitely when grounded
  278. if (_verticalVelocity < 0.0f)
  279. {
  280. _verticalVelocity = -2f;
  281. }
  282.  
  283. // Jump
  284. if (_input.jump && _jumpTimeoutDelta <= 0.0f)
  285. {
  286. // the square root of H * -2 * G = how much velocity needed to reach desired height
  287. _verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);
  288. }
  289.  
  290. // jump timeout
  291. if (_jumpTimeoutDelta >= 0.0f)
  292. {
  293. _jumpTimeoutDelta -= Time.deltaTime;
  294. }
  295. }
  296. else
  297. {
  298. // reset the jump timeout timer
  299. _jumpTimeoutDelta = JumpTimeout;
  300.  
  301. // fall timeout
  302. if (_fallTimeoutDelta >= 0.0f)
  303. {
  304. _fallTimeoutDelta -= Time.deltaTime;
  305. }
  306.  
  307. // if we are not grounded, do not jump
  308. _input.jump = false;
  309. }
  310.  
  311. // apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)
  312. if (_verticalVelocity < _terminalVelocity)
  313. {
  314. _verticalVelocity += Gravity * Time.deltaTime;
  315. }
  316. }
  317.  
  318. private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
  319. {
  320. if (lfAngle < -360f) lfAngle += 360f;
  321. if (lfAngle > 360f) lfAngle -= 360f;
  322. return Mathf.Clamp(lfAngle, lfMin, lfMax);
  323. }
  324.  
  325. private void OnDrawGizmosSelected()
  326. {
  327. Color transparentGreen = new Color(0.0f, 1.0f, 0.0f, 0.35f);
  328. Color transparentRed = new Color(1.0f, 0.0f, 0.0f, 0.35f);
  329.  
  330. if (Grounded) Gizmos.color = transparentGreen;
  331. else Gizmos.color = transparentRed;
  332.  
  333. // when selected, draw a gizmo in the position of, and matching radius of, the grounded collider
  334. Gizmos.DrawSphere(new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z), GroundedRadius);
  335. }
  336. }
  337. }
Advertisement
Add Comment
Please, Sign In to add comment