diff --git a/Assets/Scripts/PlayerCharacterController.cs b/Assets/Scripts/PlayerCharacterController.cs index 80f1d1b..b64c81f 100644 --- a/Assets/Scripts/PlayerCharacterController.cs +++ b/Assets/Scripts/PlayerCharacterController.cs @@ -92,6 +92,9 @@ public class PlayerCharacterController : MonoBehaviour [Tooltip("How fast the lean-out offset eases back to the wall while moving (units/second).")] public float wallOffsetReturnSpeed = 2f; + [Tooltip("How snappily the body follows the lean-out offset (1/second). Higher = tighter, lower = smoother.")] + public float wallOffsetFollow = 12f; + [Tooltip("Grace period (seconds) of lost contact tolerated before leaving wall-climb mode.")] public float wallExitGrace = 0.15f; @@ -406,14 +409,14 @@ public class PlayerCharacterController : MonoBehaviour velocity += alongWall.normalized * climbSpeed; if (found) { - // Hold the chosen standoff (offset, 0 = flush) without fighting the controller's - // depenetration. A deadzone wider than the skin-width settling distance ignores - // sub-threshold gap noise that otherwise buzzed the body back and forth along the - // wall normal. Clamp guards against probe spikes. + // Smoothly approach the chosen standoff. Target one skin-width beyond the offset so + // the capsule rests just outside the surface (offset 0 = flush): the controller's + // depenetration never fights back, so there's no buzz and no quantizing deadzone is + // needed. Exponential follow keeps it framerate-stable; clamp guards probe spikes. const float maxPerpSpeed = 8f; - float gapError = _wallOffset - gap; - if (Mathf.Abs(gapError) > _controller.skinWidth + 0.02f) - velocity += _wallNormal * Mathf.Clamp(gapError / dt, -maxPerpSpeed, maxPerpSpeed); + float gapError = (_wallOffset + _controller.skinWidth) - gap; + float follow = 1f - Mathf.Exp(-wallOffsetFollow * dt); + velocity += _wallNormal * Mathf.Clamp(gapError * follow / dt, -maxPerpSpeed, maxPerpSpeed); } _controller.Move(velocity * dt);