new broken mantle system and fixed lots of gamefeel regarding wall hugs and jumps and coyote time
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 20s
Create tag and build when new code gets to main / Export (push) Successful in 8m51s

This commit is contained in:
2025-12-17 16:39:10 +01:00
parent a84e0ecfb3
commit 2e2df4ff50
7 changed files with 478 additions and 69 deletions

View File

@@ -14,21 +14,98 @@ public partial class MantleSystem: Node3D
private ShapeCast3D _wallInFrontCast3D;
private ShapeCast3D _mantleCast3D;
private RayCast3D _mantleCheckCast3D;
private ShapeCast3D _inAirWallDetect;
private ShapeCast3D _groundedWallDetect;
public bool IsMantlePossible { get; private set; } = false;
public int NumberOfValidPofiles { get; private set; } = -1;
public const int WallProfileCastCount = 7;
private ShapeCast3D[] _wallProfileShapecasts = new ShapeCast3D[WallProfileCastCount];
public Vector3[] WallProfilePositions { get; private set; } = new Vector3[WallProfileCastCount];
public Vector3[] WallProfileNormals { get; private set; } = new Vector3[WallProfileCastCount];
public void Init()
{
_wallInFrontCast3D = GetNode<ShapeCast3D>("WallInFrontCast3D");
_mantleCast3D = GetNode<ShapeCast3D>("MantleCast3D");
_inAirWallDetect = GetNode<ShapeCast3D>("InAirWallDetect");
_groundedWallDetect = GetNode<ShapeCast3D>("GroundedWallDetect");
for (int i = 0; i < _wallProfileShapecasts.Length; i++)
{
_wallProfileShapecasts[i] = GetNode<ShapeCast3D>($"WallProfileShapeCasts/ShapeCast{i + 1}");
}
}
public Option<Vector3> FindMantleForHeadRotation(float rotation)
private void SetCastsEnabled(bool enabled)
{
_wallInFrontCast3D.SetRotation(new Vector3(
_wallInFrontCast3D.Rotation.X,
rotation,
_wallInFrontCast3D.Rotation.Z));
foreach (var wallProfileShapecast in _wallProfileShapecasts)
{
wallProfileShapecast.SetEnabled(enabled);
}
}
public void ProcessMantle(bool isGrounded)
{
_inAirWallDetect.SetEnabled(!isGrounded);
_groundedWallDetect.SetEnabled(isGrounded);
var isColliding = _inAirWallDetect.IsColliding() || _groundedWallDetect.IsColliding();
SetCastsEnabled(isColliding);
// Reset state
NumberOfValidPofiles = -1;
IsMantlePossible = false;
if (!isColliding)
{
return;
}
// Check if collide with wall
var collisionNormal = isGrounded ? _groundedWallDetect.GetCollisionNormal(0) : _inAirWallDetect.GetCollisionNormal(0);
if (collisionNormal.Y > 0.8f)
{
return;
}
NumberOfValidPofiles = 0;
var hasFirstProfileHit = false;
foreach (var wallProfileShapecast in _wallProfileShapecasts)
{
// Haven't met the wall yet
if (!wallProfileShapecast.IsColliding() && !hasFirstProfileHit) continue;
var globalTargetPosition = wallProfileShapecast.GlobalPosition + wallProfileShapecast.TargetPosition;
// Got to the other side of the wall, we stop there
if (!wallProfileShapecast.IsColliding())
{
WallProfilePositions[NumberOfValidPofiles] = globalTargetPosition;
WallProfileNormals[NumberOfValidPofiles] = Vector3.Zero;
NumberOfValidPofiles += 1;
break;
}
var profilePoint = wallProfileShapecast.GetCollisionPoint(0);
var profileNormal = wallProfileShapecast.GetCollisionNormal(0);
// Check if we collided parallel to a wall
var isCollisionSameAsTarget = globalTargetPosition.IsEqualApprox(profilePoint);
var isCollidingWithWall = profileNormal.Y < 0.9f;
if (isCollisionSameAsTarget || isCollidingWithWall) continue;
// We have a valid collision
WallProfilePositions[NumberOfValidPofiles] = profilePoint;
WallProfileNormals[NumberOfValidPofiles] = profileNormal;
hasFirstProfileHit = true;
NumberOfValidPofiles += 1;
}
IsMantlePossible = NumberOfValidPofiles > 0; // Should always be true
}
public Option<Vector3> FindMantle()
{
if (!_wallInFrontCast3D.IsColliding())
{
return Option<Vector3>.None;