Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using UnityEngine;
- #if ENABLE_INPUT_SYSTEM
- using UnityEngine.InputSystem;
- using UnityEngine.UIElements;
- using Unity.Cinemachine;
- #endif
- namespace StarterAssets
- {
- [RequireComponent(typeof(CharacterController))]
- #if ENABLE_INPUT_SYSTEM
- [RequireComponent(typeof(PlayerInput))]
- #endif
- public class FirstPersonController : MonoBehaviour
- {
- [Header("Player")]
- [Tooltip("Move speed of the character in m/s")]
- public float MoveSpeed = 4.0f;
- [Tooltip("Sprint speed of the character in m/s")]
- public float SprintSpeed = 6.0f;
- [Tooltip("Rotation speed of the character")]
- public float RotationSpeed = 1.0f;
- [Tooltip("Acceleration and deceleration")]
- public float SpeedChangeRate = 10.0f;
- private CollisionFlags collisionflags;
- public float currentHeight;
- public float jumpCooldown;
- [Space(10)]
- [Tooltip("The height the player can jump")]
- public float JumpHeight = 0.6f;
- [Tooltip("The character uses its own gravity value. The engine default is -9.81f")]
- public float Gravity = -15.0f;
- [Space(10)]
- [Tooltip("Time required to pass before being able to jump again. Set to 0f to instantly jump again")]
- public float JumpTimeout = 0.1f;
- [Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]
- public float FallTimeout = 0.15f;
- [Header("Player Grounded")]
- [Tooltip("If the character is grounded or not. Not part of the CharacterController built in grounded check")]
- public bool Grounded = true;
- [Tooltip("Useful for rough ground")]
- public float GroundedOffset = -0.14f;
- [Tooltip("The radius of the grounded check. Should match the radius of the CharacterController")]
- public float GroundedRadius = 0.4f;
- [Tooltip("What layers the character uses as ground")]
- public LayerMask GroundLayers;
- [Header("Cinemachine")]
- [Tooltip("The follow target set in the Cinemachine Virtual Camera that the camera will follow")]
- public GameObject CinemachineCameraTarget;
- [Tooltip("How far in degrees can you move the camera up")]
- public float TopClamp = 90.0f;
- [Tooltip("How far in degrees can you move the camera down")]
- public float BottomClamp = -90.0f;
- [Header("Crouching")]
- [Tooltip("How long it takes to get into crouch")]
- public float crouchtime = 0.2f;
- [Tooltip("How fast you are when you crouch")]
- public float crouchspeed = 1.0f;
- public bool isCrouched;
- [Header("Proning")]
- [Tooltip("How long it takes to get into prone")]
- public float pronetime = 1.0f;
- [Tooltip("How fast you are when you're prone")]
- public float pronespeed = 0.5f;
- public bool isProne;
- // cinemachine
- private float _cinemachineTargetPitch;
- // player
- private float _speed;
- private Vector3 _storedVelocity;
- private float _rotationVelocity;
- private float _verticalVelocity;
- private float _terminalVelocity = 53.0f;
- private Vector3 _finalVelocity;
- private float _friction;
- private float _floorBounceThreshhold;
- private float airDrag = 2.0f;
- // timeout deltatime
- private float _jumpTimeoutDelta;
- private float _fallTimeoutDelta;
- public Vector3 positionlastframe;
- public Vector3 positiontwoframesago;
- #if ENABLE_INPUT_SYSTEM
- private PlayerInput _playerInput;
- #endif
- private CharacterController _controller;
- private StarterAssetsInputs _input;
- private GameObject _mainCamera;
- private float playerHeight;
- private Vector3 playerCenter;
- Vector3 checkPos;
- private float crouchHeight = 1f;
- private Vector3 crouchCenter = new Vector3(0f, 0.465f, 0f);
- private const float _threshold = 0.01f;
- LayerMask layerMask;
- private bool IsCurrentDeviceMouse
- {
- get
- {
- #if ENABLE_INPUT_SYSTEM
- return _playerInput.currentControlScheme == "KeyboardMouse";
- #else
- return false;
- #endif
- }
- }
- private void Awake()
- {
- // get a reference to our main camera
- if (_mainCamera == null)
- {
- _mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
- }
- }
- private void Start()
- {
- layerMask = ~LayerMask.GetMask("Player");
- _controller = GetComponent<CharacterController>();
- _input = GetComponent<StarterAssetsInputs>();
- #if ENABLE_INPUT_SYSTEM
- _playerInput = GetComponent<PlayerInput>();
- #else
- Debug.LogError( "Starter Assets package is missing dependencies. Please use Tools/Starter Assets/Reinstall Dependencies to fix it");
- #endif
- playerCenter = _controller.center;
- playerHeight = _controller.height;
- currentHeight = _controller.height;
- // reset our timeouts on start
- _jumpTimeoutDelta = JumpTimeout;
- _fallTimeoutDelta = FallTimeout;
- _friction = 10f; //TEMPORARY VALUE
- _floorBounceThreshhold = 5f; // ditto
- checkPos = transform.position + playerCenter + Vector3.up * (playerHeight * 0.5f - _controller.radius);
- }
- private void Update()
- {
- if (!Input.GetKey(KeyCode.Space)) { _input.jump = false; }
- CrouchAndProne();
- _controller.height = currentHeight;
- GroundedCheck();
- JumpAndGravity();
- CalculateMovement();
- Move();
- checkPos = transform.position + playerCenter + Vector3.up * (playerHeight * 0.5f - _controller.radius);
- if (Input.GetKeyDown(KeyCode.V))
- {
- _storedVelocity = new Vector3(1f, -8f, -9f); //SOLELY FOR TESTING
- }
- // Debug.Log(_friction + "<-friction velocity.y->" + _verticalVelocity);
- }
- private void LateUpdate()
- {
- CameraRotation();
- }
- private void GroundedCheck()
- {
- // set sphere position, with offset
- Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z);
- Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers, QueryTriggerInteraction.Ignore);
- if (Grounded)
- {
- _friction = 100f; //temp value
- }
- else
- {
- _friction = airDrag;
- }
- }
- private void CameraRotation()
- {
- // if there is an input
- if (_input.look.sqrMagnitude >= _threshold)
- {
- //Don't multiply mouse input by Time.deltaTime
- float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1.0f : Time.deltaTime;
- _cinemachineTargetPitch += _input.look.y * RotationSpeed * deltaTimeMultiplier;
- _rotationVelocity = _input.look.x * RotationSpeed * deltaTimeMultiplier;
- // clamp our pitch rotation
- _cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp);
- // Update Cinemachine camera target pitch
- CinemachineCameraTarget.transform.localRotation = Quaternion.Euler(_cinemachineTargetPitch, 0.0f, 0.0f);
- // rotate the player left and right
- transform.Rotate(Vector3.up * _rotationVelocity);
- }
- }
- private void CalculateMovement()
- {
- // set target speed based on move speed, sprint speed and if sprint is pressed
- float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;
- if (isCrouched || !(currentHeight == playerHeight))
- {
- targetSpeed = crouchspeed;
- }
- // a simplistic acceleration and deceleration designed to be easy to remove, replace, or iterate upon
- // note: Vector2's == operator uses approximation so is not floating point error prone, and is cheaper than magnitude
- // if there is no input, set the target speed to 0
- if (_input.move == Vector2.zero) targetSpeed = 0.0f;
- // a reference to the players current horizontal velocity
- float currentHorizontalSpeed = _speed;
- float speedOffset = 0.1f;
- float inputMagnitude = _input.analogMovement ? _input.move.magnitude : 1f;
- // accelerate or decelerate to target speed
- if (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset)
- {
- // creates curved result rather than a linear one giving a more organic speed change
- // note T in Lerp is clamped, so we don't need to clamp our speed
- _speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude, Time.deltaTime * SpeedChangeRate);
- // round speed to 3 decimal places
- _speed = Mathf.Round(_speed * 1000f) / 1000f;
- }
- else
- {
- _speed = targetSpeed;
- }
- if (!Grounded)
- {
- _speed = 0f;
- }
- // normalise input direction
- Vector3 inputDirection = new Vector3(_input.move.x, 0.0f, _input.move.y).normalized;
- // note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
- // if there is a move input rotate player when the player is moving
- if (_input.move != Vector2.zero)
- {
- // move
- inputDirection = transform.right * _input.move.x + transform.forward * _input.move.y;
- }
- if (_storedVelocity.magnitude < 0.01f)
- {
- _finalVelocity = inputDirection.normalized * (_speed * Time.deltaTime);
- }
- else
- {
- Debug.Log("Friction is now: " + _friction);
- _storedVelocity = Vector3.MoveTowards(_storedVelocity, Vector3.zero, _friction * Time.deltaTime);
- float playerAssistedDecay = Vector3.Dot(_storedVelocity.normalized, inputDirection.normalized);
- float cancellation = Mathf.Max(0, -playerAssistedDecay);
- _storedVelocity -= _storedVelocity.normalized * _speed * cancellation * Time.deltaTime;
- _finalVelocity = (inputDirection.normalized * _speed + _storedVelocity) * Time.deltaTime;
- }
- if (_input.jump && _jumpTimeoutDelta <= 0.0f && !isProne && !isCrouched && currentHeight == playerHeight && jumpCooldown <= 0f)
- {
- _friction = airDrag;
- _storedVelocity += inputDirection.normalized * _speed;
- _finalVelocity = _storedVelocity * Time.deltaTime;
- _jumpTimeoutDelta = JumpTimeout;
- jumpCooldown = 1f;
- }
- }
- private void Move()
- {
- // move the player
- collisionflags = _controller.Move(_finalVelocity + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime);
- }
- private void JumpAndGravity()
- {
- if (!Input.GetKey(KeyCode.Space)) { _input.jump = false; }
- if (jumpCooldown > 0f)
- {
- jumpCooldown -= Time.deltaTime;
- }
- if (Grounded)
- {
- // reset the fall timeout timer
- _fallTimeoutDelta = FallTimeout;
- // stop our velocity dropping infinitely when grounded
- if (_verticalVelocity < 0.0f)
- {
- _verticalVelocity = -2f;
- }
- // Jump
- if (_input.jump && _jumpTimeoutDelta <= 0.0f && !isProne && !isCrouched && currentHeight == playerHeight && jumpCooldown <=0f)
- {
- // the square root of H * -2 * G = how much velocity needed to reach desired height
- _verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);
- _friction = airDrag;
- Debug.Log("Jumping! Friction is now: " + _friction);
- }
- // jump timeout
- if (_jumpTimeoutDelta >= 0.0f)
- {
- _jumpTimeoutDelta -= Time.deltaTime;
- }
- }
- else
- {
- // reset the jump timeout timer
- _jumpTimeoutDelta = JumpTimeout;
- // fall timeout
- if (_fallTimeoutDelta >= 0.0f)
- {
- _fallTimeoutDelta -= Time.deltaTime;
- }
- // if we are not grounded, do not jump
- _input.jump = false;
- }
- // apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)
- if (_verticalVelocity < _terminalVelocity)
- {
- _verticalVelocity += Gravity * Time.deltaTime;
- }
- if ((collisionflags & CollisionFlags.Above) != 0 && _verticalVelocity > 0) { _verticalVelocity = 0f; }//so hitting head stops vertical velocity instead of sitcking the palyer to the ceiling
- }
- private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
- {
- if (lfAngle < -360f) lfAngle += 360f;
- if (lfAngle > 360f) lfAngle -= 360f;
- return Mathf.Clamp(lfAngle, lfMin, lfMax);
- }
- private void OnDrawGizmos()
- {
- if (_controller == null) { return; }
- Color transparentGreen = new Color(0.0f, 1.0f, 0.0f, 0.35f);
- Color transparentRed = new Color(1.0f, 0.0f, 0.0f, 0.35f);
- if (Grounded) Gizmos.color = transparentGreen;
- else Gizmos.color = transparentRed;
- // when selected, draw a gizmo in the position of, and matching radius of, the grounded collider
- Gizmos.DrawSphere(new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z), GroundedRadius);
- Gizmos.color = Color.blue;
- checkPos = transform.position + playerCenter + Vector3.up * (playerHeight * 0.5f - _controller.radius);
- Gizmos.DrawWireSphere(checkPos, _controller.radius);
- if (!Application.isPlaying)
- return;
- Gizmos.color = Color.yellow;
- Gizmos.DrawLine(transform.position, checkPos);
- Gizmos.color = Color.red;
- Gizmos.DrawWireSphere(transform.position, 0.25f);
- }
- private void CrouchAndProne()
- {
- isCrouched = Input.GetKey(KeyCode.LeftControl);
- if (Input.GetKeyDown(KeyCode.LeftAlt))
- {
- isProne = !isProne;
- }
- if (isProne) { isCrouched = false; }
- if (isProne && _input.jump) { isProne = false; isCrouched = true; _jumpTimeoutDelta = 0.1f; _input.jump = false; }
- if (isCrouched)
- {
- currentHeight = Mathf.MoveTowards(currentHeight, crouchHeight, ((playerHeight - crouchHeight) / crouchtime) * Time.deltaTime);
- //_controller.center = Vector3.MoveTowards(_controller.center, crouchCenter, ((playerCenter.y - crouchCenter.y)/crouchtime) * Time.deltaTime); ts is not working
- //Debug.Log($"Height: {currentHeight}");
- //Debug.Log($"Center: {_controller.center}");
- }
- else if (!(currentHeight == playerHeight))
- {
- if (!Physics.CheckSphere(checkPos, _controller.radius, layerMask)) //replace with physics.checkcapsule()
- {
- currentHeight = Mathf.MoveTowards(currentHeight, playerHeight, ((playerHeight - crouchHeight) / crouchtime) * Time.deltaTime);
- }
- else
- {
- Debug.Log("Hithead");
- }
- //_controller.center = Vector3.MoveTowards(_controller.center, playerCenter, ((playerCenter.y - crouchCenter.y) / crouchtime) * Time.deltaTime);
- //Debug.Log($"Height: {currentHeight}");
- //Debug.Log($"Center: {_controller.center}");
- }
- }
- private void OnControllerColliderHit(ControllerColliderHit hit)
- {
- float wallNormals = Vector3.Dot(_storedVelocity, hit.normal);
- float upDot = Vector3.Dot(hit.normal, Vector3.up);
- Vector3 initialstoredvelocity = _storedVelocity;
- if (wallNormals < 0)
- {
- _storedVelocity -= hit.normal * wallNormals;
- _storedVelocity += hit.normal * initialstoredvelocity.magnitude * 0.1f;
- _storedVelocity.y = initialstoredvelocity.y;
- }
- if (upDot > 0.7f || upDot < -0.7f)
- {
- if (Mathf.Abs(initialstoredvelocity.y) + Mathf.Abs(_verticalVelocity) < _floorBounceThreshhold)
- {
- _storedVelocity.y = 0f;
- }
- else
- {
- _storedVelocity.y = -0.4f * initialstoredvelocity.y;
- }
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment