diff --git a/Game/CourierOld/Assets/Code/Gameplay/Player/CharacterController.cs b/Game/CourierOld/Assets/Code/Gameplay/Player/CharacterController.cs index 48bfc45..ef1135d 100644 --- a/Game/CourierOld/Assets/Code/Gameplay/Player/CharacterController.cs +++ b/Game/CourierOld/Assets/Code/Gameplay/Player/CharacterController.cs @@ -16,6 +16,7 @@ namespace Code.Gameplay.Player public Vector3 CameraPlanarDirection; // Направление камеры в горизонтальной плоскости public bool JumpPressed; // Флаг указывающий, нажата ли кнопка прыжка public bool SprintPressed; // Флаг указывающий, нажата ли кнопка спринта + public bool SlidePressed; // Флаг указывающий, нажата ли кнопка слайда } /* @@ -32,8 +33,11 @@ namespace Code.Gameplay.Player [SerializeField] private float - maxStableMoveSpeed = 8f, // Максимальная скорость движения по устойчивой поверхности + maxStableMoveSpeed = 8f, // Максимальная скорость движения по устойчивой поверхности sprintMoveSpeed = 12f, // Максимальная скорость спринта + slideSpeed = 20f, // Скорость слайда + slideDuration = 0.8f, // Длительность слайда в секундах + slideCooldown = 1.5f, // Время перезарядки слайда stableMovementSharpness = 15f, // Резкость изменения скорости движения (чем выше - тем резче) orientationSharpness = 10f, // Резкость изменения вращения персонажа (чем выше - тем резче) jumpSpeed = 10f; // Скорость прыжка @@ -54,6 +58,13 @@ namespace Code.Gameplay.Player _shouldApplyCameraRotation, // Флаг указывающий, что нужно применить вращение камеры к персонажу _jumpRequested, // Флаг указывающий, что был запрошен прыжок _isSprinting; // Флаг указывающий, что активен режим спринта + + // Переменные для управления слайдом + private bool _isSliding; // Флаг активного слайда + private float _slideTimer; // Таймер слайда + private float _slideCooldownTimer; // Таймер перезарядки слайда + private Vector3 _slideDirection; // Направление слайда + private bool _slideRequested; // Флаг запроса слайда // Инициализация контроллера private void Start() @@ -65,11 +76,33 @@ namespace Code.Gameplay.Player // Обработка и преобразование входных данных от игрока public void SetInputs(ref PlayerInputs inputs) { + // Обновляем таймер перезарядки слайда + if (_slideCooldownTimer > 0f) + { + _slideCooldownTimer -= Time.deltaTime; + } + + // Если активен слайд - игнорируем весь остальной ввод + if (_isSliding) + { + // Во время слайда не обрабатываем движение, прыжки и спринт + _moveInputVector = Vector3.zero; + _lookInputVector = Vector3.one; + _isSprinting = false; + return; + } + // Сохраняем текущий режим камеры _currentCameraMode = inputs.CameraMode; // Обработка спринта _isSprinting = inputs is { SprintPressed: true, MoveAxisForward: > 0.1f }; // Спринт только при движении вперёд + + // Обработка слайда + if (inputs.SlidePressed && !_slideRequested && _slideCooldownTimer <= 0f) + { + _slideRequested = true; + } // Нормализация вектора движения и ограничение его величины до 1 var moveInputVector = @@ -90,6 +123,71 @@ namespace Code.Gameplay.Player _jumpRequested = true; } + // Начало слайда + private void StartSlide() + { + // Проверяем условия для старта слайда + if (!motor.GroundingStatus.IsStableOnGround || _slideCooldownTimer > 0f) + { + _slideRequested = false; + return; + } + + _isSliding = true; + _slideTimer = slideDuration; + + // НАПРАВЛЕНИЕ СЛАЙДА - ВСЕГДА ВПЕРЕД ПО КАМЕРЕ + // Используем направление камеры, но проецируем на горизонтальную плоскость + var slideDir = _currentCameraMode == CameraMode.FirstPerson ? + _firstPersonCameraRotation * Vector3.forward : + _lookInputVector; + + // Проецируем на горизонтальную плоскость и нормализуем + _slideDirection = Vector3.ProjectOnPlane(slideDir, motor.CharacterUp).normalized; + + // Если направление нулевое (персонаж стоит на месте), используем направление вперед + if (_slideDirection.sqrMagnitude < 0.01f) + { + _slideDirection = motor.CharacterForward; + } + + // Принудительно отключаем спринт и сбрасываем прыжок во время слайда + _isSprinting = false; + _jumpRequested = false; + _slideRequested = false; + + // Устанавливаем перезарядку + _slideCooldownTimer = slideCooldown; + } + + // Обработка слайда + private void HandleSlide(ref Vector3 currentVelocity, float deltaTime) + { + // Уменьшаем таймер слайда + _slideTimer -= deltaTime; + + // Если время слайда вышло, персонаж в воздухе - заканчиваем слайд + if (_slideTimer <= 0f || !motor.GroundingStatus.IsStableOnGround) + { + EndSlide(); + return; + } + + // Устанавливаем скорость слайда вперед + // Используем прямую установку скорости вместо сложных вычислений + currentVelocity = _slideDirection * slideSpeed; + + // Добавляем небольшую вертикальную компоненту для лучшего скольжения по неровностям + currentVelocity += motor.CharacterUp * 2f; + } + + // Завершение слайда + private void EndSlide() + { + _isSliding = false; + _slideTimer = 0f; + } + // Обработка ввода для FirstPerson режима private void HandleFirstPersonInputs(ref PlayerInputs inputs, Vector3 moveInputVector) { @@ -158,6 +256,9 @@ namespace Code.Gameplay.Player // Обновление вращения персонажа на основе входных данных public void UpdateRotation(ref Quaternion currentRotation, float deltaTime) { + // ВО ВРЕМЯ СЛАЙДА НЕ ОБНОВЛЯЕМ ВРАЩЕНИЕ - ПЕРСОНАЖ СМОТРИТ ПРЯМО + if (_isSliding) return; + switch (_currentCameraMode) { // В FirstPerson режиме применяем вращение камеры напрямую к персонажу (только горизонталь) @@ -196,6 +297,19 @@ namespace Code.Gameplay.Player // Обновление скорости движения персонажа public void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { + // Проверяем запрос слайда и запускаем его если можно + if (_slideRequested && !_isSliding && motor.GroundingStatus.IsStableOnGround && _slideCooldownTimer <= 0f) + { + StartSlide(); + } + + // Если активен слайд - обрабатываем отдельно + if (_isSliding) + { + HandleSlide(ref currentVelocity, deltaTime); + return; + } + // Если персонаж находится на земле if (motor.GroundingStatus.IsStableOnGround) { @@ -252,6 +366,11 @@ namespace Code.Gameplay.Player // Метод вызывается после обработки столкновения с землей public void PostGroundingUpdate(float deltaTime) { + // Если во время слайда персонаж оторвался от земли - заканчиваем слайд + if (_isSliding && !motor.GroundingStatus.IsStableOnGround) + { + EndSlide(); + } } // Метод вызывается после обновления персонажа (можно использовать для пост-обработки) diff --git a/Game/CourierOld/Assets/Code/Gameplay/Player/Player.cs b/Game/CourierOld/Assets/Code/Gameplay/Player/Player.cs index 68b3386..3c1d259 100644 --- a/Game/CourierOld/Assets/Code/Gameplay/Player/Player.cs +++ b/Game/CourierOld/Assets/Code/Gameplay/Player/Player.cs @@ -59,6 +59,7 @@ public class Player : MonoBehaviour inputs.MoveAxisRight = moveInput.x; // Horizontal ось (A/D или Left/Right) inputs.JumpPressed = _inputService.JumpPressed; // Прыжки (Space) inputs.SprintPressed = _inputService.SprintPressed; // Спринт (Shift) + inputs.SlidePressed = _inputService.SlidePressed; // Слайд (Ctrl) // Передаем информацию о камере для адаптивного поведения inputs.CameraRotation = playerCamera.transform.rotation; diff --git a/Game/CourierOld/Assets/Code/Input/IInputService.cs b/Game/CourierOld/Assets/Code/Input/IInputService.cs index bb74a07..bd3b73c 100644 --- a/Game/CourierOld/Assets/Code/Input/IInputService.cs +++ b/Game/CourierOld/Assets/Code/Input/IInputService.cs @@ -15,6 +15,7 @@ namespace Code.Input Vector2 MoveInput { get; } bool JumpPressed { get; } bool SprintPressed { get; } + bool SlidePressed { get; } // Для переключения режима камеры bool ToggleCameraModePressed { get; } diff --git a/Game/CourierOld/Assets/Code/Input/InputService.cs b/Game/CourierOld/Assets/Code/Input/InputService.cs index eda443d..bf57cd8 100644 --- a/Game/CourierOld/Assets/Code/Input/InputService.cs +++ b/Game/CourierOld/Assets/Code/Input/InputService.cs @@ -14,6 +14,7 @@ namespace Code.Input public Vector2 MoveInput { get; private set; } public bool JumpPressed { get; private set; } public bool SprintPressed { get; private set; } + public bool SlidePressed { get; private set; } public bool ToggleCameraModePressed { get; private set; } public InputService(InputActionAsset inputActionAsset) @@ -74,6 +75,14 @@ namespace Code.Input sprintAction.canceled += OnSprintCanceled; } + // Обработка слайда (персонаж) + var slideAction = gameplayMap.FindAction("Slide"); + if (slideAction != null) + { + slideAction.performed += OnSlidePerformed; + slideAction.canceled += OnSlideCanceled; + } + // Обработка переключения режима камеры var toggleCameraModeAction = gameplayMap.FindAction("ToggleCameraMode"); if (toggleCameraModeAction != null) @@ -132,6 +141,16 @@ namespace Code.Input { SprintPressed = false; } + + private void OnSlidePerformed(InputAction.CallbackContext context) + { + SlidePressed = true; + } + + private void OnSlideCanceled(InputAction.CallbackContext context) + { + SlidePressed = false; + } private void OnToggleCameraModePerformed(InputAction.CallbackContext context) { @@ -171,6 +190,9 @@ namespace Code.Input gameplayMap.FindAction("Sprint").performed -= OnSprintPerformed; gameplayMap.FindAction("Sprint").canceled -= OnSprintCanceled; + gameplayMap.FindAction("Slide").performed -= OnSlidePerformed; + gameplayMap.FindAction("Slide").canceled -= OnSlideCanceled; + gameplayMap.FindAction("ToggleCameraMode").performed -= OnToggleCameraModePerformed; gameplayMap.FindAction("ToggleCameraMode").canceled -= OnToggleCameraModeCanceled; } diff --git a/Game/CourierOld/Assets/Resources/Scenes/MovementDev.unity b/Game/CourierOld/Assets/Resources/Scenes/MovementDev.unity index 35d0e2c..b6d64a8 100644 --- a/Game/CourierOld/Assets/Resources/Scenes/MovementDev.unity +++ b/Game/CourierOld/Assets/Resources/Scenes/MovementDev.unity @@ -985,8 +985,11 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Assembly-CSharp::Code.Gameplay.Player.CharacterController motor: {fileID: 1696686256} - maxStableMoveSpeed: 10 - sprintMoveSpeed: 15 + maxStableMoveSpeed: 8 + sprintMoveSpeed: 12 + slideSpeed: 20 + slideDuration: 0.8 + slideCooldown: 1.5 stableMovementSharpness: 15 orientationSharpness: 10 jumpSpeed: 10