Загрузка данных


using UnityEngine;

public class PlayerCamera : MonoBehaviour
{
    [Header("References")]
    [SerializeField] private Transform cameraHolder;

    [Header("Mouse")]
    [SerializeField] private float sensitivity = 0.15f;

    [Header("Turn Animation")]
    [SerializeField] private float smallTurnAngle = 15f;
    [SerializeField] private float bigTurnAngle = 45f;
    [SerializeField] private float turnCooldown = 0.8f;

    private float standingTurnAngle;
    private float standingTurnCooldown;

    private float crouchTurnAngle;
    private float crouchTurnCooldown;

    // Запоминаем состояние приседа
    private bool wasCrouching;

    [Header("Head Bob")]
    [SerializeField] private float walkBobSpeed = 8f;
    [SerializeField] private float walkBobAmount = 0.03f;

    [SerializeField] private float sprintBobSpeed = 12f;
    [SerializeField] private float sprintBobAmount = 0.06f;

    private float bobTimer;

    [Header("Idle Breathing")]
    [SerializeField] private float breathingSpeed = 1.3f;
    [SerializeField] private float breathingAmount = 0.015f;

    [Header("Camera Tilt")]
    [SerializeField] private float maxTilt = 3f;
    [SerializeField] private float tiltSpeed = 8f;

    private float currentTilt;

    [Header("Field Of View")]
    [SerializeField] private Camera playerCamera;
    [SerializeField] private float walkFOV = 75f;
    [SerializeField] private float sprintFOV = 80f;
    [SerializeField] private float fovSpeed = 5f;

    [Header("Landing Bounce")]
    [SerializeField] private float landingAmount = 0.08f;
    [SerializeField] private float landingRecoverSpeed = 7f;

    private float landingOffset;

    [Header("Camera Crouch")]
    [SerializeField] private float crouchOffset = 0.5f;
    [SerializeField] private float crouchSpeed = 8f;

    private Vector3 cameraDefaultLocalPos;

    private PlayerInput playerInput;
    private PlayerMovement movement;
    private Animator animator;

    private float xRotation;
    private float turnStartYaw;
    private bool turnTracking;

    private void Awake()
    {
        playerInput = GetComponent<PlayerInput>();
        movement = GetComponent<PlayerMovement>();
        animator = GetComponent<Animator>();

        movement.OnLand += PlayLandingBounce;

        cameraDefaultLocalPos = cameraHolder.localPosition;

        // Запоминаем начальное состояние приседа
        wasCrouching = movement.IsCrouching;

        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }


private void ResetCrouchTurn()
{
    crouchTurnAngle = 0f;
    crouchTurnCooldown = 0f;
}

    private void Update()
    {
        Look();
        UpdateCameraEffects();
        UpdateFOV();
        CheckCrouchStateChange();
    }

    private void Look()
    {
        Vector2 look = playerInput.LookInput;

        // Вертикальный поворот камеры
        xRotation -= look.y * sensitivity;
        xRotation = Mathf.Clamp(xRotation, -90f, 90f);

        // Небольшой наклон камеры при повороте
        float targetTilt = -look.x * 0.08f;
        targetTilt = Mathf.Clamp(targetTilt, -maxTilt, maxTilt);

        currentTilt = Mathf.Lerp(
            currentTilt,
            targetTilt,
            tiltSpeed * Time.deltaTime
        );

        cameraHolder.localRotation =
            Quaternion.Euler(xRotation, 0f, currentTilt);

        // Обычный поворот самого игрока
        float yawDelta = look.x * sensitivity;

        transform.Rotate(Vector3.up * yawDelta);

        // Проверяем turn-анимацию
        CheckTurnAnimation(yawDelta);
    }

    private void CheckTurnAnimation(float yawDelta)
{
    // В воздухе ничего не делаем
    if (!movement.IsGrounded)
    {
        turnTracking = false;
        return;
    }

    // Во время движения Turn не нужен
    if (playerInput.MoveInput.sqrMagnitude > 0.01f)
    {
        turnTracking = false;
        return;
    }

    // Если присел — crouch turn обрабатывается отдельно
    if (movement.IsCrouching)
    {
        turnTracking = false;
        CheckCrouchTurnAnimation(yawDelta);
        return;
    }

    // Если только что закончили предыдущий Turn
    if (turnCooldownTimer > 0f)
    {
        turnCooldownTimer -= Time.deltaTime;
        return;
    }

    // Начинаем отслеживать поворот
    if (!turnTracking)
    {
        turnTracking = true;
        turnStartYaw = transform.eulerAngles.y;
        return;
    }

    // Сколько градусов игрок реально повернулся
    float currentYaw = transform.eulerAngles.y;
    float turnedAngle = Mathf.DeltaAngle(turnStartYaw, currentYaw);

    // Ещё недостаточно
    if (Mathf.Abs(turnedAngle) < smallTurnAngle)
        return;

    // Поворот достаточно большой
    turnTracking = false;
    turnCooldownTimer = turnCooldown;

    if (turnedAngle < 0f)
    {
        if (Mathf.Abs(turnedAngle) >= bigTurnAngle)
            animator.SetTrigger("TurnLeftBig");
        else
            animator.SetTrigger("TurnLeftSmall");
    }
    else
    {
        if (Mathf.Abs(turnedAngle) >= bigTurnAngle)
            animator.SetTrigger("TurnRightBig");
        else
            animator.SetTrigger("TurnRightSmall");
    }
}

    private void CheckCrouchTurnAnimation(float yawDelta)
{
    if (!movement.IsGrounded)
    {
        ResetCrouchTurn();
        return;
    }

    if (!movement.IsCrouching)
    {
        ResetCrouchTurn();
        return;
    }

    if (playerInput.MoveInput.sqrMagnitude > 0.01f)
    {
        ResetCrouchTurn();
        return;
    }

    if (crouchTurnCooldown > 0f)
    {
        crouchTurnCooldown -= Time.deltaTime;
        return;
    }

    crouchTurnAngle += yawDelta;

    if (Mathf.Abs(crouchTurnAngle) < smallTurnAngle)
        return;

    float angle = crouchTurnAngle;

    crouchTurnAngle = 0f;
    crouchTurnCooldown = turnCooldown;

    if (angle < 0f)
    {
        if (Mathf.Abs(angle) >= bigTurnAngle)
            animator.SetTrigger("CrouchTurnLeftBig");
        else
            animator.SetTrigger("CrouchTurnLeftSmall");
    }
    else
    {
        if (Mathf.Abs(angle) >= bigTurnAngle)
            animator.SetTrigger("CrouchTurnRightBig");
        else
            animator.SetTrigger("CrouchTurnRightSmall");
    }
}

    private void CheckCrouchStateChange()
{
    bool isCrouching = movement.IsCrouching;

    if (isCrouching == wasCrouching)
        return;

    ResetStandingTurn();
    ResetCrouchTurn();

    animator.ResetTrigger("TurnLeftSmall");
    animator.ResetTrigger("TurnRightSmall");
    animator.ResetTrigger("TurnLeftBig");
    animator.ResetTrigger("TurnRightBig");

    animator.ResetTrigger("CrouchTurnLeftSmall");
    animator.ResetTrigger("CrouchTurnRightSmall");
    animator.ResetTrigger("CrouchTurnLeftBig");
    animator.ResetTrigger("CrouchTurnRightBig");

    wasCrouching = isCrouching;
}

    private void UpdateCameraEffects()
    {
        Vector3 targetPos = cameraDefaultLocalPos;

        // Приседание
        if (movement.IsCrouching)
        {
            targetPos.y -= crouchOffset;
        }

        // Приземление
        targetPos.y += landingOffset;

        bool moving =
            playerInput.MoveInput.sqrMagnitude > 0.01f;

        if (!moving)
        {
            // Дыхание
            targetPos.y +=
                Mathf.Sin(Time.time * breathingSpeed) *
                breathingAmount;

            bobTimer = 0f;
        }
        else
        {
            bool sprinting =
                playerInput.SprintHeld;

            float speed =
                sprinting
                    ? sprintBobSpeed
                    : walkBobSpeed;

            float amount =
                sprinting? sprintBobAmount
                    : walkBobAmount;

            bobTimer +=
                Time.deltaTime * speed;

            targetPos.y +=
                Mathf.Sin(bobTimer) * amount;

            targetPos.x +=
                Mathf.Cos(bobTimer * 0.5f) *
                amount *
                0.5f;
        }

        // Возвращаем камеру после приземления
        landingOffset = Mathf.Lerp(
            landingOffset,
            0f,
            landingRecoverSpeed * Time.deltaTime
        );

        cameraHolder.localPosition =
            Vector3.Lerp(
                cameraHolder.localPosition,
                targetPos,
                crouchSpeed * Time.deltaTime
            );
    }

    private void PlayLandingBounce()
    {
        landingOffset = -landingAmount;
    }

    private void UpdateFOV()
    {
        bool sprinting =
            playerInput.SprintHeld &&
            playerInput.MoveInput.y > 0;

        float targetFOV =
            sprinting
                ? sprintFOV
                : walkFOV;

        playerCamera.fieldOfView =
            Mathf.Lerp(
                playerCamera.fieldOfView,
                targetFOV,
                fovSpeed * Time.deltaTime
            );
    }

    private void OnDestroy()
    {
        if (movement != null)
        {
            movement.OnLand -= PlayLandingBounce;
        }
    }
}