using UnityEngine; #if ENABLE_INPUT_SYSTEM using UnityEngine.InputSystem; #endif namespace StarterAssets { [RequireComponent(typeof(CharacterController))] #if ENABLE_INPUT_SYSTEM [RequireComponent(typeof(PlayerInput))] #endif public class FirstPersonController : MonoBehaviour { [Header("Player")] public float StandingSpeed = 4.0f; private float MoveSpeed = 4.0f; public float SprintSpeed = 6.0f; public float RotationSpeed = 1.0f; public float SpeedChangeRate = 10.0f; [Space(10)] public float JumpHeight = 1.2f; public float Gravity = -15.0f; [Space(10)] public float JumpTimeout = 0.1f; public float FallTimeout = 0.15f; [Header("Posture Heights")] [Tooltip("Height when standing")] public float StandingHeight = 1.8f; [Tooltip("Height when crouching")] public float CrouchHeight = 1.2f; [Tooltip("Height when crawling")] public float CrawlHeight = 0.8f; [Header("Player Grounded")] public bool Grounded = true; public float GroundedOffset = -0.14f; public float GroundedRadius = 0.5f; public LayerMask GroundLayers; [Header("Cinemachine")] public GameObject CinemachineCameraTarget; public float TopClamp = 90.0f; public float BottomClamp = -90.0f; private float _cinemachineTargetPitch; private float _speed; private float _rotationVelocity; private float _verticalVelocity; private float _terminalVelocity = 53.0f; private float _jumpTimeoutDelta; private float _fallTimeoutDelta; #if ENABLE_INPUT_SYSTEM private PlayerInput _playerInput; #endif private CharacterController _controller; private StarterAssetsInputs _input; private GameObject _mainCamera; private const float _threshold = 0.01f; // Posture state private enum PlayerPosture { Standing, Crouching, Crawling } private PlayerPosture _currentPosture = PlayerPosture.Standing; private PlayerPosture _OldPosture = PlayerPosture.Standing; private bool IsCurrentDeviceMouse { get { #if ENABLE_INPUT_SYSTEM return _playerInput.currentControlScheme == "KeyboardMouse"; #else return false; #endif } } private void Awake() { if (_mainCamera == null) { _mainCamera = GameObject.FindGameObjectWithTag("MainCamera"); } } private void Start() { MoveSpeed = StandingSpeed; _controller = GetComponent(); _input = GetComponent(); #if ENABLE_INPUT_SYSTEM _playerInput = GetComponent(); #else Debug.LogError("Starter Assets package is missing dependencies."); #endif _jumpTimeoutDelta = JumpTimeout; _fallTimeoutDelta = FallTimeout; // Initialise posture SetPosture(PlayerPosture.Standing); } private void Update() { JumpAndGravity(); GroundedCheck(); HandlePosture(); Move(); } private void LateUpdate() { CameraRotation(); } private void GroundedCheck() { Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z); Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers, QueryTriggerInteraction.Ignore); } private void CameraRotation() { if (_input.look.sqrMagnitude >= _threshold) { float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1.0f : Time.deltaTime; _cinemachineTargetPitch += _input.look.y * RotationSpeed * deltaTimeMultiplier; _rotationVelocity = _input.look.x * RotationSpeed * deltaTimeMultiplier; _cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp); CinemachineCameraTarget.transform.localRotation = Quaternion.Euler(_cinemachineTargetPitch, 0.0f, 0.0f); transform.Rotate(Vector3.up * _rotationVelocity); } } private void Move() { float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed; if (_input.move == Vector2.zero) targetSpeed = 0.0f; float currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude; float speedOffset = 0.1f; float inputMagnitude = _input.analogMovement ? _input.move.magnitude : 1f; if (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset) { _speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude, Time.deltaTime * SpeedChangeRate); _speed = Mathf.Round(_speed * 1000f) / 1000f; } else { _speed = targetSpeed; } Vector3 inputDirection = new Vector3(_input.move.x, 0.0f, _input.move.y).normalized; if (_input.move != Vector2.zero) { inputDirection = transform.right * _input.move.x + transform.forward * _input.move.y; } _controller.Move(inputDirection.normalized * (_speed * Time.deltaTime) + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime); } private void JumpAndGravity() { if (Grounded) { _fallTimeoutDelta = FallTimeout; if (_verticalVelocity < 0.0f) { _verticalVelocity = -2f; } if (_input.jump && _jumpTimeoutDelta <= 0.0f) { _verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity); } if (_jumpTimeoutDelta >= 0.0f) { _jumpTimeoutDelta -= Time.deltaTime; } } else { _jumpTimeoutDelta = JumpTimeout; if (_fallTimeoutDelta >= 0.0f) { _fallTimeoutDelta -= Time.deltaTime; } _input.jump = false; } if (_verticalVelocity < _terminalVelocity) { _verticalVelocity += Gravity * Time.deltaTime; } } private void HandlePosture() { // Appui sur 'C' : Passage entre Crouch et Standing if (_input.crouch) { // Debug.Log("_input.crouch : " + _input.crouch); _input.crouch = false; if (_currentPosture == PlayerPosture.Crouching) { // Vérification de la hauteur du plafond avant de se redresser if (CheckCeilingHeight() >= StandingHeight) { SetPosture(PlayerPosture.Standing); } else { Debug.Log("Pas assez de hauteur pour se lever."); } } else if (_currentPosture == PlayerPosture.Standing || _currentPosture == PlayerPosture.Crawling ) { if (CheckCeilingHeight() >= CrouchHeight) { SetPosture(PlayerPosture.Crouching); } else { Debug.Log("Pas assez de hauteur pour se baisser."); } } } // Appui sur 'V' : Passage entre Crawl et Standing if (_input.crawl) { // Debug.Log("_input.crawl : " + _input.crawl); _input.crawl = false; if (_currentPosture == PlayerPosture.Crawling) { // Vérification de la hauteur du plafond avant de se redresser if (CheckCeilingHeight() >= StandingHeight) { SetPosture(PlayerPosture.Standing); } else { Debug.Log("Pas assez de hauteur pour se lever."); if (CheckCeilingHeight() >= CrouchHeight) { SetPosture(PlayerPosture.Crouching); } else { Debug.Log("Pas assez de hauteur pour se baisser."); } } } else if (_currentPosture == PlayerPosture.Standing) { SetPosture(PlayerPosture.Crawling); } else if (_currentPosture == PlayerPosture.Crouching) { SetPosture(PlayerPosture.Crawling); } } } private float CheckCeilingHeight() { // Calculer la position de la tête, qui est à la hauteur maximale de la capsule Vector3 headPosition = transform.position + Vector3.up * (_controller.height / 2); // Effectuer un rayon vers le haut à partir de la position de la tête RaycastHit hit; if (Physics.Raycast(headPosition, Vector3.up, out hit, 2f)) { // Si le rayon touche un objet, on retourne la distance entre la tête et le plafond Debug.Log ("hit.distance : " + hit.distance + ", headPosition : " + headPosition); return 10.0f; // return hit.distance; } return 10.0f; // return Mathf.Infinity; // Si rien n'est touché, on considère qu'il n'y a pas de plafond } private void SetPosture(PlayerPosture posture) { float h=0.0f; _OldPosture = _currentPosture; _currentPosture = posture; if ( _OldPosture == PlayerPosture.Standing) h = StandingHeight; else if ( _OldPosture == PlayerPosture.Crouching) h = CrouchHeight; else if ( _OldPosture == PlayerPosture.Crawling) h = CrawlHeight; Debug.Log ("_OldPosture : " + _OldPosture + ", _currentPosture : " + _currentPosture); switch (posture) { case PlayerPosture.Standing: _controller.height = StandingHeight; _controller.center = new Vector3(0.0f, StandingHeight / 2.0f, 0.0f); MoveSpeed = StandingSpeed; CinemachineCameraTarget.transform.position = new Vector3( CinemachineCameraTarget.transform.position.x, CinemachineCameraTarget.transform.position.y + StandingHeight - h, CinemachineCameraTarget.transform.position.z ); break; case PlayerPosture.Crouching: _controller.height = CrouchHeight; _controller.center = new Vector3(0.0f, CrouchHeight / 2.0f, 0.0f); MoveSpeed = StandingSpeed*0.6f; CinemachineCameraTarget.transform.position = new Vector3( CinemachineCameraTarget.transform.position.x, CinemachineCameraTarget.transform.position.y + CrouchHeight - h, CinemachineCameraTarget.transform.position.z ); break; case PlayerPosture.Crawling: _controller.height = CrawlHeight; _controller.center = new Vector3(0.0f, CrawlHeight / 2.0f, 0.0f); MoveSpeed = StandingSpeed*0.4f; CinemachineCameraTarget.transform.position = new Vector3( CinemachineCameraTarget.transform.position.x, CinemachineCameraTarget.transform.position.y + CrawlHeight - h, CinemachineCameraTarget.transform.position.z ); break; } } 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 OnDrawGizmosSelected() { Color transparentGreen = new Color(0.0f, 1.0f, 0.0f, 0.35f); Color transparentRed = new Color(1.0f, 0.0f, 0.0f, 0.35f); Gizmos.color = Grounded ? transparentGreen : transparentRed; Gizmos.DrawSphere(new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z), GroundedRadius); } } }