diff --git a/player_controller/PlayerController.tscn b/player_controller/PlayerController.tscn index 761fc962..4ebddfee 100644 --- a/player_controller/PlayerController.tscn +++ b/player_controller/PlayerController.tscn @@ -59,7 +59,7 @@ height = 1.0 [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_6lejt"] radius = 1.0 -height = 3.0 +height = 3.5 [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_2q0ik"] blend_mode = 1 @@ -89,6 +89,7 @@ SimpleDashTime = 0.2 AimedDashTime = 0.2 PostDashSpeed = 30.0 SlamSpeed = 80.0 +FlatGroundSlideSpeedLossRate = 0.996 GroundSlideJumpMultiplier = 0.1 GroundSlideJumpSpeedFactor = 0.1 GroundSlideDownSlopeAcceleration = 0.2 @@ -248,7 +249,10 @@ mesh = SubResource("CylinderMesh_nodcl") [node name="GroundDetector" type="ShapeCast3D" parent="."] shape = SubResource("CapsuleShape3D_6lejt") -target_position = Vector3(0, -0.5, 0) +collision_mask = 2 + +[node name="DirectGroundDetector" type="RayCast3D" parent="."] +target_position = Vector3(0, -2, 0) collision_mask = 2 [node name="DashCooldown" type="Timer" parent="."] @@ -647,6 +651,12 @@ to = NodePath("../../../Jump/SimpleJump") event = &"jump" delay_in_seconds = "0.0" +[node name="OnCancel" type="Node" parent="StateChart/Root/Movement/Sliding/GroundSlide"] +script = ExtResource("28_n7qhm") +to = NodePath("../../SlideCanceled") +event = &"slide_canceled" +delay_in_seconds = "0.0" + [node name="AirGlideDoubleJumpEnabled" type="Node" parent="StateChart/Root/Movement/Sliding"] script = ExtResource("27_34snm") @@ -683,6 +693,33 @@ to = NodePath("../../GroundSlide") event = &"grounded" delay_in_seconds = "0.0" +[node name="SlideCanceled" type="Node" parent="StateChart/Root/Movement/Sliding"] +script = ExtResource("27_34snm") + +[node name="OnAirborne" type="Node" parent="StateChart/Root/Movement/Sliding/SlideCanceled"] +script = ExtResource("28_n7qhm") +to = NodePath("../../../Airborne/CoyoteEnabled") +event = &"start_falling" +delay_in_seconds = "0.0" + +[node name="OnDash" type="Node" parent="StateChart/Root/Movement/Sliding/SlideCanceled"] +script = ExtResource("28_n7qhm") +to = NodePath("../../../Dashing/Dash") +event = &"dash" +delay_in_seconds = "0.0" + +[node name="OnJump" type="Node" parent="StateChart/Root/Movement/Sliding/SlideCanceled"] +script = ExtResource("28_n7qhm") +to = NodePath("../../../Jump/SimpleJump") +event = &"jump" +delay_in_seconds = "0.0" + +[node name="OnSlideReleased" type="Node" parent="StateChart/Root/Movement/Sliding/SlideCanceled"] +script = ExtResource("28_n7qhm") +to = NodePath("../../../Grounded") +event = &"slide_released" +delay_in_seconds = "0.0" + [node name="Airborne" type="Node" parent="StateChart/Root/Movement"] script = ExtResource("26_infe6") initial_state = NodePath("CoyoteEnabled") diff --git a/player_controller/Scripts/PlayerController.cs b/player_controller/Scripts/PlayerController.cs index fbd7a7cd..37fe8041 100644 --- a/player_controller/Scripts/PlayerController.cs +++ b/player_controller/Scripts/PlayerController.cs @@ -49,6 +49,7 @@ public partial class PlayerController : CharacterBody3D public CylinderMesh DashIndicatorMeshCylinder; public RayCast3D WallRunSnapper; public ShapeCast3D GroundDetector; + public RayCast3D DirectGroundDetector; // Inspector stuff [Export] public Marker3D TutorialWeaponTarget; @@ -142,6 +143,8 @@ public partial class PlayerController : CharacterBody3D public float AccelerationGroundSlide = 1.0f; [Export(PropertyHint.Range, "0,1,0.01,or_greater")] public float DecelerationGroundSlide = 0.1f; + [Export(PropertyHint.Range, "0.99,1,0.0001")] + public float FlatGroundSlideSpeedLossRate = 0.9975f; [Export(PropertyHint.Range, "0,10,0.1,or_greater")] public float GroundSlideJumpMultiplier = 1.0f; [Export(PropertyHint.Range, "0,1,0.01,or_greater")] @@ -282,6 +285,7 @@ public partial class PlayerController : CharacterBody3D private StateChartState _groundSliding; private StateChartState _airGliding; private StateChartState _airGlidingDoubleJump; + private StateChartState _slideCanceled; private StateChartState _slamming; private StateChartState _onWall; private StateChartState _onWallHugging; @@ -332,6 +336,7 @@ public partial class PlayerController : CharacterBody3D WallHugSystem = GetNode("WallHugSystem"); WallRunSnapper = GetNode("%WallRunSnapper"); GroundDetector = GetNode("GroundDetector"); + DirectGroundDetector = GetNode("DirectGroundDetector"); RayCast3D stairsBelowRayCast3D = GetNode("StairsBelowRayCast3D"); RayCast3D stairsAheadRayCast3D = GetNode("StairsAheadRayCast3D"); _headCollisionDetectors = new RayCast3D[NUM_OF_HEAD_COLLISION_DETECTORS]; @@ -354,6 +359,7 @@ public partial class PlayerController : CharacterBody3D _slamming = StateChartState.Of(GetNode("StateChart/Root/Movement/Slamming")); _sliding = StateChartState.Of(GetNode("StateChart/Root/Movement/Sliding")); + _slideCanceled = StateChartState.Of(GetNode("StateChart/Root/Movement/Sliding/SlideCanceled")); _groundSliding = StateChartState.Of(GetNode("StateChart/Root/Movement/Sliding/GroundSlide")); _airGliding = StateChartState.Of(GetNode("StateChart/Root/Movement/Sliding/AirGlide")); _airGlidingDoubleJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Sliding/AirGlideDoubleJumpEnabled")); @@ -450,6 +456,8 @@ public partial class PlayerController : CharacterBody3D _sliding.StateEntered += SlideStarted; _sliding.StateExited += SlideEnded; + _slideCanceled.StateEntered += OnSlideCanceled; + _slideCanceled.StatePhysicsProcessing += HandleSlideCanceled; _groundSliding.StatePhysicsProcessing += HandleGroundSlide; _airGliding.StatePhysicsProcessing += HandleAirGlide; _airGlidingDoubleJump.StatePhysicsProcessing += HandleAirGlide; @@ -1265,11 +1273,11 @@ public partial class PlayerController : CharacterBody3D ); public Vector3 GetGroundPosition() { - return GroundDetector.GetCollisionPoint(0); + return DirectGroundDetector.GetCollisionPoint(); } public Vector3 GetGroundNormal() { - return GroundDetector.GetCollisionNormal(0); + return DirectGroundDetector.GetCollisionNormal(); } public SlopeRecord GetSlope() { @@ -1289,39 +1297,63 @@ public partial class PlayerController : CharacterBody3D { // Store current velocity var currentVelocity = Velocity.Length(); + var finalSpeed = currentVelocity * FlatGroundSlideSpeedLossRate; // Going down slope? var (position, normal, slopeDirection, slopeAngleRadians) = GetSlope(); // Change velocity based on Input var horizontalVelocity = ComputeHVelocity(delta, AccelerationGroundSlide, DecelerationGroundSlide); - var newVelocityDirection = new Vector3(horizontalVelocity.X, 0, horizontalVelocity.Z).Normalized(); + var newVelocityDirection = new Vector3(horizontalVelocity.X, Velocity.Y, horizontalVelocity.Z).Normalized(); + var newVelocityHDirection = new Vector3(horizontalVelocity.X, 0, horizontalVelocity.Z).Normalized(); // var redirectedVelocity = newVelocityDirection.Slide(normal); var redirectedVelocity = newVelocityDirection; - var speedFactorFromDownSlope = 1f; if (slopeAngleRadians > Mathf.Epsilon) { var slopeHDirection = new Vector3(slopeDirection.X, 0, slopeDirection.Z); redirectedVelocity = newVelocityDirection.Lerp(slopeHDirection, delta * GroundSlideSlopeMagnetism); - var angleBetweenVelocityAndSlope = redirectedVelocity.AngleTo(slopeHDirection); - var cosAngle = Mathf.Cos(angleBetweenVelocityAndSlope); - var redirectedVVelocity = slopeDirection.Y * cosAngle; + var angleBetweenVelocityAndSlope = newVelocityHDirection.AngleTo(slopeHDirection); + var velocitySlopeAlignment = Mathf.Cos(angleBetweenVelocityAndSlope); + var slopeSpeedFactor = Mathf.Remap(velocitySlopeAlignment, -1, 1, 0.98, 1.02); + var speedFactorFromDownSlope = Velocity.Length() > GroundSlideDownSlopeMaxSpeed ? 1f : slopeSpeedFactor; + finalSpeed *= (float) speedFactorFromDownSlope; - redirectedVelocity = new Vector3(redirectedVelocity.X, redirectedVVelocity, redirectedVelocity.Z); - speedFactorFromDownSlope = Velocity.Length() > GroundSlideDownSlopeMaxSpeed ? 1f : 1f + GroundSlideDownSlopeAcceleration * GetFloorAngle() * cosAngle; + // var redirectedVVelocity = slopeDirection.Y * velocitySlopeAlignment; + // redirectedVelocity = new Vector3(redirectedVelocity.X, redirectedVVelocity, redirectedVelocity.Z); + + // Moving upslope and not enough speed + if (velocitySlopeAlignment < 0 && DirectGroundDetector.IsColliding() && Velocity.Length() < WalkSpeed / 2f) _playerState.SendEvent("slide_canceled"); + } + else if (DirectGroundDetector.IsColliding() && Velocity.Length() < WalkSpeed / 2f) + { + // Moving on flat ground and not enough speed + _playerState.SendEvent("slide_canceled"); } // Preserve velocity when changing direction - var finalVelocity = redirectedVelocity.Normalized() * currentVelocity * speedFactorFromDownSlope; + var finalVelocity = redirectedVelocity.Normalized() * finalSpeed; Velocity = finalVelocity; + if (DirectGroundDetector.IsColliding()) + { + GlobalPosition = new Vector3(GlobalPosition.X, position.Y, GlobalPosition.Z); + } + } + + public void OnSlideCanceled() + { + _targetSpeed = WalkSpeed; + } + public void HandleSlideCanceled(float delta) + { + HandleGrounded(delta); } public void HandleGroundSlide(float delta) { SlideOnGround(delta); if (MantleSystem.IsMantlePossible && IsPlayerInputtingForward()) _playerState.SendEvent("mantle"); - if (!isOnFloorCustom()) + if (!isOnFloorCustom() && !DirectGroundDetector.IsColliding()) _playerState.SendEvent("start_falling"); }