gd: added the drop action. Set up state managed coyote time, jump, double jump and overall movement.

This commit is contained in:
2025-06-10 10:30:15 +02:00
parent 974e0bb522
commit 141688ef32
6 changed files with 321 additions and 111 deletions

View File

@ -40,6 +40,8 @@ public partial class PlayerController : CharacterBody3D
private bool _isAiming;
private bool _dashCanceled;
private Timer _coyoteTimer;
private StateChart _playerState;
// Actions state
private StateChartState _weaponInHand;
@ -49,11 +51,14 @@ public partial class PlayerController : CharacterBody3D
private StateChartState _actionHanging;
// Movement state
private StateChartState _grounded;
private StateChartState _mantling;
private StateChartState _movHanging;
private StateChartState _wallHugging;
private StateChartState _airborne;
private StateChartState _coyoteEnabled;
private StateChartState _jump;
private StateChartState _doubleJumpEnabled;
private StateChartState _doubleJump;
private StateChartState _falling;
public override void _Ready()
@ -110,12 +115,17 @@ public partial class PlayerController : CharacterBody3D
_actionHanging = StateChartState.Of(GetNode("StateChart/Root/Actions/Hanging"));
// Movement states
_grounded = StateChartState.Of(GetNode("StateChart/Root/Movement/Grounded"));
_mantling = StateChartState.Of(GetNode("StateChart/Root/Movement/Mantling"));
_movHanging = StateChartState.Of(GetNode("StateChart/Root/Movement/Hanging"));
_wallHugging = StateChartState.Of(GetNode("StateChart/Root/Movement/WallHugging"));
_airborne = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne"));
_coyoteEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/CoyoteEnabled"));
_jump = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/Jump"));
_doubleJumpEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/DoubleJumpEnabled"));
_doubleJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/DoubleJump"));
_falling = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/Falling"));
// State timers
_coyoteTimer = GetNode<Timer>("CoyoteTime");
///////////////////////////
// Initialize components //
@ -130,7 +140,6 @@ public partial class PlayerController : CharacterBody3D
FieldOfView.Init(camera);
// Movement stuff
// Getting universal setting from GODOT editor to be in sync
float gravitySetting = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
Gravity.Init(gravitySetting);
@ -161,11 +170,114 @@ public partial class PlayerController : CharacterBody3D
///////////////////////////
DashSystem.DashEnded += OnDashEnded;
_weaponInHand.StateProcessing += HandleWeaponInHand;
_aiming.StateProcessing += HandleAiming;
_grounded.StatePhysicsProcessing += HandleGrounded;
_airborne.StatePhysicsProcessing += HandleAirborne;
_coyoteEnabled.StateEntered += StartCoyoteTime;
_coyoteTimer.Timeout += CoyoteExpired;
_jump.StateEntered += Jump;
_doubleJump.StateEntered += DoubleJump;
_mantling.StateEntered += Mantle;
_dashing.StateEntered += OnDashStarted;
_weaponThrown.StateEntered += OnWeaponThrown;
}
///////////////////////////
// Input Management ///////
///////////////////////////
public void OnInputMove(Vector3 value)
{
_inputMove = value;
}
public void OnInputRotateY(float value)
{
_inputRotateY = value;
}
public void OnInputRotateFloorplane(float value)
{
_inputRotateFloorplane = value;
}
public void OnInputAimPressed()
{
_playerState.SendEvent("aim_pressed");
}
public void OnInputAimReleased()
{
_playerState.SendEvent("aim_released");
}
public void OnInputAimCanceled()
{
_playerState.SendEvent("aim_canceled");
DashSystem.CancelDash();
}
public void OnInputHitPressed()
{
_playerState.SendEvent("hit_pressed");
}
public void OnInputJumpPressed()
{
if (MoveSystem.CanMantle())
{
_playerState.SendEvent("mantle");
return;
}
_playerState.SendEvent("jump");
}
public void OnInputDropPressed()
{
_playerState.SendEvent("drop");
}
///////////////////////////
// Stateful logic /////////
///////////////////////////
// Jumping
public void StartCoyoteTime()
{
_coyoteTimer.Start();
}
public void CoyoteExpired()
{
_playerState.SendEvent("coyote_expired");
}
public void Jump()
{
_playerState.SendEvent("to_double_jump");
PerformJump(false);
}
public void DoubleJump()
{
_playerState.SendEvent("to_falling");
PerformJump(true);
}
private void PerformJump(bool isDoubleJump)
{
bool doesCapsuleHaveCrouchingHeight = CapsuleCollider.IsCrouchingHeight();
bool isPlayerDead = HealthSystem.IsDead();
if (!doesCapsuleHaveCrouchingHeight && !isPlayerDead)
MoveSystem.Jump(isDoubleJump);
}
// Mantling
public void Mantle()
{
var optionTween = MoveSystem.Mantle();
if (optionTween.IsSome(out var tween))
tween.Finished += MantleFinished;
}
public void MantleFinished()
{
_playerState.SendEvent("to_grounded");
}
// Dashing and weapon throwing
public void OnDashStarted()
{
if (WeaponSystem.FlyingState.Active)
@ -180,7 +292,6 @@ public partial class PlayerController : CharacterBody3D
_dashDirection = (DashSystem.DashResolve.DashLocation - GlobalPosition).Normalized();
DashSystem.Dash();
}
public void OnDashEnded()
{
// Regular dash
@ -211,7 +322,6 @@ public partial class PlayerController : CharacterBody3D
if (isPlantedOnWall)
{
MoveSystem.CanDoubleJump = true;
_playerState.SendEvent("dash_to_planted");
return; // In case states aren't exclusives
}
@ -219,7 +329,6 @@ public partial class PlayerController : CharacterBody3D
// Weapon planted anywhere else
_playerState.SendEvent("dash_ended");
}
public void OnWeaponThrown()
{
RemoveChild(WeaponRoot);
@ -232,91 +341,53 @@ public partial class PlayerController : CharacterBody3D
DashSystem.CancelDash();
WeaponSystem.ThrowWeapon(location, hasHit, collisionPoint, collisionNormal);
}
public void OnInputMove(Vector3 value)
{
_inputMove = value;
}
public void OnInputRotateY(float value)
// Regular processes
public void HandleWeaponInHand(float delta)
{
_inputRotateY = value;
RotateWeaponWithPlayer();
}
public void OnInputRotateFloorplane(float value)
public void HandleAiming(float delta)
{
_inputRotateFloorplane = value;
RotateWeaponWithPlayer();
DashSystem.PrepareDash();
}
public void OnInputAimPressed()
// Physics processes
public void HandleGrounded(float delta)
{
_playerState.SendEvent("aim_pressed");
if (!isOnFloorCustom())
_playerState.SendEvent("start_falling");
}
public void OnInputAimReleased()
public void HandleAirborne(float delta)
{
_playerState.SendEvent("aim_released");
}
public void OnInputAimCanceled()
{
_playerState.SendEvent("aim_canceled");
DashSystem.CancelDash();
if (isOnFloorCustom())
_playerState.SendEvent("grounded");
}
public void OnInputHitPressed()
///////////////////////////
// Stateless logic ////////
///////////////////////////
private void LookAround()
{
_playerState.SendEvent("hit_pressed");
Vector2 inputLookDir = new Vector2(_inputRotateY, _inputRotateFloorplane);
HeadSystem.LookAround(inputLookDir);
}
public void OnInputJumpPressed()
private void MoveAround(double delta)
{
_playerState.SendEvent("jump");
bool doesCapsuleHaveCrouchingHeight = CapsuleCollider.IsCrouchingHeight();
bool isPlayerDead = HealthSystem.IsDead();
if (!doesCapsuleHaveCrouchingHeight && !isPlayerDead)
MoveSystem.Jump(IsOnFloor());
}
public override void _PhysicsProcess(double delta)
{
var isPlayerDead = HealthSystem.IsDead();
var isHeadTouchingCeiling = IsHeadTouchingCeiling();
TweenQueueSystem.ProcessTweens();
if (_weaponInHand.Active || _aiming.Active)
WeaponRoot.SetRotation(HeadSystem.Rotation);
if (_aiming.Active)
DashSystem.PrepareDash();
var moveAroundParams = new MoveSystem.MoveAroundParameters(
delta,
_inputMove,
isOnFloorCustom(),
isPlayerDead,
isHeadTouchingCeiling,
HealthSystem.IsDead(),
IsHeadTouchingCeiling(),
_actionHanging.Active);
MoveSystem.MoveAround(moveAroundParams);
Vector2 inputLookDir = new Vector2(_inputRotateY, _inputRotateFloorplane);
HeadSystem.LookAround(inputLookDir);
Bobbing.CameraBobbingParams cameraBobbingParams = new Bobbing.CameraBobbingParams
{
Delta = (float)delta,
IsOnFloorCustom = isOnFloorCustom(),
Velocity = Velocity
};
Bobbing.PerformCameraBobbing(cameraBobbingParams);
}
FieldOfView.FovParameters fovParams = new FieldOfView.FovParameters
{
IsCrouchingHeight = CapsuleCollider.IsCrouchingHeight(),
Delta = (float)delta,
SprintSpeed = MoveSystem.SprintSpeed,
Velocity = Velocity
};
FieldOfView.PerformFovAdjustment(fovParams);
private void HandleStairs(float delta)
{
StairsSystem.UpStairsCheckParams upStairsCheckParams = new StairsSystem.UpStairsCheckParams
{
@ -331,11 +402,6 @@ public partial class PlayerController : CharacterBody3D
GlobalTransformFromDriver = GlobalTransform,
Rid = GetRid()
};
// TODO: SnapUpStairsCheck influences the ability of player to crouch because of `stepHeightY <= 0.01` part
// Ideally, it should not. SnapUpStairsCheck and SnapDownStairsCheck should be called, when player is actually
// on the stairs
StairsSystem.UpStairsCheckResult upStairsCheckResult = StairsSystem.SnapUpStairsCheck(upStairsCheckParams);
if (upStairsCheckResult.UpdateRequired)
@ -378,6 +444,29 @@ public partial class PlayerController : CharacterBody3D
StairsSystem.SlideCameraSmoothBackToOrigin(slideCameraParams);
}
private void CameraModifications(float delta)
{
Bobbing.CameraBobbingParams cameraBobbingParams = new Bobbing.CameraBobbingParams
{
Delta = delta,
IsOnFloorCustom = isOnFloorCustom(),
Velocity = Velocity
};
Bobbing.PerformCameraBobbing(cameraBobbingParams);
FieldOfView.FovParameters fovParams = new FieldOfView.FovParameters
{
IsCrouchingHeight = CapsuleCollider.IsCrouchingHeight(),
Delta = (float)delta,
SprintSpeed = MoveSystem.SprintSpeed,
Velocity = Velocity
};
FieldOfView.PerformFovAdjustment(fovParams);
}
///////////////////////////
// Helpers ////////////////
///////////////////////////
private bool IsHeadTouchingCeiling()
{
for (int i = 0; i < NUM_OF_HEAD_COLLISION_DETECTORS; i++)
@ -395,4 +484,22 @@ public partial class PlayerController : CharacterBody3D
{
return IsOnFloor() || StairsSystem.WasSnappedToStairsLastFrame();
}
public void RotateWeaponWithPlayer()
{
WeaponRoot.SetRotation(HeadSystem.Rotation);
}
///////////////////////////
// Processes //////////////
///////////////////////////
public override void _PhysicsProcess(double delta)
{
TweenQueueSystem.ProcessTweens();
LookAround();
MoveAround(delta);
CameraModifications((float) delta);
HandleStairs((float) delta);
}
}