wall run is easier to control and triggers more consistently

This commit is contained in:
2026-02-13 10:24:25 +01:00
parent c09dfd1e7b
commit 032e059826
3 changed files with 112 additions and 47 deletions

View File

@@ -869,16 +869,16 @@ public partial class PlayerController : CharacterBody3D,
_playerState.SendEvent("grounded");
if (IsTryingToMantle()) _playerState.SendEvent("mantle");
if (!WallHugSystem.IsWallHugging())
{
_isWallJumpAvailable = true; // reset wall jump if we left the wall
return;
}
// Going upwards, we stay simply airborne
if (Velocity.AngleTo(Vector3.Up) < Math.PI / 4)
return;
// // Going upwards, we stay simply airborne
// if (Velocity.AngleTo(Vector3.Up) < Math.PI / 4)
// return;
// Should we start a wall run
if (ShouldStartWallRun())
@@ -1188,8 +1188,8 @@ public partial class PlayerController : CharacterBody3D,
}
public void OnWallDetected()
{
if (!_onWall.Active)
return;
// if (!_onWall.Active)
// return;
var newWallNormal = WallHugSystem.WallHugNormal.UnwrapOr(Vector3.Up);
if (newWallNormal.AngleTo(_wallHugStartNormal) > Mathf.Pi/4) return;
@@ -1223,11 +1223,11 @@ public partial class PlayerController : CharacterBody3D,
// _canDashAirborne = true;
WallHug(delta);
if (ShouldStartWallRun())
{
_playerState.SendEvent("wall_run");
return;
}
// if (ShouldStartWallRun())
// {
// _playerState.SendEvent("wall_run");
// return;
// }
if (isOnFloorCustom())
_playerState.SendEvent("grounded");
if (!WallHugSystem.IsWallHugging() || !IsInputTowardsWall(_wallHugStartNormal))
@@ -1263,7 +1263,7 @@ public partial class PlayerController : CharacterBody3D,
// Adapt vertical speed
var verticalSpeed = Velocity.Y - WallRunAltitudeLossSpeed * delta;
Velocity = finalHVel + Vector3.Up*verticalSpeed;
// Velocity *= 0.999f;
Velocity *= 0.999f;
_currentWallContactPoint = WallHugSystem.WallHugLocation.UnwrapOr(Vector3.Zero);
@@ -1273,7 +1273,7 @@ public partial class PlayerController : CharacterBody3D,
_playerState.SendEvent("grounded");
if (!WallHugSystem.IsWallHugging())
_playerState.SendEvent("start_falling");
if (Velocity.Length() < WallRunSpeedThreshold / 2f)
if (!CanKeepWallRun())
_playerState.SendEvent("wall_hug");
}
@@ -1286,27 +1286,78 @@ public partial class PlayerController : CharacterBody3D,
public bool ShouldStartWallRun()
{
var wallNormal = WallHugSystem.WallHugNormal.UnwrapOr(Vector3.Zero);
var isIndeedWall = wallNormal.Y < 0.1;
var isThereInput = GetMoveInput().Length() > Mathf.Epsilon;
var hvel = new Vector3(Velocity.X, 0, Velocity.Z);
var hvelProjected = Velocity.Slide(_wallHugStartNormal);
var haveEnoughSpeed = hvelProjected.Length() > WallRunSpeedThreshold;
var isCoplanarEnough = Math.Abs(Velocity.Dot(wallNormal)) < 0.3;
var isGoingDownwards = Velocity.Dot(Vector3.Down) > 0.9;
var isLookingInDirectionOfRun = true; // hvelProjected.Dot(-HeadSystem.GetForwardHorizontalVector()) > 0.5;
var shouldStart = haveEnoughSpeed && isThereInput && !isGoingDownwards && isIndeedWall && isCoplanarEnough && isLookingInDirectionOfRun;
var debugText = "--------------\n";
debugText += shouldStart ? "WALL RUN STARTED\n" : "NO WALL RUN\n";
debugText += $"Enough speed? {haveEnoughSpeed}\n";
debugText += $"Coplanar enough? {isCoplanarEnough}\n";
debugText += $"Going downwards? {isGoingDownwards}\n";
debugText += $"Is looking in direction of run? {isLookingInDirectionOfRun}\n";
debugText += "--------------\n";
GD.Print(debugText);
if (_wallHugStartNormal.Length() < Mathf.Epsilon)
{
// GD.Print("No wall normal");
return false;
}
return shouldStart;
var isIndeedWall = _wallHugStartNormal.Y < 0.1;
if (!isIndeedWall)
{
// GD.Print("Not a wall");
return false;
}
var isThereInput = GetMoveInput().Length() > Mathf.Epsilon;
if (!isThereInput)
{
// GD.Print("Not a wall");
return false;
}
var canUseVelocity = Velocity.Length() > WallRunSpeedThreshold;
if (!canUseVelocity)
{
// GD.Print("Not enough speed");
return false;
}
var hvel = new Vector3(Velocity.X, 0, Velocity.Z);
var hvelProjected = hvel.Slide(_wallHugStartNormal);
var haveEnoughSpeed = hvelProjected.Length() > WallRunSpeedThreshold;
if (!haveEnoughSpeed)
{
// GD.Print("Not enough projected speed");
return false;
}
var isCoplanarEnough = Math.Abs(Velocity.Normalized().Dot(_wallHugStartNormal)) < 0.5;
if (!isCoplanarEnough)
{
// GD.Print("Not coplanar enough");
return false;
}
var isGoingDownwards = Velocity.Normalized().Dot(Vector3.Down) > 0.5;
if (isGoingDownwards)
{
// GD.Print("Going down");
return false;
}
var isLookingInDirectionOfRun = hvelProjected.Normalized().Dot(-HeadSystem.GetForwardHorizontalVector()) > 0.5;
if (!isLookingInDirectionOfRun)
{
// GD.Print("Not looking in direction of run");
return false;
}
return true;
}
public bool CanKeepWallRun()
{
var isThereInput = GetMoveInput().Length() > Mathf.Epsilon;
if (!isThereInput) return false;
var isInputForward = GetInputLocalHDirection().Z < 0;
if (!isInputForward) return false;
var haveEnoughSpeed = Velocity.Length() > WallRunSpeedThreshold;
if (!haveEnoughSpeed) return false;
return true;
}
public void WallHug(float delta)