diff --git a/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/Abilities/WallClimbMechanic.cs b/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/Abilities/WallClimbMechanic.cs index 667250f..64967d5 100644 --- a/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/Abilities/WallClimbMechanic.cs +++ b/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/Abilities/WallClimbMechanic.cs @@ -15,6 +15,12 @@ namespace Code.Gameplay.Character.Controller.Abilities private readonly float _minWallClimbSpeed; // Пороговый косинус стены public float WallNormalThreshold { get; } + + private bool _hasClimbedThisWallSession; + private float _wallClimbCooldownTimer; + private readonly float _wallClimbCooldownDuration = 1.5f; + public bool IsWallInteractionAllowed => + !_hasClimbedThisWallSession && _wallClimbCooldownTimer <= 0f; // Конструктор механики залазания на стену public WallClimbMechanic(float wallClimbSpeedFactor, float minWallClimbSpeed, float wallDetectionAngleThreshold) @@ -26,12 +32,22 @@ namespace Code.Gameplay.Character.Controller.Abilities } // Обработка столкновения при движении (вызывается при ударе о препятствие) - public void OnMovementHit(KinematicCharacterMotor motor, Vector3 hitNormal) + public bool TryClimbWall(KinematicCharacterMotor motor, Vector3 hitNormal) { // Проверяем, что это вертикальная или почти вертикальная поверхность (стена) var dotWithUp = Vector3.Dot(hitNormal, motor.CharacterUp); - if (Mathf.Abs(dotWithUp) > WallNormalThreshold) return; // Не стена — это пол или потолок + + if (Mathf.Abs(dotWithUp) > WallNormalThreshold) + { + // Сбрасываем сессию, если коснулись пола/потолка → выход из "режима стены" + _hasClimbedThisWallSession = false; + return false; + } + // Запрещаем повторное залазание в рамках одной сессии + if (_hasClimbedThisWallSession || _wallClimbCooldownTimer > 0f) + return false; + // Получаем текущую скорость персонажа var currentVelocity = motor.Velocity; @@ -41,6 +57,7 @@ namespace Code.Gameplay.Character.Controller.Abilities // Если движемся в стену с достаточной скоростью if (velocityIntoWall > _minWallClimbSpeed) { + _wallClimbCooldownTimer = _wallClimbCooldownDuration; // Преобразуем часть горизонтальной скорости в вертикальную var verticalBoost = velocityIntoWall * _wallClimbSpeedFactor; @@ -49,6 +66,24 @@ namespace Code.Gameplay.Character.Controller.Abilities // Предотвращаем заземление и повторное применение в этом кадре motor.ForceUnground(); + _hasClimbedThisWallSession = true; + + return true; + } + + return false; + } + + public void ResetWallClimbSession() + { + _hasClimbedThisWallSession = false; + } + + public void TickCooldown(float deltaTime) + { + if (_wallClimbCooldownTimer > 0f) + { + _wallClimbCooldownTimer -= deltaTime; } } } diff --git a/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/Abilities/WallLeanMechanic.cs b/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/Abilities/WallLeanMechanic.cs index 3b3296e..ae39cf7 100644 --- a/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/Abilities/WallLeanMechanic.cs +++ b/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/Abilities/WallLeanMechanic.cs @@ -129,7 +129,7 @@ namespace Code.Gameplay.Character.Controller.Abilities { _leanFactor = Mathf.Lerp(_leanFactor, 0f, 1f - Mathf.Exp(-_leanSharpness * deltaTime)); } - + ApplyLeanRotation(motor); } diff --git a/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/CustomCharacterController.cs b/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/CustomCharacterController.cs index 6d6ef5d..fa69933 100644 --- a/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/CustomCharacterController.cs +++ b/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/CustomCharacterController.cs @@ -81,6 +81,7 @@ namespace Code.Gameplay.Character.Controller { // Обновляем таймер перезарядки слайда _slideMechanic.TickCooldown(Time.deltaTime); + _wallClimbMechanic.TickCooldown(Time.deltaTime); // Если активен слайд - игнорируем весь остальной ввод if (_slideMechanic.IsSliding) @@ -196,6 +197,11 @@ namespace Code.Gameplay.Character.Controller { _slideMechanic.EndSlide(); } + + if (motor.GroundingStatus.IsStableOnGround) + { + _wallClimbMechanic.ResetWallClimbSession(); + } } // Метод вызывается после обновления персонажа @@ -220,13 +226,8 @@ namespace Code.Gameplay.Character.Controller public void OnMovementHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, ref HitStabilityReport hitStabilityReport) { - _wallClimbMechanic.OnMovementHit(motor, hitNormal); - - var dotWithUp = Vector3.Dot(hitNormal, motor.CharacterUp); - if (Mathf.Abs(dotWithUp) <= _wallClimbMechanic.WallNormalThreshold) - { + if (_wallClimbMechanic.TryClimbWall(motor, hitNormal)) _wallLeanMechanic.OnWallContact(motor, hitNormal, motor.TransientPosition.y); - } } // Обработка отчета о стабильности столкновения