Souljack/Assets/_Project/Scripts/PlayerUtils/PlayerMovement.cs

249 lines
9.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using UnityEngine.InputSystem;
namespace _Project.Scripts.PlayerUtils
{
[RequireComponent(typeof(Rigidbody), typeof(CapsuleCollider))]
public class PlayerMovement : MonoBehaviour
{
[Header("Скорости передвижения (м/с)")]
[SerializeField] private float moveSpeed = 6.5f;
[SerializeField] private float sprintSpeed = 9.5f;
[SerializeField] private float crouchSpeed = 3f;
[Header("Параметры Подката (Slide)")]
[SerializeField] private float slideInitialSpeed = 15f;
[SerializeField] private float slideDuration = 0.85f;
[SerializeField] private float slideFriction = 2.8f;
[SerializeField] private float slideCooldown = 0.35f;
[Header("Физика CS / Source (Bunnyhop & Strafe)")]
[SerializeField] private float groundFriction = 5f;
[SerializeField] private float groundAcceleration = 10f;
[SerializeField] private float airAcceleration = 80f;
[SerializeField] private float airCap = 2.0f;
[SerializeField] private bool showSpeedometer = true;
[Header("Гравитация и Прыжок")]
[SerializeField] private float jumpHeight = 1.35f;
[SerializeField] private float gravity = -25f;
[SerializeField] private float fallMultiplier = 1.8f;
[SerializeField] private LayerMask groundLayer;
[SerializeField] private float groundCheckDistance = 0.12f;
[SerializeField] private float jumpBufferTime = 0.12f;
[Header("Коллайдер (Приседание)")]
[SerializeField] private float standingHeight = 2f;
[SerializeField] private float crouchingHeight = 1.05f;
[SerializeField] private float crouchTransitionSpeed = 15f;
private Rigidbody rb;
private CapsuleCollider col;
private PlayerControls controls;
public bool IsGrounded { get; private set; }
public bool IsSprinting { get; private set; }
public bool IsCrouching { get; private set; }
public bool IsSliding { get; private set; }
public Vector2 MoveInput { get; private set; }
public float CurrentSpeed => horizontalVelocity.magnitude;
private bool wasGrounded;
private float jumpBufferTimer = 0f;
private Vector3 horizontalVelocity = Vector3.zero;
private float verticalVelocity = 0f;
private float slideTimer = 0f;
private float slideCurrentSpeed = 0f;
private Vector3 slideDirection = Vector3.zero;
private float slideCooldownTimer = 0f;
private void Start()
{
rb = GetComponent<Rigidbody>();
col = GetComponent<CapsuleCollider>();
rb.useGravity = false;
rb.freezeRotation = true;
rb.interpolation = RigidbodyInterpolation.None;
controls = Player.Instance.Controls;
controls.Player.Sprint.started += _ => IsSprinting = true;
controls.Player.Sprint.canceled += _ => IsSprinting = false;
controls.Player.Crouch.started += _ => OnCrouchPressed();
controls.Player.Crouch.canceled += _ => OnCrouchReleased();
controls.Player.Jump.performed += _ => QueueJump();
}
private void Update()
{
MoveInput = controls.Player.Move.ReadValue<Vector2>();
if (jumpBufferTimer > 0f) jumpBufferTimer -= Time.deltaTime;
if (slideCooldownTimer > 0f) slideCooldownTimer -= Time.deltaTime;
if (Mouse.current != null && Mathf.Abs(Mouse.current.scroll.y.ReadValue()) > 0.1f)
QueueJump();
HandleColliderHeight();
}
private void FixedUpdate()
{
CheckGrounded();
CalculateCustomGravity();
if (jumpBufferTimer > 0f && IsGrounded)
ExecuteJump();
ApplyMovement();
rb.linearVelocity = new Vector3(horizontalVelocity.x, verticalVelocity, horizontalVelocity.z);
wasGrounded = IsGrounded;
}
private void HandleColliderHeight()
{
bool isLowered = IsCrouching || IsSliding;
float targetHeight = isLowered ? crouchingHeight : standingHeight;
col.height = Mathf.Lerp(col.height, targetHeight, Time.deltaTime * crouchTransitionSpeed);
col.center = new Vector3(col.center.x, col.height * 0.5f, col.center.z);
}
private void QueueJump() => jumpBufferTimer = jumpBufferTime;
private void ExecuteJump()
{
if (IsSliding)
{
horizontalVelocity = slideDirection * slideCurrentSpeed;
StopSlide();
}
verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity);
jumpBufferTimer = 0f;
Player.Instance.CameraController.TriggerJumpImpact();
// ВЫЗОВ ЗВУКА ИЗ НОВОГО МОДУЛЯ
Player.Instance.Sound.PlayJump();
}
private void ApplyMovement()
{
if (IsSliding)
{
slideCurrentSpeed = Mathf.Lerp(slideCurrentSpeed, crouchSpeed, Time.fixedDeltaTime * slideFriction);
slideTimer -= Time.fixedDeltaTime;
Vector3 steerDir = (transform.forward * MoveInput.y + transform.right * MoveInput.x).normalized;
if (steerDir.sqrMagnitude > 0.01f)
slideDirection = Vector3.Slerp(slideDirection, steerDir, Time.fixedDeltaTime * 2.5f);
horizontalVelocity = slideDirection * slideCurrentSpeed;
if (slideTimer <= 0f || slideCurrentSpeed <= crouchSpeed + 0.5f)
{
StopSlide();
IsCrouching = controls.Player.Crouch.IsPressed();
}
return;
}
Vector3 wishDir = (transform.forward * MoveInput.y + transform.right * MoveInput.x);
wishDir.y = 0f;
wishDir.Normalize();
float targetSpeed = IsCrouching ? crouchSpeed : (IsSprinting ? sprintSpeed : moveSpeed);
if (IsGrounded)
{
if (jumpBufferTimer <= 0f) ApplyGroundFriction();
Accelerate(wishDir, targetSpeed, groundAcceleration);
}
else
{
AirAccelerate(wishDir, targetSpeed, airAcceleration, airCap);
}
}
private void ApplyGroundFriction()
{
float speed = horizontalVelocity.magnitude;
if (speed < 0.0001f) { horizontalVelocity = Vector3.zero; return; }
float drop = speed * groundFriction * Time.fixedDeltaTime;
horizontalVelocity *= (Mathf.Max(speed - drop, 0f) / speed);
}
private void Accelerate(Vector3 wishDir, float wishSpeed, float accel)
{
float currentSpeed = Vector3.Dot(horizontalVelocity, wishDir);
float addSpeed = Mathf.Max(wishSpeed - currentSpeed, 0f);
horizontalVelocity += wishDir * Mathf.Min(accel * wishSpeed * Time.fixedDeltaTime, addSpeed);
}
private void AirAccelerate(Vector3 wishDir, float wishSpeed, float accel, float cap)
{
float currentSpeed = Vector3.Dot(horizontalVelocity, wishDir);
float addSpeed = Mathf.Max(Mathf.Min(wishSpeed, cap) - currentSpeed, 0f);
horizontalVelocity += wishDir * Mathf.Min(accel * wishSpeed * Time.fixedDeltaTime, addSpeed);
}
private void CheckGrounded()
{
float checkRadius = col.radius * 0.85f;
float bottomOffset = (col.height * 0.5f) - checkRadius;
Vector3 rayOrigin = transform.position + col.center - (Vector3.up * bottomOffset) + (Vector3.up * 0.05f);
IsGrounded = Physics.SphereCast(rayOrigin, checkRadius, Vector3.down, out _, groundCheckDistance + 0.05f, groundLayer, QueryTriggerInteraction.Ignore);
if (!wasGrounded && IsGrounded)
{
// ВЫЗОВ ЗВУКА ИЗ НОВОГО МОДУЛЯ
Player.Instance.Sound.PlayLand();
}
if (IsGrounded && jumpBufferTimer <= 0f && rb.linearVelocity.magnitude > 0.01f && horizontalVelocity == Vector3.zero)
horizontalVelocity = new Vector3(rb.linearVelocity.x, 0f, rb.linearVelocity.z);
}
private void CalculateCustomGravity()
{
if (IsGrounded && verticalVelocity < 0f) verticalVelocity = -2f;
else verticalVelocity += ((verticalVelocity < 0f) ? (gravity * fallMultiplier) : gravity) * Time.fixedDeltaTime;
}
private void OnCrouchPressed()
{
if (IsSprinting && IsGrounded && MoveInput.y > 0.2f && slideCooldownTimer <= 0f) StartSlide();
else IsCrouching = true;
}
private void OnCrouchReleased()
{
IsCrouching = false;
if (IsSliding) StopSlide();
}
private void StartSlide()
{
IsSliding = true;
slideTimer = slideDuration;
slideCurrentSpeed = slideInitialSpeed;
slideDirection = (transform.forward * MoveInput.y + transform.right * MoveInput.x).normalized;
if (slideDirection.sqrMagnitude < 0.01f) slideDirection = transform.forward;
// ВЫЗОВ ЗВУКА ИЗ НОВОГО МОДУЛЯ
Player.Instance.Sound.PlaySlide();
}
private void StopSlide()
{
IsSliding = false;
slideCooldownTimer = slideCooldown;
}
}
}