168 lines
5.5 KiB
C#
168 lines
5.5 KiB
C#
|
|
using UnityEngine;
|
||
|
|
|
||
|
|
[RequireComponent(typeof(CharacterController))]
|
||
|
|
public class PlayerController : MonoBehaviour
|
||
|
|
{
|
||
|
|
[Header("Movement")]
|
||
|
|
public float walkSpeed = 6f;
|
||
|
|
public float sprintSpeed = 10f;
|
||
|
|
public float crouchSpeed = 3f;
|
||
|
|
public float jumpHeight = 1.2f;
|
||
|
|
public float gravity = -20f;
|
||
|
|
|
||
|
|
[Header("Mouse Look")]
|
||
|
|
public float mouseSensitivity = 2f;
|
||
|
|
public float maxLookAngle = 85f;
|
||
|
|
public Transform cameraHolder;
|
||
|
|
|
||
|
|
[Header("Crouch")]
|
||
|
|
public float standingHeight = 2f;
|
||
|
|
public float crouchingHeight = 1f;
|
||
|
|
public float crouchTransitionSpeed = 10f;
|
||
|
|
|
||
|
|
[Header("Head Bob")]
|
||
|
|
public float bobFrequency = 10f;
|
||
|
|
public float bobAmplitude = 0.04f;
|
||
|
|
public float sprintBobMultiplier = 1.6f;
|
||
|
|
|
||
|
|
// --- Private State ---
|
||
|
|
private CharacterController _cc;
|
||
|
|
private Vector3 _velocity;
|
||
|
|
private float _xRotation;
|
||
|
|
private bool _isCrouching;
|
||
|
|
private bool _isSprinting;
|
||
|
|
private float _bobTimer;
|
||
|
|
private Vector3 _cameraLocalOrigin;
|
||
|
|
|
||
|
|
void Awake()
|
||
|
|
{
|
||
|
|
_cc = GetComponent<CharacterController>();
|
||
|
|
|
||
|
|
if (cameraHolder == null && Camera.main != null)
|
||
|
|
cameraHolder = Camera.main.transform;
|
||
|
|
|
||
|
|
if (cameraHolder != null)
|
||
|
|
_cameraLocalOrigin = cameraHolder.localPosition;
|
||
|
|
|
||
|
|
Cursor.lockState = CursorLockMode.Locked;
|
||
|
|
Cursor.visible = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Update()
|
||
|
|
{
|
||
|
|
HandleMouseLook();
|
||
|
|
HandleCrouch();
|
||
|
|
HandleMovement();
|
||
|
|
HandleHeadBob();
|
||
|
|
}
|
||
|
|
|
||
|
|
// -----------------------------------------------------------------------
|
||
|
|
// Mouse Look
|
||
|
|
// -----------------------------------------------------------------------
|
||
|
|
void HandleMouseLook()
|
||
|
|
{
|
||
|
|
float mouseX = Input.GetAxisRaw("Mouse X") * mouseSensitivity;
|
||
|
|
float mouseY = Input.GetAxisRaw("Mouse Y") * mouseSensitivity;
|
||
|
|
|
||
|
|
_xRotation -= mouseY;
|
||
|
|
_xRotation = Mathf.Clamp(_xRotation, -maxLookAngle, maxLookAngle);
|
||
|
|
|
||
|
|
if (cameraHolder != null)
|
||
|
|
cameraHolder.localRotation = Quaternion.Euler(_xRotation, 0f, 0f);
|
||
|
|
|
||
|
|
transform.Rotate(Vector3.up * mouseX);
|
||
|
|
}
|
||
|
|
|
||
|
|
// -----------------------------------------------------------------------
|
||
|
|
// Movement & Jumping
|
||
|
|
// -----------------------------------------------------------------------
|
||
|
|
void HandleMovement()
|
||
|
|
{
|
||
|
|
bool grounded = _cc.isGrounded;
|
||
|
|
|
||
|
|
if (grounded && _velocity.y < 0f)
|
||
|
|
_velocity.y = -2f; // keep grounded
|
||
|
|
|
||
|
|
float h = Input.GetAxisRaw("Horizontal");
|
||
|
|
float v = Input.GetAxisRaw("Vertical");
|
||
|
|
|
||
|
|
_isSprinting = grounded && Input.GetKey(KeyCode.LeftShift) && !_isCrouching && v > 0f;
|
||
|
|
|
||
|
|
float speed = _isCrouching ? crouchSpeed : (_isSprinting ? sprintSpeed : walkSpeed);
|
||
|
|
|
||
|
|
// Instant direction change — no momentum (arcade feel)
|
||
|
|
Vector3 move = (transform.right * h + transform.forward * v).normalized * speed;
|
||
|
|
_cc.Move(move * Time.deltaTime);
|
||
|
|
|
||
|
|
// Jump
|
||
|
|
if (Input.GetButtonDown("Jump") && grounded && !_isCrouching)
|
||
|
|
_velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
||
|
|
|
||
|
|
// Gravity
|
||
|
|
_velocity.y += gravity * Time.deltaTime;
|
||
|
|
_cc.Move(new Vector3(0f, _velocity.y, 0f) * Time.deltaTime);
|
||
|
|
}
|
||
|
|
|
||
|
|
// -----------------------------------------------------------------------
|
||
|
|
// Crouch
|
||
|
|
// -----------------------------------------------------------------------
|
||
|
|
void HandleCrouch()
|
||
|
|
{
|
||
|
|
bool wantCrouch = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.C);
|
||
|
|
|
||
|
|
// Prevent standing up if something is above the player
|
||
|
|
if (_isCrouching && !wantCrouch && !CanStandUp())
|
||
|
|
wantCrouch = true;
|
||
|
|
|
||
|
|
_isCrouching = wantCrouch;
|
||
|
|
|
||
|
|
float targetHeight = _isCrouching ? crouchingHeight : standingHeight;
|
||
|
|
_cc.height = Mathf.Lerp(_cc.height, targetHeight, crouchTransitionSpeed * Time.deltaTime);
|
||
|
|
_cc.center = new Vector3(0f, _cc.height / 2f, 0f);
|
||
|
|
|
||
|
|
// Move camera with the capsule top
|
||
|
|
if (cameraHolder != null)
|
||
|
|
{
|
||
|
|
float targetCamY = _cameraLocalOrigin.y - (standingHeight - _cc.height) * 0.5f;
|
||
|
|
Vector3 pos = cameraHolder.localPosition;
|
||
|
|
pos.y = Mathf.Lerp(pos.y, targetCamY, crouchTransitionSpeed * Time.deltaTime);
|
||
|
|
cameraHolder.localPosition = new Vector3(_cameraLocalOrigin.x, pos.y, _cameraLocalOrigin.z);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool CanStandUp()
|
||
|
|
{
|
||
|
|
float checkDistance = standingHeight - _cc.height;
|
||
|
|
Vector3 top = transform.position + Vector3.up * (_cc.height - _cc.radius);
|
||
|
|
return !Physics.SphereCast(top, _cc.radius * 0.9f, Vector3.up, out _, checkDistance);
|
||
|
|
}
|
||
|
|
|
||
|
|
// -----------------------------------------------------------------------
|
||
|
|
// Head Bob
|
||
|
|
// -----------------------------------------------------------------------
|
||
|
|
void HandleHeadBob()
|
||
|
|
{
|
||
|
|
if (cameraHolder == null) return;
|
||
|
|
|
||
|
|
bool isMoving = _cc.isGrounded && _cc.velocity.magnitude > 0.1f;
|
||
|
|
|
||
|
|
if (isMoving)
|
||
|
|
{
|
||
|
|
float multiplier = _isSprinting ? sprintBobMultiplier : 1f;
|
||
|
|
_bobTimer += Time.deltaTime * bobFrequency * multiplier;
|
||
|
|
|
||
|
|
float bobY = Mathf.Sin(_bobTimer) * bobAmplitude;
|
||
|
|
float bobX = Mathf.Sin(_bobTimer * 0.5f) * bobAmplitude * 0.5f;
|
||
|
|
|
||
|
|
Vector3 pos = cameraHolder.localPosition;
|
||
|
|
pos.y += bobY;
|
||
|
|
pos.x = _cameraLocalOrigin.x + bobX;
|
||
|
|
cameraHolder.localPosition = pos;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
_bobTimer = 0f;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|