using System.Collections; using System.Collections.Generic; using UnityEngine; using SimpleInventorySystem; public class FirstPersonController : MonoBehaviour { public InventorySystem inventorySystem; // Reference to the InventorySystem script // Flags for various character states and actions public bool CanMove { get; private set; } = true; private bool IsSprinting => canSprint && Input.GetKey(sprintKey); private bool ShouldJump => Input.GetKeyDown(jumpKey) && characterController.isGrounded; private bool ShouldCrouch => Input.GetKeyDown(crouchKey) && !duringCrouchAnimation && characterController.isGrounded; [Header("Functional Options")] [SerializeField] private bool canSprint = true; [SerializeField] private bool canJump = true; [SerializeField] private bool canCrouch = true; [SerializeField] private bool canUseHeadbob = true; [SerializeField] private bool canLean = true; [Header("Controls")] [SerializeField] private KeyCode sprintKey = KeyCode.LeftShift; [SerializeField] private KeyCode jumpKey = KeyCode.Space; [SerializeField] private KeyCode crouchKey = KeyCode.C; [Header("Movement Parameters")] [SerializeField] private float minWalkSpeed = 1.0f; [SerializeField] private float maxWalkSpeed = 5.0f; [SerializeField] private float walkSpeedInterval = 0.5f; private float currentWalkSpeed; [SerializeField] private float walkSpeed = 3.0f; [SerializeField] private float sprintSpeed = 6.0f; [SerializeField] private float crouchSpeed = 1.5f; [Header("Look Parameters")] [SerializeField, Range(1, 10)] private float lookSpeedX = 2.0f; [SerializeField, Range(1, 10)] private float lookSpeedY = 2.0f; [SerializeField, Range(1, 180)] private float upperLookLimit = 80.0f; [SerializeField, Range(1, 180)] private float lowerLookLimit = 80.0f; [Header("Leaning Parameters")] [SerializeField] private bool toggleLeaning = false; // Toggle mode [SerializeField] private KeyCode leanLeftKey = KeyCode.Q; [SerializeField] private KeyCode leanRightKey = KeyCode.E; [SerializeField, Range(1, 45)] private float maxLeaningAngle = 15.0f; private float currentLeaningAngle; private bool isLeaningLeft; private bool isLeaningRight; [Header("Jumping Parameters")] [SerializeField] private float jumpForce = 8.0f; [SerializeField] private float gravity = 30.0f; [Header("Crouch Parameters")] [SerializeField] private float crouchHeightInterval = 0.1f; [SerializeField] private float crouchHeight = 0.5f; [SerializeField] private float standingHeight = 2.0f; [SerializeField] private float timeToCrouch = 0.25f; [SerializeField] private Vector3 crouchingCenter = new Vector3(0, 0.5f, 0); [SerializeField] private Vector3 standingCenter = new Vector3(0, 0, 0); private bool isCrouching; private bool duringCrouchAnimation; private float targetCrouchHeight; // The target crouch height to apply when "C" key is released private float tempCrouchHeight; // The temporary crouch height while "C" key is held private float currentCrouchHeight; private float minCrouchHeight = 0.5f; // Minimum crouch height private bool isCrouchKeyHeld; [Header("Headbob Parameters")] [SerializeField] private float walkBobSpeed = 14f; [SerializeField] private float walkBobAmount = 0.05f; [SerializeField] private float sprintBobSpeed = 18f; [SerializeField] private float sprintBobAmount = 0.1f; [SerializeField] private float crouchBobSpeed = 8f; [SerializeField] private float crouchBobAmount = 0.025f; private float defaultYPos = 0; private float timer; private Camera playerCamera; private CharacterController characterController; private Vector3 moveDirection; private Vector2 currentInput; private float rotationX = 0; void Awake() { playerCamera = GetComponentInChildren(); characterController = GetComponent(); defaultYPos = playerCamera.transform.localPosition.y; Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; currentWalkSpeed = walkSpeed; currentCrouchHeight = crouchHeight; } void Update() { bool isInventoryUIOpen = inventorySystem.IsUsingInventoryMenuUI(); if (isInventoryUIOpen) { CanMove = false; // Disable movement when inventory UI is open } else { CanMove = true; // Enable movement HandleMovementInput(); HandleMouseLook(); HandleSpeedChange(); HandleCrouchAdjustment(); if (canJump) HandleJump(); if (canCrouch) HandleCrouch(); if (canUseHeadbob) HandleHeadbob(); if (canLean) HandleLeaning(); // Process leaning logic ApplyFinalMovements(); } } private void HandleMovementInput() { // Determine the current speed based on the character's state (crouching, sprinting, etc.) currentInput = new Vector2((isCrouching ? crouchSpeed : IsSprinting ? sprintSpeed : walkSpeed) * Input.GetAxis("Vertical"), (isCrouching ? crouchSpeed : IsSprinting ? sprintSpeed : walkSpeed) * Input.GetAxis("Horizontal")); float moveDirectionY = moveDirection.y; moveDirection = (transform.TransformDirection(Vector3.forward) * currentInput.x) + (transform.TransformDirection(Vector3.right) * currentInput.y); moveDirection.y = moveDirectionY; } private void HandleJump() { if (ShouldJump) moveDirection.y = jumpForce; // Apply jump force } private void HandleCrouch() { if (Input.GetKeyDown(crouchKey) && !duringCrouchAnimation && characterController.isGrounded) { isCrouchKeyHeld = true; tempCrouchHeight = currentCrouchHeight; } if (Input.GetKeyUp(crouchKey) && isCrouchKeyHeld) { // Apply the new crouch height when "C" key is released targetCrouchHeight = tempCrouchHeight; isCrouchKeyHeld = false; StartCoroutine(CrouchStand()); } } private void HandleCrouchAdjustment() { // Adjust crouch height based on mouse scroll input float scroll = Input.GetAxis("Mouse ScrollWheel"); if (scroll > 0 && tempCrouchHeight < standingHeight) { tempCrouchHeight += crouchHeightInterval; } else if (scroll < 0 && tempCrouchHeight > minCrouchHeight) { tempCrouchHeight -= crouchHeightInterval; } crouchHeight = tempCrouchHeight; // Update crouch height } private IEnumerator CrouchStand() { if (isCrouching && Physics.Raycast(playerCamera.transform.position, Vector3.up, 1f)) yield break; duringCrouchAnimation = true; float timeElapsed = 0; float targetHeight = isCrouching ? standingHeight : currentCrouchHeight; float currentHeight = characterController.height; Vector3 targetCenter = isCrouching ? standingCenter : crouchingCenter; Vector3 currentCenter = characterController.center; // Smooth transition between crouch and stand while (timeElapsed < timeToCrouch) { characterController.height = Mathf.Lerp(currentHeight, targetHeight, timeElapsed / timeToCrouch); characterController.center = Vector3.Lerp(currentCenter, targetCenter, timeElapsed / timeToCrouch); timeElapsed += Time.deltaTime; yield return null; } characterController.height = targetHeight; characterController.center = targetCenter; isCrouching = !isCrouching; duringCrouchAnimation = false; crouchHeight = currentCrouchHeight; // Update to current crouch height } private void HandleHeadbob() { if (!characterController.isGrounded) return; // Apply head bobbing effect based on movement speed and type if (Mathf.Abs(moveDirection.x) > 0.1f || Mathf.Abs(moveDirection.z) > 0.1f) { timer += Time.deltaTime * (isCrouching ? crouchBobSpeed : IsSprinting ? sprintBobSpeed : walkBobSpeed); playerCamera.transform.localPosition = new Vector3( playerCamera.transform.localPosition.x, defaultYPos + Mathf.Sin(timer) * (isCrouching ? crouchBobAmount : IsSprinting ? sprintBobAmount : walkBobAmount), playerCamera.transform.localPosition.z); } } private void HandleMouseLook() { rotationX -= Input.GetAxis("Mouse Y") * lookSpeedY; rotationX = Mathf.Clamp(rotationX, -upperLookLimit, lowerLookLimit); // Apply current leaning angle to camera's rotation playerCamera.transform.localRotation = Quaternion.Euler(rotationX + currentLeaningAngle, 0, 0); transform.rotation *= Quaternion.Euler(0, Input.GetAxis("Mouse X") * lookSpeedX, 0); } private void HandleLeaning() { if (toggleLeaning) { // Toggle leaning with a single key press if (Input.GetKeyDown(leanLeftKey)) { isLeaningLeft = !isLeaningLeft; isLeaningRight = false; } if (Input.GetKeyDown(leanRightKey)) { isLeaningRight = !isLeaningRight; isLeaningLeft = false; } } else { // Continuous leaning while the keys are held down if (Input.GetKey(leanLeftKey) && !isLeaningRight) { isLeaningLeft = true; isLeaningRight = false; } if (Input.GetKey(leanRightKey) && !isLeaningLeft) { isLeaningRight = true; isLeaningLeft = false; } } // Calculate and apply the current leaning angle currentLeaningAngle = isLeaningLeft ? -maxLeaningAngle : isLeaningRight ? maxLeaningAngle : 0; } private void ApplyFinalMovements() { if (!characterController.isGrounded) moveDirection.y -= gravity * Time.deltaTime; characterController.Move(moveDirection * Time.deltaTime); } private void HandleSpeedChange() { float scroll = Input.GetAxis("Mouse ScrollWheel"); // Adjust walking speed based on mouse scroll input if (scroll > 0 && currentWalkSpeed < maxWalkSpeed) { currentWalkSpeed += walkSpeedInterval; } else if (scroll < 0 && currentWalkSpeed > minWalkSpeed) { currentWalkSpeed -= walkSpeedInterval; } walkSpeed = currentWalkSpeed; // Update walk speed } }