Compare commits

...

4 Commits

Author SHA1 Message Date
fffd8c947b implementing jump input buffering on grounded
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 18s
Create tag and build when new code gets to main / Export (push) Successful in 9m47s
2026-01-07 09:58:18 +01:00
a1d57d6a1a some tuto fixing
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 19s
Create tag and build when new code gets to main / Export (push) Successful in 9m49s
2026-01-06 16:35:17 +01:00
941205af2b small gravity and jump balancing, removed wall jumping coyote times
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 19s
Create tag and build when new code gets to main / Export (push) Successful in 9m34s
2026-01-06 16:08:39 +01:00
6c2ad89687 fixing the manlting into geo
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 19s
Create tag and build when new code gets to main / Export (push) Successful in 9m42s
2026-01-06 12:36:38 +01:00
8 changed files with 685 additions and 610 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -63,22 +63,22 @@ blend_mode = 1
[node name="Player" type="CharacterBody3D"]
script = ExtResource("1_poq2x")
WalkSpeed = 7.5
AccelerationAir = 2.0
DecelerationAir = 0.1
Weight = 5.0
MantleTime = 0.2
AccelerationAir = 0.8
DecelerationAir = 0.02
Weight = 4.0
MantleTime = 0.3
MantlePath = ExtResource("2_6lejt")
CoyoteTime = 0.3
SimpleJumpStartVelocity = 8.0
InputBufferFrames = 5
SimpleJumpStartVelocity = 6.0
SimpleJumpHangTimeInFrames = 1
SimpleJumpGravityLesseningFactor = 2.5
DoubleJumpStartVelocity = 15.0
SimpleJumpGravityLesseningFactor = 2.0
DoubleJumpHangTimeInFrames = 3
DoubleJumpGravityLesseningFactor = 1.5
MegaJumpStartVelocity = 30.0
MegaJumpHangTimeInFrames = 12
MegaJumpGravityLesseningFactor = 1.2
WallJumpStartVelocity = 12.0
WallJumpStartVelocity = 8.0
MaxNumberOfEmpoweredActions = 3
SimpleDashStrength = 15.0
PoweredDashStrength = 30.0
@@ -236,6 +236,11 @@ transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 0
mesh = SubResource("CylinderMesh_nodcl")
[node name="DashCooldown" type="Timer" parent="."]
wait_time = 0.8
one_shot = true
[node name="AirborneDashCooldown" type="Timer" parent="."]
wait_time = 0.5
one_shot = true
[node name="PowerCooldown" type="Timer" parent="."]
@@ -609,7 +614,7 @@ default_state = NodePath("../CoyoteEnabled")
[node name="OnWallRun" type="Node" parent="StateChart/Root/Movement/Airborne"]
script = ExtResource("28_n7qhm")
to = NodePath("../../OnWall/RunningCoyoteEnabled")
to = NodePath("../../OnWall/Running")
event = &"wall_run"
delay_in_seconds = "0.0"
@@ -672,7 +677,7 @@ script = ExtResource("27_34snm")
[node name="OnWallHug" type="Node" parent="StateChart/Root/Movement/Airborne/Falling"]
script = ExtResource("28_n7qhm")
to = NodePath("../../../OnWall/HuggingCoyoteEnabled")
to = NodePath("../../../OnWall/Hugging")
event = &"wall_hug"
delay_in_seconds = "0.0"
@@ -736,7 +741,7 @@ script = ExtResource("27_34snm")
[node name="OnJump" type="Node" parent="StateChart/Root/Movement/OnWall/Hugging"]
script = ExtResource("28_n7qhm")
to = NodePath("../../../Jump/SimpleJump")
to = NodePath("../../../Jump/DoubleJump")
event = &"jump"
delay_in_seconds = "0.0"
@@ -745,7 +750,7 @@ script = ExtResource("27_34snm")
[node name="OnJump" type="Node" parent="StateChart/Root/Movement/OnWall/Hanging"]
script = ExtResource("28_n7qhm")
to = NodePath("../../../Jump/SimpleJump")
to = NodePath("../../../Jump/DoubleJump")
event = &"jump"
delay_in_seconds = "0.0"

View File

@@ -58,6 +58,7 @@ public partial class PlayerController : CharacterBody3D
// Timers
private Timer _timeScaleAimInAirTimer;
private Timer _simpleDashCooldownTimer;
private Timer _airborneDashCooldownTimer;
private Timer _powerCooldownTimer;
[Export] public Marker3D TutorialWeaponTarget;
@@ -88,6 +89,8 @@ public partial class PlayerController : CharacterBody3D
[ExportGroup("Jump")]
[Export(PropertyHint.Range, "0,1,0.01")]
public float CoyoteTime { get; set; } = 0.2f;
[Export(PropertyHint.Range, "0,10,1,or_greater")]
public int InputBufferFrames { get; set; } = 3;
// Simple jump
[ExportSubgroup("Simple jump")]
@@ -228,6 +231,8 @@ public partial class PlayerController : CharacterBody3D
private Transition _onLeaveWallFromRunCoyote;
private Transition _onLeaveWallFromRun;
private int _currentJumpBufferFrames = 0;
private float _playerHeight;
private float _playerRadius;
private bool _isJumpInputPressed;
@@ -327,6 +332,7 @@ public partial class PlayerController : CharacterBody3D
_powerCooldownTimer = GetNode<Timer>("PowerCooldown");
_timeScaleAimInAirTimer = GetNode<Timer>("TimeScaleAimInAir");
_simpleDashCooldownTimer = GetNode<Timer>("DashCooldown");
_airborneDashCooldownTimer = GetNode<Timer>("AirborneDashCooldown");
///////////////////////////
// Initialize components //
@@ -398,6 +404,7 @@ public partial class PlayerController : CharacterBody3D
_aimedDash.StateExited += OnAimedDashFinished;
_simpleDashCooldownTimer.Timeout += DashCooldownTimeout;
_airborneDashCooldownTimer.Timeout += AirborneDashCooldownTimeout;
_onWall.StateEntered += OnWallStarted;
_onWall.StateExited += OnWallStopped;
@@ -463,16 +470,31 @@ public partial class PlayerController : CharacterBody3D
if (_simpleDashCooldownTimer.IsStopped())
_simpleDashCooldownTimer.Start();
if (_currentJumpBufferFrames > 0)
{
_currentJumpBufferFrames = 0;
PerformJump();
}
}
public void DashCooldownTimeout()
{
_canDash = true;
}
public void AirborneDashCooldownTimeout()
{
_canDashAirborne = true;
}
public bool IsPlayerInputtingForward()
{
return GetMoveInput().Z < -0.5f;
}
public bool IsTryingToMantle()
{
return MantleSystem.IsMantlePossible && GetMoveInput().Z < -0.5f && _isJumpInputPressed;
return MantleSystem.IsMantlePossible && IsPlayerInputtingForward() && _isJumpInputPressed;
}
public void HandleGrounded(float delta)
@@ -485,15 +507,17 @@ public partial class PlayerController : CharacterBody3D
public void HandleAirborne(float delta)
{
MoveInAir(delta);
if (isOnFloorCustom())
_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)
@@ -625,7 +649,13 @@ public partial class PlayerController : CharacterBody3D
// Jump
public void OnInputJumpStarted()
{
_currentJumpBufferFrames = InputBufferFrames;
_isJumpInputPressed = true;
PerformJump();
}
public void PerformJump()
{
if (MantleSystem.IsMantlePossible)
{
_playerState.SendEvent("mantle");
@@ -638,13 +668,15 @@ public partial class PlayerController : CharacterBody3D
return;
}
if (_onWallHuggingCoyoteEnabled.Active || _onWallRunningCoyoteEnabled.Active)
{
if (!_isWallJumpAvailable) return;
}
if (_onWall.Active && !_isWallJumpAvailable && IsFacingWall()) return;
_playerState.SendEvent("jump");
}
public bool IsFacingWall()
{
return _wallHugStartNormal.Dot(GetGlobalForwardFacingVector()) < -0.5f;
}
public void OnInputJumpOngoing()
{
}
@@ -667,7 +699,7 @@ public partial class PlayerController : CharacterBody3D
public void OnDoubleJumpStarted()
{
_canDash = true;
_canDashAirborne = true;
// _canDashAirborne = true;
OnJumpStarted(DoubleJumpStartVelocity);
}
public void OnMegaJumpStarted()
@@ -699,7 +731,14 @@ public partial class PlayerController : CharacterBody3D
public void OnJumpFromWall()
{
ComputeJumpFromWallHSpeed(WallJumpStartVelocity);
if (!IsFacingWall())
{
ComputeJumpFromWallHSpeed(WallJumpStartVelocity);
}
// Remove the ability to dash straight away so you cannot scale up the wall
_canDashAirborne = false;
_airborneDashCooldownTimer.Start();
_isWallJumpAvailable = false;
}
public void OnMegajumpFromWall()
{
@@ -847,8 +886,8 @@ public partial class PlayerController : CharacterBody3D
SetVerticalVelocity(Velocity.Y - 2.0f);
}
// Move back to Airborne state management when starting to go down again
if (_framesSinceJumpAtApex > hangFrames)
// Move back to Airborne state when starting to go down again or if input isn't held anymore (buffered jump)
if (_framesSinceJumpAtApex > hangFrames || !_isJumpInputPressed)
_playerState.SendEvent("jump_ended");
}
public void HandleSimpleJump(float delta)
@@ -1361,12 +1400,19 @@ public partial class PlayerController : CharacterBody3D
{
WeaponRoot.SetRotation(HeadSystem.Rotation);
}
public Vector3 GetGlobalForwardFacingVector()
{
return Transform.Basis * HeadSystem.Transform.Basis * Vector3.Forward;
}
///////////////////////////
// Processes //////////////
///////////////////////////
public override void _PhysicsProcess(double delta)
{
if (_currentJumpBufferFrames > 0) _currentJumpBufferFrames -= 1;
LookAround(delta);
CameraModifications((float) delta);
HandleStairs((float) delta);

View File

@@ -57,13 +57,15 @@ public partial class MantleSystem: Node3D
IsMantlePossible = false;
if (!isColliding) return;
// Check if collide with wall
// Check if face something wall-like that should be climbable
var collisionNormal = isGrounded ? _groundedWallDetect.GetCollisionNormal(0) : _inAirWallDetect.GetCollisionNormal(0);
if (collisionNormal.Y > 0.9f) return;
if (collisionNormal.Y > 0.7f) return;
var spaceState = GetWorld3D().DirectSpaceState;
MantleCurve = new Curve3D();
MantleCurve.AddPoint(Vector3.Zero);
var hasFirstProfileHit = false;
var hasFirstProfileHit = false;
var previousProfilePoint = GlobalPosition;
foreach (var wallProfileShapecast in _wallProfileShapecasts)
{
// Haven't met the wall yet
@@ -74,22 +76,31 @@ public partial class MantleSystem: Node3D
// Got to the other side of the wall, we stop there
if (!wallProfileShapecast.IsColliding())
{
MantleCurve.AddPoint(ToLocal(globalTargetPosition));
// MantleCurve.AddPoint(ToLocal(globalTargetPosition));
break;
}
var profilePoint = wallProfileShapecast.GetCollisionPoint(0);
var profileNormal = wallProfileShapecast.GetCollisionNormal(0);
var shape = wallProfileShapecast.Shape as SphereShape3D;
var shapeRadius = shape == null ? 0.125f : shape.Radius;
var centerOfShape = profilePoint + profileNormal * shapeRadius;
// Check if we collided parallel to a wall
var isCollisionSameAsTarget = globalTargetPosition.IsEqualApprox(profilePoint);
var isCollisionSameAsTarget = globalTargetPosition.IsEqualApprox(centerOfShape);
var isCollidingWithWall = profileNormal.Y < 0.1f;
if (isCollisionSameAsTarget || isCollidingWithWall) continue;
// Check if the path from the previous point makes us go through a wall
var query = PhysicsRayQueryParameters3D.Create(previousProfilePoint, centerOfShape, wallProfileShapecast.CollisionMask);
var result = spaceState.IntersectRay(query);
if (result.Count > 0) break; // We are going through a wall, we stop there
// We have a valid collision
if (!hasFirstProfileHit) FirstMantleProfilePoint = profilePoint;
if (!hasFirstProfileHit) FirstMantleProfilePoint = centerOfShape;
hasFirstProfileHit = true;
MantleCurve.AddPoint(ToLocal(profilePoint));
previousProfilePoint = centerOfShape;
MantleCurve.AddPoint(ToLocal(centerOfShape));
}
if (MantleCurve.PointCount == 1) return;

View File

@@ -58,47 +58,47 @@ collision_mask = 2
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, -0.5)
enabled = false
shape = SubResource("SphereShape3D_i32qj")
target_position = Vector3(0, -2.375, 0)
target_position = Vector3(0, -2.125, 0)
collision_mask = 2
[node name="ShapeCast2" type="ShapeCast3D" parent="WallProfileShapeCasts"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, -0.75)
enabled = false
shape = SubResource("SphereShape3D_i32qj")
target_position = Vector3(0, -2.375, 0)
target_position = Vector3(0, -2.125, 0)
collision_mask = 2
[node name="ShapeCast3" type="ShapeCast3D" parent="WallProfileShapeCasts"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, -1)
enabled = false
shape = SubResource("SphereShape3D_i32qj")
target_position = Vector3(0, -2.375, 0)
target_position = Vector3(0, -2.125, 0)
collision_mask = 2
[node name="ShapeCast4" type="ShapeCast3D" parent="WallProfileShapeCasts"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, -1.25)
enabled = false
shape = SubResource("SphereShape3D_i32qj")
target_position = Vector3(0, -2.375, 0)
target_position = Vector3(0, -2.125, 0)
collision_mask = 2
[node name="ShapeCast5" type="ShapeCast3D" parent="WallProfileShapeCasts"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, -1.5)
enabled = false
shape = SubResource("SphereShape3D_i32qj")
target_position = Vector3(0, -2.375, 0)
target_position = Vector3(0, -2.125, 0)
collision_mask = 2
[node name="ShapeCast6" type="ShapeCast3D" parent="WallProfileShapeCasts"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, -1.75)
enabled = false
shape = SubResource("SphereShape3D_i32qj")
target_position = Vector3(0, -2.375, 0)
target_position = Vector3(0, -2.125, 0)
collision_mask = 2
[node name="ShapeCast7" type="ShapeCast3D" parent="WallProfileShapeCasts"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, -2)
enabled = false
shape = SubResource("SphereShape3D_i32qj")
target_position = Vector3(0, -2.375, 0)
target_position = Vector3(0, -2.125, 0)
collision_mask = 2