382 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			382 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System.Collections;
 | |
| using System.Collections.Generic;
 | |
| using UnityEngine;
 | |
| 
 | |
| namespace Polyart {
 | |
|     public class FirstPersonController_Polyart : MonoBehaviour
 | |
|     {
 | |
|         public static FirstPersonController_Polyart instance;
 | |
|         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) && !duringCrouchAnim && characterController.isGrounded;
 | |
| 
 | |
|         [Header("Character Options")]
 | |
|         [SerializeField] private bool canSprint = true;
 | |
|         [SerializeField] private bool canJump = true;
 | |
|         [SerializeField] private bool canCrouch = true;
 | |
|         [SerializeField] private bool canHeadBob = true;
 | |
|         [SerializeField] private bool canInteract = true;
 | |
|         [SerializeField] private bool useFootsteps = true;
 | |
| 
 | |
|         [Header("Controls")]
 | |
|         [SerializeField] private KeyCode sprintKey = KeyCode.LeftShift;
 | |
|         [SerializeField] private KeyCode jumpKey = KeyCode.Space;
 | |
|         [SerializeField] private KeyCode crouchKey = KeyCode.LeftControl;
 | |
|         
 | |
|       //  [SerializeField] private KeyCode interactKey = KeyCode.E;
 | |
|         /* Uncomment if you want to use interaction with Player
 | |
|         [Header("Interaction")]
 | |
|         [SerializeField] private Vector3 interactionRayPoint = default;
 | |
|         [SerializeField] private float interactionDistance = default;
 | |
|         [SerializeField] private LayerMask interactionLayer = default;
 | |
|         private Interactable_Dreamscape currentInteractable;
 | |
|         */
 | |
|         [Header("Movement Parameters")]
 | |
|         [SerializeField] private float walkSpeed = 3.0f;
 | |
|         [SerializeField] private float sprintSpeed = 6.0f;
 | |
|         [SerializeField] private float crouchSpeed = 1.5f;
 | |
| 
 | |
|         [Header("Camera 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 lowerLookLimit = 80.0f;
 | |
|         [SerializeField, Range(1, 180)] private float upperLookLimit = 80.0f;
 | |
|         
 | |
|         // Runtime sensitivity values (loaded from settings)
 | |
|         private float currentLookSpeedX = 2.0f;
 | |
|         private float currentLookSpeedY = 2.0f;
 | |
|         private bool mouseInverted = false;
 | |
| 
 | |
|         [Header("Jumping Parameters")]
 | |
|         [SerializeField] private float jumpForce = 8.0f;
 | |
|         [SerializeField] private float gravity = 30.0f;
 | |
| 
 | |
|         [Header("Crouching Parameters")]
 | |
| 
 | |
|         [SerializeField] private float crouchHeight = 0.5f;
 | |
|         [SerializeField] private float standinghHeight = 2f;
 | |
|         [SerializeField] private float timeToCrouch = 0.25f;
 | |
|         [SerializeField] private Vector3 crouchCenter = new Vector3(0, 0.5f, 0);
 | |
|         [SerializeField] private Vector3 standingCenter = new Vector3(0, 0, 0);
 | |
|         private bool isCrouching;
 | |
|         private bool duringCrouchAnim;
 | |
| 
 | |
|         [Header("Headbob Parameters")]
 | |
|         [SerializeField] private float walkBobSpeed = 14.0f;
 | |
|         [SerializeField] private float walkBobAmount = 0.05f;
 | |
|         [SerializeField] private float sprintBobSpeed = 18.0f;
 | |
|         [SerializeField] private float sprintBobAmount = 0.11f;
 | |
| 
 | |
|         [Header("Footstep Parameters")]
 | |
|         [SerializeField] private float baseStepSpeed = 0.5f;
 | |
|         [SerializeField] private float crouchStepMultiplier = 1.5f;
 | |
|         [SerializeField] private float sprintStepMultiplier = 0.6f;
 | |
|         [SerializeField] private AudioSource footstepAudioSource = default;
 | |
|         [SerializeField] private AudioClip[] woodClips = default;
 | |
|         [SerializeField] private AudioClip[] stoneClips = default;
 | |
|         [SerializeField] private AudioClip[] waterClips = default;
 | |
|         [SerializeField] private AudioClip[] grassClips = default;
 | |
| 
 | |
| 
 | |
|         [Header("Spawn Points")]
 | |
|         [SerializeField] private Transform spawnPoint = default;
 | |
|         [SerializeField] private Transform miniGameStartPoint = default;
 | |
| 
 | |
| 
 | |
|         private float footstepTimer = 0;
 | |
|         private float GetCurrentOffset => isCrouching ? baseStepSpeed * crouchStepMultiplier : isSprinting ? baseStepSpeed * sprintStepMultiplier : baseStepSpeed;
 | |
| 
 | |
|         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<Camera>();
 | |
|             characterController = GetComponent<CharacterController>();
 | |
|             Cursor.lockState = CursorLockMode.Locked;
 | |
|             Cursor.visible = false;
 | |
|             defaultYPos = playerCamera.transform.localPosition.y;
 | |
|         }
 | |
| 
 | |
|         private void Start()
 | |
|         {
 | |
|             // Load mouse sensitivity settings from PlayerPrefs
 | |
|             LoadMouseSettings();
 | |
| 
 | |
|             // Check if we should start mini game immediately after spawning
 | |
|             if (LoadingScene.Instance != null && LoadingScene.Instance.playMiniGame)
 | |
|             {
 | |
|                 StartMiniGame();
 | |
|                 LoadingScene.Instance.playMiniGame = false; // Reset the flag
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void Update()
 | |
|         {
 | |
| 
 | |
|             if (LoadingScene.Instance != null && LoadingScene.Instance.playMiniGame)
 | |
|             {
 | |
|                 StartMiniGame();
 | |
|                 LoadingScene.Instance.playMiniGame = false; // Reset the flag
 | |
|             }
 | |
| 
 | |
|             if (CanMove)
 | |
|             {
 | |
|                 HandleMovementInput();
 | |
|                 HandleMouseLook();
 | |
| 
 | |
|                 if (canJump)
 | |
|                     HandleJump();
 | |
| 
 | |
|                 if (canCrouch)
 | |
|                     HandleCrouch();
 | |
| 
 | |
|                 if (canHeadBob)
 | |
|                 {
 | |
|                     HandleHeadBob();
 | |
|                 }
 | |
| 
 | |
|                 if (canInteract)
 | |
|                 {
 | |
|                    // HandleInteractionCheck();
 | |
|                     //HandleInteractionInput();
 | |
|                 }
 | |
| 
 | |
|                 if (useFootsteps)
 | |
|                 {
 | |
|                     HandleFootsteps();
 | |
|                 }
 | |
| 
 | |
| 
 | |
|                 ApplyFinalMovements();
 | |
| 
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void HandleMovementInput()
 | |
|         {
 | |
|             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 HandleHeadBob()
 | |
|         {
 | |
|             if (!characterController.isGrounded) return;
 | |
|             if (Mathf.Abs(moveDirection.x) > 0.1f || Mathf.Abs(moveDirection.z) > 0.1f)
 | |
|             {
 | |
|                 timer += Time.deltaTime * (isSprinting ? sprintBobSpeed : walkBobSpeed);
 | |
|                 playerCamera.transform.localPosition = new Vector3(
 | |
|                     playerCamera.transform.localPosition.x,
 | |
|                     defaultYPos + Mathf.Sin(timer) * (isSprinting ? sprintBobAmount : walkBobAmount),
 | |
|                     playerCamera.transform.localPosition.z);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void HandleMouseLook()
 | |
|         {
 | |
|             float mouseY = Input.GetAxis("Mouse Y") * currentLookSpeedY;
 | |
|             float mouseX = Input.GetAxis("Mouse X") * currentLookSpeedX;
 | |
|             
 | |
|             // Apply mouse inversion if enabled
 | |
|             if (mouseInverted)
 | |
|             {
 | |
|                 mouseY = -mouseY;
 | |
|             }
 | |
|             
 | |
|             rotationX -= mouseY;
 | |
|             rotationX = Mathf.Clamp(rotationX, -upperLookLimit, lowerLookLimit);
 | |
|             playerCamera.transform.localRotation = Quaternion.Euler(rotationX, 0, 0);
 | |
|             transform.rotation *= Quaternion.Euler(0, mouseX, 0);
 | |
|         }
 | |
|         private void HandleJump()
 | |
|         {
 | |
|             if (ShouldJump)
 | |
|                 moveDirection.y = jumpForce;
 | |
|         }
 | |
| 
 | |
|         private void HandleCrouch()
 | |
|         {
 | |
|             if (ShouldCrouch)
 | |
|                 StartCoroutine(CrouchStand());
 | |
| 
 | |
|         }
 | |
|         private void ApplyFinalMovements()
 | |
|         {
 | |
|             if (!characterController.isGrounded)
 | |
|                 moveDirection.y -= gravity * Time.deltaTime;
 | |
| 
 | |
|             characterController.Move(moveDirection * Time.deltaTime);
 | |
|         }
 | |
| 
 | |
|         private IEnumerator CrouchStand()
 | |
|         {
 | |
|             if (isCrouching && Physics.Raycast(playerCamera.transform.position, Vector3.up, 1f))
 | |
|                 yield break;
 | |
| 
 | |
|             duringCrouchAnim = true;
 | |
| 
 | |
|             float timeElapsed = 0;
 | |
|             float targetHeight = isCrouching ? standinghHeight : crouchHeight;
 | |
|             float currentHeight = characterController.height;
 | |
|             Vector3 targetCenter = isCrouching ? standingCenter : crouchCenter;
 | |
|             Vector3 currentCenter = characterController.center;
 | |
| 
 | |
|             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;
 | |
| 
 | |
|             duringCrouchAnim = false;
 | |
|         }
 | |
|        /* private void HandleInteractionCheck()
 | |
|         {
 | |
|             if (Physics.Raycast(playerCamera.ViewportPointToRay(interactionRayPoint), out RaycastHit hit, interactionDistance))
 | |
|             {
 | |
|                 int interactableLayer = LayerMask.NameToLayer("Interactable");
 | |
|                 if (hit.collider.gameObject.layer == interactableLayer && (currentInteractable == null || hit.collider.gameObject.GetInstanceID() != currentInteractable.gameObject.GetInstanceID()))
 | |
|                 {
 | |
|                     hit.collider.TryGetComponent(out currentInteractable);
 | |
|                     if (currentInteractable)
 | |
|                         currentInteractable.OnFocus();
 | |
|                 }
 | |
|             }
 | |
|             else if (currentInteractable)
 | |
|             {
 | |
|                 currentInteractable.OnLoseFocus();
 | |
|                 currentInteractable = null;
 | |
|             }
 | |
|         }
 | |
|             */
 | |
| 
 | |
|         private void HandleFootsteps()
 | |
|         {
 | |
|             if (!characterController.isGrounded) return;
 | |
|             if (currentInput == Vector2.zero) return;
 | |
| 
 | |
|             footstepTimer -= Time.deltaTime;
 | |
|             if (footstepTimer <= 0)
 | |
|             {
 | |
|                 if (Physics.Raycast(playerCamera.transform.position, Vector3.down, out RaycastHit hit, 2))
 | |
|                 {
 | |
|                     switch (hit.collider.tag)
 | |
|                     {
 | |
|                         case "Footsteps/Grass":
 | |
|                             footstepAudioSource.PlayOneShot(grassClips[Random.Range(0, grassClips.Length - 1)]);
 | |
|                             break;
 | |
|                         case "Footsteps/Stone":
 | |
|                             footstepAudioSource.PlayOneShot(stoneClips[Random.Range(0, stoneClips.Length - 1)]);
 | |
|                             break;
 | |
|                         case "Footsteps/Water":
 | |
|                             footstepAudioSource.PlayOneShot(waterClips[Random.Range(0, waterClips.Length - 1)]);
 | |
|                             break;
 | |
|                         case "Footsteps/Wood":
 | |
|                             footstepAudioSource.PlayOneShot(woodClips[Random.Range(0, woodClips.Length - 1)]);
 | |
|                             break;
 | |
|                         default:
 | |
|                             footstepAudioSource.PlayOneShot(grassClips[Random.Range(0, grassClips.Length - 1)]);
 | |
|                             break;
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 footstepTimer = GetCurrentOffset;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|        /* private void HandleInteractionInput()
 | |
|         {
 | |
|             if (Input.GetKeyDown(interactKey) && currentInteractable != null && Physics.Raycast(playerCamera.ViewportPointToRay(interactionRayPoint), out RaycastHit hit, interactionDistance, interactionLayer))
 | |
|             {
 | |
|                 currentInteractable.OnInteract();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         */
 | |
|         private void StartMiniGame()
 | |
|         {
 | |
|             CanMove = true;
 | |
| 
 | |
|             // Disable character controller before teleporting
 | |
|             characterController.enabled = false;
 | |
| 
 | |
|             if (miniGameStartPoint != null)
 | |
|             {
 | |
|                 transform.position = miniGameStartPoint.position;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 Debug.LogWarning("Mini-game start point not found!");
 | |
|             }
 | |
| 
 | |
|             // Re-enable character controller after teleporting
 | |
|             characterController.enabled = true;
 | |
|             
 | |
|            
 | |
|         }
 | |
| 
 | |
|         // Public method to access miniGameStartPoint for respawn triggers
 | |
|         public Transform GetMiniGameStartPoint()
 | |
|         {
 | |
|             return miniGameStartPoint;
 | |
|         }
 | |
| 
 | |
|         // Public method to access spawnPoint for respawn triggers
 | |
|         public Transform GetSpawnPoint()
 | |
|         {
 | |
|             return spawnPoint;
 | |
|         }
 | |
| 
 | |
|         // Load mouse sensitivity settings from PlayerPrefs
 | |
|         private void LoadMouseSettings()
 | |
|         {
 | |
|             currentLookSpeedX = PlayerPrefs.GetFloat("XSensitivity", lookSpeedX);
 | |
|             currentLookSpeedY = PlayerPrefs.GetFloat("YSensitivity", lookSpeedY);
 | |
|             mouseInverted = PlayerPrefs.GetInt("Inverted", 0) == 1;
 | |
|         }
 | |
| 
 | |
|         // Public method to update mouse settings (called by GameSettingsManager)
 | |
|         public void UpdateMouseSettings(float sensitivityX, float sensitivityY, bool inverted)
 | |
|         {
 | |
|             currentLookSpeedX = sensitivityX;
 | |
|             currentLookSpeedY = sensitivityY;
 | |
|             mouseInverted = inverted;
 | |
|         }
 | |
| 
 | |
|         // Public method to control player movement from external scripts
 | |
|         public void SetCanMove(bool canMove)
 | |
|         {
 | |
|             CanMove = canMove;
 | |
|         }
 | |
| 
 | |
|         // Public method to disable player completely (for mini-games)
 | |
|         public void DisablePlayer()
 | |
|         {
 | |
|             CanMove = false;
 | |
|         }
 | |
| 
 | |
|         // Public method to enable player completely
 | |
|         public void EnablePlayer()
 | |
|         {
 | |
|             CanMove = true;
 | |
|         }
 | |
|     }
 | |
| }
 |