Guest User

Untitled

a guest
Aug 1st, 2026
37
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.24 KB | Source Code | 0 0
  1. using UnityEngine;
  2. #if ENABLE_INPUT_SYSTEM
  3. using UnityEngine.InputSystem;
  4. using UnityEngine.UIElements;
  5. using Unity.Cinemachine;
  6. #endif
  7.  
  8. namespace StarterAssets
  9. {
  10. [RequireComponent(typeof(CharacterController))]
  11. #if ENABLE_INPUT_SYSTEM
  12. [RequireComponent(typeof(PlayerInput))]
  13. #endif
  14. public class FirstPersonController : MonoBehaviour
  15. {
  16.  
  17.  
  18. [Header("Player")]
  19. [Tooltip("Move speed of the character in m/s")]
  20. public float MoveSpeed = 4.0f;
  21. [Tooltip("Sprint speed of the character in m/s")]
  22. public float SprintSpeed = 6.0f;
  23. [Tooltip("Rotation speed of the character")]
  24. public float RotationSpeed = 1.0f;
  25. [Tooltip("Acceleration and deceleration")]
  26. public float SpeedChangeRate = 10.0f;
  27. private CollisionFlags collisionflags;
  28. public float currentHeight;
  29. public float jumpCooldown;
  30.  
  31. [Space(10)]
  32. [Tooltip("The height the player can jump")]
  33. public float JumpHeight = 0.6f;
  34. [Tooltip("The character uses its own gravity value. The engine default is -9.81f")]
  35. public float Gravity = -15.0f;
  36.  
  37. [Space(10)]
  38. [Tooltip("Time required to pass before being able to jump again. Set to 0f to instantly jump again")]
  39. public float JumpTimeout = 0.1f;
  40. [Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]
  41. public float FallTimeout = 0.15f;
  42.  
  43. [Header("Player Grounded")]
  44. [Tooltip("If the character is grounded or not. Not part of the CharacterController built in grounded check")]
  45. public bool Grounded = true;
  46. [Tooltip("Useful for rough ground")]
  47. public float GroundedOffset = -0.14f;
  48. [Tooltip("The radius of the grounded check. Should match the radius of the CharacterController")]
  49. public float GroundedRadius = 0.4f;
  50. [Tooltip("What layers the character uses as ground")]
  51. public LayerMask GroundLayers;
  52.  
  53. [Header("Cinemachine")]
  54. [Tooltip("The follow target set in the Cinemachine Virtual Camera that the camera will follow")]
  55. public GameObject CinemachineCameraTarget;
  56. [Tooltip("How far in degrees can you move the camera up")]
  57. public float TopClamp = 90.0f;
  58. [Tooltip("How far in degrees can you move the camera down")]
  59. public float BottomClamp = -90.0f;
  60.  
  61. [Header("Crouching")]
  62. [Tooltip("How long it takes to get into crouch")]
  63. public float crouchtime = 0.2f;
  64. [Tooltip("How fast you are when you crouch")]
  65. public float crouchspeed = 1.0f;
  66. public bool isCrouched;
  67.  
  68.  
  69. [Header("Proning")]
  70. [Tooltip("How long it takes to get into prone")]
  71. public float pronetime = 1.0f;
  72. [Tooltip("How fast you are when you're prone")]
  73. public float pronespeed = 0.5f;
  74. public bool isProne;
  75.  
  76. // cinemachine
  77. private float _cinemachineTargetPitch;
  78.  
  79. // player
  80. private float _speed;
  81. private Vector3 _storedVelocity;
  82. private float _rotationVelocity;
  83. private float _verticalVelocity;
  84. private float _terminalVelocity = 53.0f;
  85. private Vector3 _finalVelocity;
  86. private float _friction;
  87. private float _floorBounceThreshhold;
  88.  
  89.  
  90. private float airDrag = 2.0f;
  91.  
  92. // timeout deltatime
  93. private float _jumpTimeoutDelta;
  94. private float _fallTimeoutDelta;
  95.  
  96. public Vector3 positionlastframe;
  97. public Vector3 positiontwoframesago;
  98.  
  99. #if ENABLE_INPUT_SYSTEM
  100. private PlayerInput _playerInput;
  101. #endif
  102. private CharacterController _controller;
  103. private StarterAssetsInputs _input;
  104. private GameObject _mainCamera;
  105.  
  106. private float playerHeight;
  107. private Vector3 playerCenter;
  108. Vector3 checkPos;
  109.  
  110.  
  111. private float crouchHeight = 1f;
  112. private Vector3 crouchCenter = new Vector3(0f, 0.465f, 0f);
  113.  
  114. private const float _threshold = 0.01f;
  115.  
  116. LayerMask layerMask;
  117.  
  118. private bool IsCurrentDeviceMouse
  119. {
  120. get
  121. {
  122. #if ENABLE_INPUT_SYSTEM
  123. return _playerInput.currentControlScheme == "KeyboardMouse";
  124. #else
  125. return false;
  126. #endif
  127. }
  128. }
  129.  
  130. private void Awake()
  131. {
  132. // get a reference to our main camera
  133. if (_mainCamera == null)
  134. {
  135. _mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
  136. }
  137. }
  138.  
  139. private void Start()
  140. {
  141. layerMask = ~LayerMask.GetMask("Player");
  142.  
  143. _controller = GetComponent<CharacterController>();
  144. _input = GetComponent<StarterAssetsInputs>();
  145. #if ENABLE_INPUT_SYSTEM
  146. _playerInput = GetComponent<PlayerInput>();
  147. #else
  148. Debug.LogError( "Starter Assets package is missing dependencies. Please use Tools/Starter Assets/Reinstall Dependencies to fix it");
  149. #endif
  150. playerCenter = _controller.center;
  151. playerHeight = _controller.height;
  152. currentHeight = _controller.height;
  153. // reset our timeouts on start
  154. _jumpTimeoutDelta = JumpTimeout;
  155. _fallTimeoutDelta = FallTimeout;
  156. _friction = 10f; //TEMPORARY VALUE
  157. _floorBounceThreshhold = 5f; // ditto
  158. checkPos = transform.position + playerCenter + Vector3.up * (playerHeight * 0.5f - _controller.radius);
  159.  
  160. }
  161.  
  162. private void Update()
  163. {
  164.  
  165. if (!Input.GetKey(KeyCode.Space)) { _input.jump = false; }
  166. CrouchAndProne();
  167. _controller.height = currentHeight;
  168. GroundedCheck();
  169. JumpAndGravity();
  170. CalculateMovement();
  171. Move();
  172. checkPos = transform.position + playerCenter + Vector3.up * (playerHeight * 0.5f - _controller.radius);
  173. if (Input.GetKeyDown(KeyCode.V))
  174. {
  175. _storedVelocity = new Vector3(1f, -8f, -9f); //SOLELY FOR TESTING
  176. }
  177. // Debug.Log(_friction + "<-friction velocity.y->" + _verticalVelocity);
  178. }
  179.  
  180. private void LateUpdate()
  181. {
  182. CameraRotation();
  183. }
  184.  
  185. private void GroundedCheck()
  186. {
  187. // set sphere position, with offset
  188. Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z);
  189. Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers, QueryTriggerInteraction.Ignore);
  190. if (Grounded)
  191. {
  192.  
  193. _friction = 100f; //temp value
  194.  
  195. }
  196. else
  197. {
  198.  
  199. _friction = airDrag;
  200.  
  201.  
  202. }
  203. }
  204.  
  205. private void CameraRotation()
  206. {
  207. // if there is an input
  208. if (_input.look.sqrMagnitude >= _threshold)
  209. {
  210. //Don't multiply mouse input by Time.deltaTime
  211. float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1.0f : Time.deltaTime;
  212.  
  213. _cinemachineTargetPitch += _input.look.y * RotationSpeed * deltaTimeMultiplier;
  214. _rotationVelocity = _input.look.x * RotationSpeed * deltaTimeMultiplier;
  215.  
  216. // clamp our pitch rotation
  217. _cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp);
  218.  
  219. // Update Cinemachine camera target pitch
  220. CinemachineCameraTarget.transform.localRotation = Quaternion.Euler(_cinemachineTargetPitch, 0.0f, 0.0f);
  221.  
  222. // rotate the player left and right
  223. transform.Rotate(Vector3.up * _rotationVelocity);
  224. }
  225. }
  226.  
  227. private void CalculateMovement()
  228. {
  229. // set target speed based on move speed, sprint speed and if sprint is pressed
  230. float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;
  231. if (isCrouched || !(currentHeight == playerHeight))
  232. {
  233. targetSpeed = crouchspeed;
  234. }
  235.  
  236. // a simplistic acceleration and deceleration designed to be easy to remove, replace, or iterate upon
  237.  
  238. // note: Vector2's == operator uses approximation so is not floating point error prone, and is cheaper than magnitude
  239. // if there is no input, set the target speed to 0
  240. if (_input.move == Vector2.zero) targetSpeed = 0.0f;
  241.  
  242. // a reference to the players current horizontal velocity
  243. float currentHorizontalSpeed = _speed;
  244.  
  245. float speedOffset = 0.1f;
  246. float inputMagnitude = _input.analogMovement ? _input.move.magnitude : 1f;
  247.  
  248. // accelerate or decelerate to target speed
  249. if (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset)
  250. {
  251. // creates curved result rather than a linear one giving a more organic speed change
  252. // note T in Lerp is clamped, so we don't need to clamp our speed
  253. _speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude, Time.deltaTime * SpeedChangeRate);
  254.  
  255. // round speed to 3 decimal places
  256. _speed = Mathf.Round(_speed * 1000f) / 1000f;
  257. }
  258. else
  259. {
  260. _speed = targetSpeed;
  261. }
  262.  
  263. if (!Grounded)
  264. {
  265. _speed = 0f;
  266. }
  267.  
  268. // normalise input direction
  269. Vector3 inputDirection = new Vector3(_input.move.x, 0.0f, _input.move.y).normalized;
  270.  
  271. // note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
  272. // if there is a move input rotate player when the player is moving
  273. if (_input.move != Vector2.zero)
  274. {
  275. // move
  276. inputDirection = transform.right * _input.move.x + transform.forward * _input.move.y;
  277. }
  278.  
  279.  
  280. if (_storedVelocity.magnitude < 0.01f)
  281. {
  282. _finalVelocity = inputDirection.normalized * (_speed * Time.deltaTime);
  283. }
  284. else
  285. {
  286. Debug.Log("Friction is now: " + _friction);
  287. _storedVelocity = Vector3.MoveTowards(_storedVelocity, Vector3.zero, _friction * Time.deltaTime);
  288.  
  289.  
  290.  
  291. float playerAssistedDecay = Vector3.Dot(_storedVelocity.normalized, inputDirection.normalized);
  292.  
  293. float cancellation = Mathf.Max(0, -playerAssistedDecay);
  294.  
  295. _storedVelocity -= _storedVelocity.normalized * _speed * cancellation * Time.deltaTime;
  296.  
  297.  
  298. _finalVelocity = (inputDirection.normalized * _speed + _storedVelocity) * Time.deltaTime;
  299.  
  300.  
  301.  
  302. }
  303.  
  304. if (_input.jump && _jumpTimeoutDelta <= 0.0f && !isProne && !isCrouched && currentHeight == playerHeight && jumpCooldown <= 0f)
  305. {
  306. _friction = airDrag;
  307. _storedVelocity += inputDirection.normalized * _speed;
  308.  
  309. _finalVelocity = _storedVelocity * Time.deltaTime;
  310.  
  311.  
  312. _jumpTimeoutDelta = JumpTimeout;
  313. jumpCooldown = 1f;
  314.  
  315. }
  316.  
  317.  
  318.  
  319.  
  320.  
  321.  
  322.  
  323. }
  324.  
  325. private void Move()
  326. {
  327. // move the player
  328. collisionflags = _controller.Move(_finalVelocity + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime);
  329. }
  330.  
  331. private void JumpAndGravity()
  332. {
  333. if (!Input.GetKey(KeyCode.Space)) { _input.jump = false; }
  334.  
  335. if (jumpCooldown > 0f)
  336. {
  337.  
  338. jumpCooldown -= Time.deltaTime;
  339.  
  340. }
  341.  
  342. if (Grounded)
  343. {
  344. // reset the fall timeout timer
  345. _fallTimeoutDelta = FallTimeout;
  346.  
  347. // stop our velocity dropping infinitely when grounded
  348. if (_verticalVelocity < 0.0f)
  349. {
  350. _verticalVelocity = -2f;
  351. }
  352.  
  353. // Jump
  354. if (_input.jump && _jumpTimeoutDelta <= 0.0f && !isProne && !isCrouched && currentHeight == playerHeight && jumpCooldown <=0f)
  355. {
  356. // the square root of H * -2 * G = how much velocity needed to reach desired height
  357. _verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);
  358. _friction = airDrag;
  359. Debug.Log("Jumping! Friction is now: " + _friction);
  360.  
  361. }
  362.  
  363. // jump timeout
  364. if (_jumpTimeoutDelta >= 0.0f)
  365. {
  366. _jumpTimeoutDelta -= Time.deltaTime;
  367. }
  368. }
  369. else
  370. {
  371. // reset the jump timeout timer
  372. _jumpTimeoutDelta = JumpTimeout;
  373.  
  374. // fall timeout
  375. if (_fallTimeoutDelta >= 0.0f)
  376. {
  377. _fallTimeoutDelta -= Time.deltaTime;
  378. }
  379.  
  380. // if we are not grounded, do not jump
  381. _input.jump = false;
  382. }
  383.  
  384. // apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)
  385. if (_verticalVelocity < _terminalVelocity)
  386. {
  387. _verticalVelocity += Gravity * Time.deltaTime;
  388. }
  389.  
  390. if ((collisionflags & CollisionFlags.Above) != 0 && _verticalVelocity > 0) { _verticalVelocity = 0f; }//so hitting head stops vertical velocity instead of sitcking the palyer to the ceiling
  391. }
  392.  
  393. private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
  394. {
  395. if (lfAngle < -360f) lfAngle += 360f;
  396. if (lfAngle > 360f) lfAngle -= 360f;
  397. return Mathf.Clamp(lfAngle, lfMin, lfMax);
  398. }
  399.  
  400. private void OnDrawGizmos()
  401. {
  402.  
  403. if (_controller == null) { return; }
  404.  
  405. Color transparentGreen = new Color(0.0f, 1.0f, 0.0f, 0.35f);
  406. Color transparentRed = new Color(1.0f, 0.0f, 0.0f, 0.35f);
  407.  
  408. if (Grounded) Gizmos.color = transparentGreen;
  409. else Gizmos.color = transparentRed;
  410.  
  411. // when selected, draw a gizmo in the position of, and matching radius of, the grounded collider
  412. Gizmos.DrawSphere(new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z), GroundedRadius);
  413.  
  414. Gizmos.color = Color.blue;
  415.  
  416. checkPos = transform.position + playerCenter + Vector3.up * (playerHeight * 0.5f - _controller.radius);
  417.  
  418. Gizmos.DrawWireSphere(checkPos, _controller.radius);
  419.  
  420. if (!Application.isPlaying)
  421. return;
  422.  
  423. Gizmos.color = Color.yellow;
  424. Gizmos.DrawLine(transform.position, checkPos);
  425.  
  426. Gizmos.color = Color.red;
  427. Gizmos.DrawWireSphere(transform.position, 0.25f);
  428. }
  429.  
  430.  
  431. private void CrouchAndProne()
  432. {
  433. isCrouched = Input.GetKey(KeyCode.LeftControl);
  434. if (Input.GetKeyDown(KeyCode.LeftAlt))
  435. {
  436. isProne = !isProne;
  437. }
  438. if (isProne) { isCrouched = false; }
  439. if (isProne && _input.jump) { isProne = false; isCrouched = true; _jumpTimeoutDelta = 0.1f; _input.jump = false; }
  440.  
  441. if (isCrouched)
  442. {
  443.  
  444.  
  445. currentHeight = Mathf.MoveTowards(currentHeight, crouchHeight, ((playerHeight - crouchHeight) / crouchtime) * Time.deltaTime);
  446.  
  447. //_controller.center = Vector3.MoveTowards(_controller.center, crouchCenter, ((playerCenter.y - crouchCenter.y)/crouchtime) * Time.deltaTime); ts is not working
  448.  
  449. //Debug.Log($"Height: {currentHeight}");
  450. //Debug.Log($"Center: {_controller.center}");
  451.  
  452. }
  453. else if (!(currentHeight == playerHeight))
  454. {
  455.  
  456.  
  457.  
  458.  
  459. if (!Physics.CheckSphere(checkPos, _controller.radius, layerMask)) //replace with physics.checkcapsule()
  460. {
  461. currentHeight = Mathf.MoveTowards(currentHeight, playerHeight, ((playerHeight - crouchHeight) / crouchtime) * Time.deltaTime);
  462. }
  463. else
  464. {
  465. Debug.Log("Hithead");
  466. }
  467.  
  468. //_controller.center = Vector3.MoveTowards(_controller.center, playerCenter, ((playerCenter.y - crouchCenter.y) / crouchtime) * Time.deltaTime);
  469.  
  470. //Debug.Log($"Height: {currentHeight}");
  471. //Debug.Log($"Center: {_controller.center}");
  472.  
  473. }
  474.  
  475. }
  476.  
  477.  
  478.  
  479. private void OnControllerColliderHit(ControllerColliderHit hit)
  480. {
  481. float wallNormals = Vector3.Dot(_storedVelocity, hit.normal);
  482. float upDot = Vector3.Dot(hit.normal, Vector3.up);
  483. Vector3 initialstoredvelocity = _storedVelocity;
  484.  
  485. if (wallNormals < 0)
  486. {
  487. _storedVelocity -= hit.normal * wallNormals;
  488. _storedVelocity += hit.normal * initialstoredvelocity.magnitude * 0.1f;
  489. _storedVelocity.y = initialstoredvelocity.y;
  490. }
  491.  
  492. if (upDot > 0.7f || upDot < -0.7f)
  493. {
  494. if (Mathf.Abs(initialstoredvelocity.y) + Mathf.Abs(_verticalVelocity) < _floorBounceThreshhold)
  495. {
  496. _storedVelocity.y = 0f;
  497. }
  498. else
  499. {
  500. _storedVelocity.y = -0.4f * initialstoredvelocity.y;
  501. }
  502.  
  503. }
  504.  
  505.  
  506.  
  507. }
  508.  
  509. }
  510. }
Advertisement
Add Comment
Please, Sign In to add comment