diff --git a/player_controller/PlayerController.tscn b/player_controller/PlayerController.tscn index 23e7ebe..52e290a 100644 --- a/player_controller/PlayerController.tscn +++ b/player_controller/PlayerController.tscn @@ -50,11 +50,18 @@ blend_mode = 1 [node name="Player" type="CharacterBody3D"] script = ExtResource("1_poq2x") -AccelerationSpeedFactorFloor = 3.0 +WalkSpeed = 7.5 +AccelerationAir = 2.0 +DecelerationAir = 0.1 Weight = 5.0 -SimpleJumpStartVelocity = 7.0 -HangTimeInFrames = 3 -GravityLesseningFactorUpward = 2.5 +SimpleJumpStartVelocity = 8.0 +SimpleJumpHangTimeInFrames = 3 +SimpleJumpGravityLesseningFactor = 2.5 +DoubleJumpStartVelocity = 15.0 +DoubleJumpGravityLesseningFactor = 1.2 +MegaJumpStartVelocity = 30.0 +MegaJumpHangTimeInFrames = 15 +MegaJumpGravityLesseningFactor = 1.2 JumpFromDashSpeedFactor = 4.0 WallHugHorizontalDeceleration = 3.0 MaxJumpBoostAfterDashing = 0.7 @@ -452,18 +459,42 @@ initial_state = NodePath("SimpleJump") [node name="SimpleJump" type="Node" parent="StateChart/Root/Movement/Jump"] script = ExtResource("27_34snm") +[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Jump/SimpleJump"] +script = ExtResource("28_n7qhm") +to = NodePath("../../MegaJump") +event = &"megajump" +delay_in_seconds = "0.0" + [node name="OnJumpEnded" type="Node" parent="StateChart/Root/Movement/Jump/SimpleJump"] script = ExtResource("28_n7qhm") to = NodePath("../../../Airborne/DoubleJumpEnabled") event = &"jump_ended" delay_in_seconds = "0.0" -[node name="Double" type="Node" parent="StateChart/Root/Movement/Jump"] +[node name="DoubleJump" type="Node" parent="StateChart/Root/Movement/Jump"] script = ExtResource("27_34snm") -[node name="Empowered" type="Node" parent="StateChart/Root/Movement/Jump"] +[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Jump/DoubleJump"] +script = ExtResource("28_n7qhm") +to = NodePath("../../MegaJump") +event = &"megajump" +delay_in_seconds = "0.0" + +[node name="OnJumpEnded" type="Node" parent="StateChart/Root/Movement/Jump/DoubleJump"] +script = ExtResource("28_n7qhm") +to = NodePath("../../../Airborne/Falling") +event = &"jump_ended" +delay_in_seconds = "0.0" + +[node name="MegaJump" type="Node" parent="StateChart/Root/Movement/Jump"] script = ExtResource("27_34snm") +[node name="OnJumpEnded" type="Node" parent="StateChart/Root/Movement/Jump/MegaJump"] +script = ExtResource("28_n7qhm") +to = NodePath("../../../Airborne/Falling") +event = &"jump_ended" +delay_in_seconds = "0.0" + [node name="Grounded" type="Node" parent="StateChart/Root/Movement"] script = ExtResource("27_34snm") @@ -475,7 +506,7 @@ delay_in_seconds = "0.0" [node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Grounded"] script = ExtResource("28_n7qhm") -to = NodePath("../../Airborne/Falling") +to = NodePath("../../Jump/MegaJump") event = &"megajump" delay_in_seconds = "0.0" @@ -493,6 +524,12 @@ initial_state = NodePath("CoyoteEnabled") script = ExtResource("41_ruloh") default_state = NodePath("../CoyoteEnabled") +[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Airborne"] +script = ExtResource("28_n7qhm") +to = NodePath("../../Jump/MegaJump") +event = &"megajump" +delay_in_seconds = "0.0" + [node name="OnGrounded" type="Node" parent="StateChart/Root/Movement/Airborne"] script = ExtResource("28_n7qhm") to = NodePath("../../Grounded") @@ -531,7 +568,7 @@ script = ExtResource("27_34snm") [node name="OnJump" type="Node" parent="StateChart/Root/Movement/Airborne/DoubleJumpEnabled"] script = ExtResource("28_n7qhm") -to = NodePath("../../Falling") +to = NodePath("../../../Jump/DoubleJump") event = &"jump" delay_in_seconds = "0.0" diff --git a/player_controller/Scripts/PlayerController.cs b/player_controller/Scripts/PlayerController.cs index d756fb0..cc4a446 100644 --- a/player_controller/Scripts/PlayerController.cs +++ b/player_controller/Scripts/PlayerController.cs @@ -60,12 +60,14 @@ public partial class PlayerController : CharacterBody3D [Export(PropertyHint.Range, "0,20,0.1,or_greater")] public float WalkSpeed { get; set; } = 7.0f; [Export(PropertyHint.Range, "0,10,0.1,or_greater")] - public float AccelerationSpeedFactorFloor = 5.0f; + public float AccelerationFloor = 5.0f; [Export(PropertyHint.Range, "0,10,0.1,or_greater")] - public float DecelerationSpeedFactorFloor = 5.0f; + public float DecelerationFloor = 5.0f; [ExportGroup("Air")] [Export(PropertyHint.Range, "0,10,0.1,or_greater")] - public float DecelerationSpeedFactorAir = 1.0f; + public float AccelerationAir = 3.0f; + [Export(PropertyHint.Range, "0,10,0.01,or_greater")] + public float DecelerationAir = 1.0f; [Export(PropertyHint.Range, "0,10,0.01,or_greater")] public float Weight { get; set; } = 3.0f; @@ -77,9 +79,29 @@ public partial class PlayerController : CharacterBody3D [Export(PropertyHint.Range, "0,100,1,or_greater")] public float SimpleJumpStartVelocity { get; set; } = 3.0f; [Export(PropertyHint.Range, "0,10,1,or_greater")] - public int HangTimeInFrames { get; set; } = 5; + public int SimpleJumpHangTimeInFrames { get; set; } = 5; [Export(PropertyHint.Range, "1,10,0.1,or_greater")] - public float GravityLesseningFactorUpward { get; set; } = 3f; + public float SimpleJumpGravityLesseningFactor { get; set; } = 3f; + + + // Double jump + [ExportSubgroup("Double jump")] + [Export(PropertyHint.Range, "0,100,1,or_greater")] + public float DoubleJumpStartVelocity { get; set; } = 10.0f; + [Export(PropertyHint.Range, "0,10,1,or_greater")] + public int DoubleJumpHangTimeInFrames { get; set; } = 5; + [Export(PropertyHint.Range, "1,10,0.1,or_greater")] + public float DoubleJumpGravityLesseningFactor { get; set; } = 3f; + + + // Mega jump + [ExportSubgroup("Mega jump")] + [Export(PropertyHint.Range, "0,100,1,or_greater")] + public float MegaJumpStartVelocity { get; set; } = 10.0f; + [Export(PropertyHint.Range, "0,10,1,or_greater")] + public int MegaJumpHangTimeInFrames { get; set; } = 5; + [Export(PropertyHint.Range, "1,10,0.1,or_greater")] + public float MegaJumpGravityLesseningFactor { get; set; } = 3f; // Other jump [ExportSubgroup("Other jump")] @@ -153,6 +175,8 @@ public partial class PlayerController : CharacterBody3D private StateChartState _airborne; private StateChartState _coyoteEnabled; private StateChartState _simpleJump; + private StateChartState _doubleJump; + private StateChartState _megaJump; private StateChartState _doubleJumpEnabled; private StateChartState _onWall; private StateChartState _onWallHugCanceled; @@ -230,6 +254,8 @@ public partial class PlayerController : CharacterBody3D _airborne = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne")); _coyoteEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/CoyoteEnabled")); _simpleJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Jump/SimpleJump")); + _doubleJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Jump/DoubleJump")); + _megaJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Jump/MegaJump")); _doubleJumpEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/DoubleJumpEnabled")); _onWall = StateChartState.Of(GetNode("StateChart/Root/Movement/OnWall")); _onWallHugging = StateChartState.Of(GetNode("StateChart/Root/Movement/OnWall/Hugging")); @@ -311,18 +337,56 @@ public partial class PlayerController : CharacterBody3D // _empowerOff.StateEntered += EmpowerStopped; // _empowerTimeDownscale.Timeout += EmpowerTimerTimeout; + _powerFull.StateEntered += StopPowerCooldown; _powerFull.StateExited += StartPowerCooldown; _powerRecharging.StateEntered += StartPowerCooldown; - _powerFull.StateEntered += StopPowerCooldown; _powerCooldownTimer.Timeout += PowerCooldownExpired; _powerRecharging.StateProcessing += PowerRecharging; _powerExpired.StateProcessing += PowerRecharging; _simpleJump.StateEntered += OnSimpleJumpStarted; _simpleJump.StatePhysicsProcessing += HandleSimpleJump; - _simpleJump.StateExited += OnSimpleJumpEnded; + + _doubleJump.StateEntered += OnDoubleJumpStarted; + _doubleJump.StatePhysicsProcessing += HandleDoubleJump; + + _megaJump.StateEntered += OnMegaJumpStarted; + _megaJump.StatePhysicsProcessing += HandleMegaJump; } + // Physics processes + public void HandleGrounded(float delta) + { + MoveOnGround(delta); + _canDash = true; + if (!isOnFloorCustom()) + _playerState.SendEvent("start_falling"); + } + public void HandleAirborne(float delta) + { + MoveInAir(delta); + if (isOnFloorCustom()) + _playerState.SendEvent("grounded"); + if (WallHugSystem.IsWallHugging() && Velocity.Y < 0) + _playerState.SendEvent("wall_hug"); + } + public void HandleWallHugging(float delta) + { + WallHug(delta); + if (isOnFloorCustom()) + _playerState.SendEvent("grounded"); + if (!WallHugSystem.IsWallHugging()) + _playerState.SendEvent("start_falling"); + } + + public void HandleWallHanging(float delta) + { + WallHang(delta); + } + + // Jump + private int _framesSinceJumpAtApex = 0; + public void OnInputJumpStarted() { if (CanMantle()) @@ -330,7 +394,7 @@ public partial class PlayerController : CharacterBody3D Mantle(); return; } - + // if (_grounded.Active || _coyoteEnabled.Active) // { // if (_empowerOn.Active && CanPerformEmpoweredAction()) @@ -351,7 +415,12 @@ public partial class PlayerController : CharacterBody3D // PerformJump(JumpTypes.DoubleJump); // else if (_onWall.Active) // JumpFromWall(_empowerOn.Active); - + + if (_empowerOn.Active && CanPerformEmpoweredAction()) + { + _playerState.SendEvent("megajump"); + return; + } _playerState.SendEvent("jump"); } @@ -363,32 +432,67 @@ public partial class PlayerController : CharacterBody3D { _playerState.SendEvent("jump_ended"); } - - private int _framesSinceJumpAtApex = 0; - public void OnSimpleJumpStarted() + + public void OnJumpStarted(float verticalVelocity) { _framesSinceJumpAtApex = 0; + SetVerticalVelocity(verticalVelocity); + } + public void OnSimpleJumpStarted() + { + OnJumpStarted(SimpleJumpStartVelocity); + } + public void OnDoubleJumpStarted() + { + OnJumpStarted(DoubleJumpStartVelocity); + } + public void OnMegaJumpStarted() + { + OnJumpStarted(MegaJumpStartVelocity); + } + + public Vector3 ComputeHVelocity(float delta, float accelerationFactor, float decelerationFactor) + { + Vector3 direction = HeadSystem.Transform.Basis * _inputMove; + var acceleration = direction.Length() > 0 ? accelerationFactor : decelerationFactor; + + float xAcceleration = Mathf.Lerp(Velocity.X, direction.X * _targetSpeed, delta * acceleration); + float zAcceleration = Mathf.Lerp(Velocity.Z, direction.Z * _targetSpeed, delta * acceleration); + return new Vector3(xAcceleration, 0, zAcceleration); + } + public Vector3 ComputeHVelocityGround(float delta) + { + return ComputeHVelocity(delta, AccelerationFloor, DecelerationFloor); + } + public Vector3 ComputeHVelocityAir(float delta) + { + return ComputeHVelocity(delta, AccelerationAir, DecelerationAir); + } + + public void SetVerticalVelocity(float verticalVelocity) + { Velocity = new Vector3( x: Velocity.X, - y: SimpleJumpStartVelocity, + y: verticalVelocity, z: Velocity.Z); } - public void HandleSimpleJump(float delta) + public void HandleJump(float delta, float gravityFactor, int hangFrames) { + // Update horizontal velocity + var horizontalVelocity = ComputeHVelocityAir(delta); + Velocity = new Vector3(horizontalVelocity.X, Velocity.Y, horizontalVelocity.Z); + // Hang time at the top of the jump if (Velocity.Y <= Mathf.Epsilon) { _framesSinceJumpAtApex++; - Velocity = new Vector3( - x: Velocity.X, - y: 0, - z: Velocity.Z); + SetVerticalVelocity(0); } // Cancel gravity on jump apex - var gravity = CalculateGravityForce() / GravityLesseningFactorUpward; + var gravity = CalculateGravityForce() / gravityFactor; var isAtApex = _framesSinceJumpAtApex > 0; if (isAtApex) { @@ -396,19 +500,27 @@ public partial class PlayerController : CharacterBody3D } // Update velocity accordingly var newVerticalSpeed = Velocity.Y - gravity * delta; - Velocity = new Vector3( - x: Velocity.X, - y: newVerticalSpeed, - z: Velocity.Z); - + SetVerticalVelocity(newVerticalSpeed); + if (IsHeadTouchingCeiling()) + { + SetVerticalVelocity(Velocity.Y - 2.0f); + } // Move back to Airborne state management when starting to go down again - if (_framesSinceJumpAtApex > HangTimeInFrames) + if (_framesSinceJumpAtApex > hangFrames) _playerState.SendEvent("jump_ended"); } - - public void OnSimpleJumpEnded() + public void HandleSimpleJump(float delta) { + HandleJump(delta, SimpleJumpGravityLesseningFactor, SimpleJumpHangTimeInFrames); + } + public void HandleDoubleJump(float delta) + { + HandleJump(delta, DoubleJumpGravityLesseningFactor, DoubleJumpHangTimeInFrames); + } + public void HandleMegaJump(float delta) + { + HandleJump(delta, MegaJumpGravityLesseningFactor, MegaJumpHangTimeInFrames); } public void PowerRecharging(float delta) @@ -777,36 +889,6 @@ public partial class PlayerController : CharacterBody3D DashSystem.PrepareDash(); } - // Physics processes - public void HandleGrounded(float delta) - { - MoveOnGround(delta); - _canDash = true; - if (!isOnFloorCustom()) - _playerState.SendEvent("start_falling"); - } - public void HandleAirborne(float delta) - { - MoveInAir(delta); - if (isOnFloorCustom()) - _playerState.SendEvent("grounded"); - if (WallHugSystem.IsWallHugging() && Velocity.Y < 0) - _playerState.SendEvent("wall_hug"); - } - public void HandleWallHugging(float delta) - { - WallHug(delta); - if (isOnFloorCustom()) - _playerState.SendEvent("grounded"); - if (!WallHugSystem.IsWallHugging()) - _playerState.SendEvent("start_falling"); - } - - public void HandleWallHanging(float delta) - { - WallHang(delta); - } - /////////////////////////// // Stateless logic //////// @@ -819,33 +901,15 @@ public partial class PlayerController : CharacterBody3D public void MoveOnGround(double delta) { - Vector3 direction = HeadSystem.Transform.Basis * _inputMove; - - var accelerationFactor = direction.Length() > 0 ? AccelerationSpeedFactorFloor : DecelerationSpeedFactorFloor; - - float xAcceleration = Mathf.Lerp(Velocity.X, direction.X * _targetSpeed, - (float)delta * accelerationFactor); - float zAcceleration = Mathf.Lerp(Velocity.Z, direction.Z * _targetSpeed, - (float)delta * accelerationFactor); - Velocity = new Vector3(xAcceleration, Velocity.Y, zAcceleration); + var horizontalVelocity = ComputeHVelocityGround((float) delta); + Velocity = new Vector3(horizontalVelocity.X, Velocity.Y, horizontalVelocity.Z); } public void MoveInAir(double delta) { - Velocity = new Vector3( - x: Velocity.X, - y: Velocity.Y - (CalculateGravityForce() * (float)delta), - z: Velocity.Z); - - // The code below is required to quickly adjust player's position on Y-axis when there's a ceiling on the - // trajectory of player's jump and player is standing - if (IsHeadTouchingCeiling()) - { - Velocity = new Vector3( - x: Velocity.X, - y: Velocity.Y - 2.0f, - z: Velocity.Z); - } + var horizontalVelocity = ComputeHVelocityAir((float) delta); + var verticalVelocity = Velocity.Y - (CalculateGravityForce() * (float)delta); + Velocity = new Vector3(horizontalVelocity.X, verticalVelocity, horizontalVelocity.Z); } public void WallHug(float delta)