Files
Fishing2/Assets/EasyPeasyFirstPersonController/Scripts/FirstPersonController.cs
2025-05-29 21:10:30 +08:00

268 lines
12 KiB
C#

namespace EasyPeasyFirstPersonController
{
using System;
using System.Collections;
using UnityEngine;
public class FirstPersonController : MonoBehaviour
{
[Range(0, 100)] public float mouseSensitivity = 25f;
[Range(0f, 200f)] private float snappiness = 100f;
[Range(0f, 20f)] public float walkSpeed = 10f;
[Range(0f, 30f)] public float sprintSpeed = 15f;
[Range(0f, 10f)] public float crouchSpeed = 6f;
public float crouchHeight = 1f;
public float crouchCameraHeight = 0.5f;
public float slideSpeed = 9f;
public float slideDuration = 0.7f;
public float slideFovBoost = 5f;
public float slideTiltAngle = 5f;
[Range(0f, 15f)] public float jumpSpeed = 3f;
[Range(0f, 50f)] public float gravity = 9.81f;
public bool coyoteTimeEnabled = true;
public float coyoteTimeDuration = 0.25f;
public float normalFov = 60f;
public float sprintFov = 70f;
public float fovChangeSpeed = 5f;
public float walkingBobbingSpeed = 14f;
public float bobbingAmount = 0.05f;
private float sprintBobMultiplier = 1.2f;
private float recoilReturnSpeed = 8f;
public bool canSlide = true;
public bool canJump = true;
public bool canSprint = true;
public bool canCrouch = true;
public Transform groundCheck;
public float groundDistance = 0.3f;
public LayerMask groundMask;
public Transform playerCamera;
public Transform cameraParent;
private float rotX, rotY;
private float xVelocity, yVelocity;
private CharacterController characterController;
private Vector3 moveDirection = Vector3.zero;
private bool isGrounded;
private Vector2 moveInput;
public bool isSprinting;
public bool isCrouching;
public bool isSliding;
private float slideTimer;
private float postSlideCrouchTimer;
private Vector3 slideDirection;
private float originalHeight;
private float originalCameraParentHeight;
private float coyoteTimer;
private Camera cam;
private AudioSource slideAudioSource;
private float bobTimer;
private Vector3 recoil = Vector3.zero;
private bool isLook = true, isMove = true;
private float currentCameraHeight;
private float currentBobOffset;
private float currentFov;
private float fovVelocity;
private float currentSlideSpeed;
private float slideSpeedVelocity;
private float currentTiltAngle;
private float tiltVelocity;
private void Awake()
{
characterController = GetComponent<CharacterController>();
cam = playerCamera.GetComponent<Camera>();
originalHeight = characterController.height;
originalCameraParentHeight = cameraParent.localPosition.y;
// defaultPosY = cameraParent.localPosition.y;
slideAudioSource = gameObject.AddComponent<AudioSource>();
slideAudioSource.playOnAwake = false;
slideAudioSource.loop = false;
Cursor.lockState = CursorLockMode.Locked;
currentCameraHeight = originalCameraParentHeight;
currentBobOffset = 0f;
currentFov = normalFov;
currentSlideSpeed = 0f;
currentTiltAngle = 0f;
}
private void Update()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if (isGrounded && moveDirection.y < 0)
{
moveDirection.y = -2f;
coyoteTimer = coyoteTimeEnabled ? coyoteTimeDuration : 0f;
}
else if (coyoteTimeEnabled)
{
coyoteTimer -= Time.deltaTime;
}
if (isLook)
{
float mouseX = Input.GetAxis("Mouse X") * 10 * mouseSensitivity * Time.deltaTime;
float mouseY = Input.GetAxis("Mouse Y") * 10 * mouseSensitivity * Time.deltaTime;
rotX += mouseX;
rotY -= mouseY;
rotY = Mathf.Clamp(rotY, -90f, 90f);
xVelocity = Mathf.Lerp(xVelocity, rotX, snappiness * Time.deltaTime);
yVelocity = Mathf.Lerp(yVelocity, rotY, snappiness * Time.deltaTime);
float targetTiltAngle = isSliding ? slideTiltAngle : 0f;
currentTiltAngle = Mathf.SmoothDamp(currentTiltAngle, targetTiltAngle, ref tiltVelocity, 0.2f);
playerCamera.transform.localRotation = Quaternion.Euler(yVelocity - currentTiltAngle, 0f, 0f);
transform.rotation = Quaternion.Euler(0f, xVelocity, 0f);
}
// HandleHeadBob();
bool wantsToCrouch = canCrouch && Input.GetKey(KeyCode.LeftControl) && !isSliding;
Vector3 point1 = transform.position + characterController.center -
Vector3.up * (characterController.height * 0.5f);
Vector3 point2 = point1 + Vector3.up * characterController.height * 0.6f;
float capsuleRadius = characterController.radius * 0.95f;
float castDistance = isSliding ? originalHeight + 0.2f : originalHeight - crouchHeight + 0.2f;
bool hasCeiling = Physics.CapsuleCast(point1, point2, capsuleRadius, Vector3.up, castDistance, groundMask);
if (isSliding)
{
postSlideCrouchTimer = 0.3f;
}
if (postSlideCrouchTimer > 0)
{
postSlideCrouchTimer -= Time.deltaTime;
isCrouching = canCrouch;
}
else
{
isCrouching = canCrouch && (wantsToCrouch || (hasCeiling && !isSliding));
}
if (canSlide && isSprinting && Input.GetKeyDown(KeyCode.LeftControl) && isGrounded)
{
isSliding = true;
slideTimer = slideDuration;
slideDirection = moveInput.magnitude > 0.1f
? (transform.right * moveInput.x + transform.forward * moveInput.y).normalized
: transform.forward;
currentSlideSpeed = sprintSpeed;
}
float slideProgress = slideTimer / slideDuration;
if (isSliding)
{
slideTimer -= Time.deltaTime;
if (slideTimer <= 0f || !isGrounded)
{
isSliding = false;
}
float targetSlideSpeed = slideSpeed * Mathf.Lerp(0.7f, 1f, slideProgress);
currentSlideSpeed = Mathf.SmoothDamp(currentSlideSpeed, targetSlideSpeed, ref slideSpeedVelocity, 0.2f);
characterController.Move(slideDirection * currentSlideSpeed * Time.deltaTime);
}
float targetHeight = isCrouching || isSliding ? crouchHeight : originalHeight;
characterController.height = Mathf.Lerp(characterController.height, targetHeight, Time.deltaTime * 10f);
characterController.center = new Vector3(0f, characterController.height * 0.5f, 0f);
float targetFov = isSprinting
? sprintFov
: (isSliding ? sprintFov + (slideFovBoost * Mathf.Lerp(0f, 1f, 1f - slideProgress)) : normalFov);
currentFov = Mathf.SmoothDamp(currentFov, targetFov, ref fovVelocity, 1f / fovChangeSpeed);
cam.fieldOfView = currentFov;
HandleMovement();
}
private void HandleHeadBob()
{
Vector3 horizontalVelocity =
new Vector3(characterController.velocity.x, 0f, characterController.velocity.z);
bool isMovingEnough = horizontalVelocity.magnitude > 0.1f;
float targetBobOffset = isMovingEnough ? Mathf.Sin(bobTimer) * bobbingAmount : 0f;
currentBobOffset = Mathf.Lerp(currentBobOffset, targetBobOffset, Time.deltaTime * walkingBobbingSpeed);
if (!isGrounded || isSliding || isCrouching)
{
bobTimer = 0f;
float targetCameraHeight = isCrouching || isSliding ? crouchCameraHeight : originalCameraParentHeight;
currentCameraHeight = Mathf.Lerp(currentCameraHeight, targetCameraHeight, Time.deltaTime * 10f);
cameraParent.localPosition = new Vector3(
cameraParent.localPosition.x,
currentCameraHeight + currentBobOffset,
cameraParent.localPosition.z);
recoil = Vector3.zero;
cameraParent.localRotation = Quaternion.RotateTowards(cameraParent.localRotation,
Quaternion.Euler(recoil), recoilReturnSpeed * Time.deltaTime);
return;
}
if (isMovingEnough)
{
float bobSpeed = walkingBobbingSpeed * (isSprinting ? sprintBobMultiplier : 1f);
bobTimer += Time.deltaTime * bobSpeed;
float targetCameraHeight = isCrouching || isSliding ? crouchCameraHeight : originalCameraParentHeight;
currentCameraHeight = Mathf.Lerp(currentCameraHeight, targetCameraHeight, Time.deltaTime * 10f);
cameraParent.localPosition = new Vector3(
cameraParent.localPosition.x,
currentCameraHeight + currentBobOffset,
cameraParent.localPosition.z);
recoil.z = moveInput.x * -2f;
}
else
{
bobTimer = 0f;
float targetCameraHeight = isCrouching || isSliding ? crouchCameraHeight : originalCameraParentHeight;
currentCameraHeight = Mathf.Lerp(currentCameraHeight, targetCameraHeight, Time.deltaTime * 10f);
cameraParent.localPosition = new Vector3(
cameraParent.localPosition.x,
currentCameraHeight + currentBobOffset,
cameraParent.localPosition.z);
recoil = Vector3.zero;
}
cameraParent.localRotation = Quaternion.RotateTowards(cameraParent.localRotation, Quaternion.Euler(recoil),
recoilReturnSpeed * Time.deltaTime);
}
private void HandleMovement()
{
moveInput.x = Input.GetAxis("Horizontal");
moveInput.y = Input.GetAxis("Vertical");
isSprinting = canSprint && Input.GetKey(KeyCode.LeftShift) && moveInput.y > 0.1f && isGrounded &&
!isCrouching && !isSliding;
float currentSpeed = isCrouching ? crouchSpeed : (isSprinting ? sprintSpeed : walkSpeed);
if (!isMove) currentSpeed = 0f;
Vector3 direction = new Vector3(moveInput.x, 0f, moveInput.y);
Vector3 moveVector = transform.TransformDirection(direction) * currentSpeed;
moveVector = Vector3.ClampMagnitude(moveVector, currentSpeed);
if (isGrounded || coyoteTimer > 0f)
{
if (canJump && Input.GetKeyDown(KeyCode.Space) && !isSliding)
{
moveDirection.y = jumpSpeed;
}
else if (moveDirection.y < 0)
{
moveDirection.y = -2f;
}
}
else
{
moveDirection.y -= gravity * Time.deltaTime;
}
if (!isSliding)
{
moveDirection = new Vector3(moveVector.x, moveDirection.y, moveVector.z);
characterController.Move(moveDirection * Time.deltaTime);
}
}
}
}