cirnogodot/Scripts/Components/FSM/3DPlayer/IsoMovementModule.cs

147 lines
4.9 KiB
C#
Raw Normal View History

2025-06-10 16:33:43 +02:00
using Cirno.Scripts.Components.Actors;
2025-06-26 15:38:28 +02:00
using Cirno.Scripts.Components.Actors._3D;
2025-06-29 12:35:19 +02:00
using Cirno.Scripts.Utils;
2025-06-10 16:33:43 +02:00
using Godot;
2025-06-29 12:35:19 +02:00
using GodotPlugins.Game;
2025-06-10 16:33:43 +02:00
namespace Cirno.Scripts.Components.FSM._3DPlayer;
public partial class IsoMovementModule : ModuleBase<PlayerState, CharacterBody3D>
{
2025-08-14 18:11:41 +02:00
[Export] public IsoPlayerStorageModule PlayerStorage { get; private set; }
2025-06-10 16:33:43 +02:00
[Export] private InputProvider _inputProvider;
2025-08-14 18:11:41 +02:00
2025-06-26 15:38:28 +02:00
[Export] public PlayerHitboxSpriteProvider3D HitboxSpriteProvider { get; private set; }
2025-08-14 18:11:41 +02:00
2025-06-10 16:33:43 +02:00
[Export] public int Speed { get; set; } = 45;
[Export] public int StrafeSpeed { get; set; } = 35;
[Export] public float Acceleration = 8f;
[Export] public float Deceleration = 8f;
2025-06-11 15:28:26 +02:00
[Export] public float Gravity = -9.8f;
[Export] public float FallSpeed = 20f;
2025-08-14 18:11:41 +02:00
[Export] public float TurnResistance { get; set; } = 0.5f;
2025-06-10 16:33:43 +02:00
private bool _isStrafing;
private float _accelerationPerSecond;
private float _decelerationPerSecond;
public int MovementSpeed => _isStrafing ? StrafeSpeed : Speed;
private IStateMachine<PlayerState, CharacterBody3D> _stateMachine;
private CharacterBody3D MainObject => _stateMachine.MainObject;
2025-08-14 18:11:41 +02:00
2025-06-10 16:33:43 +02:00
public override void EnterState(PlayerState state)
{
_accelerationPerSecond = Speed / Acceleration;
_decelerationPerSecond = Speed / Deceleration;
}
public override void ExitState(PlayerState state)
{
}
public override void Init(IStateMachine<PlayerState, CharacterBody3D> machine)
{
_stateMachine = machine;
}
public override void Process(double delta)
{
2025-06-11 15:28:26 +02:00
var movementInput = _inputProvider.GetMovementInput();
2025-08-14 18:11:41 +02:00
2025-06-11 15:28:26 +02:00
_isStrafing = _inputProvider.GetStrafePressed();
var rightStickInput = _inputProvider.GetAimInput().Normalized();
2025-08-14 18:11:41 +02:00
2025-06-11 15:28:26 +02:00
// Update Facing Direction
// if (!_isStrafing)
// {
if (rightStickInput.Length() > 0.1f) // If the right stick is moved
{
PlayerStorage.FacingDirection = rightStickInput;
2025-06-18 18:09:30 +02:00
PlayerStorage.AimingDirection = rightStickInput;
2025-06-11 15:28:26 +02:00
}
else if (movementInput != Vector2.Zero) // Fall back to movement direction
{
PlayerStorage.FacingDirection = movementInput;
2025-06-18 18:09:30 +02:00
PlayerStorage.AimingDirection = rightStickInput;
2025-06-11 15:28:26 +02:00
}
// }
var rotatedMovementDirection = movementInput.Rotated(Mathf.DegToRad(-45f));
2025-08-14 18:11:41 +02:00
2025-06-29 12:35:19 +02:00
//PlayerStorage.MovementDirection = new Vector3(rotatedMovementDirection.X, 0, rotatedMovementDirection.Y);
PlayerStorage.MovementDirection = rotatedMovementDirection;
2025-06-26 15:38:28 +02:00
HitboxSpriteProvider.SetVisibility(_isStrafing);
2025-06-10 16:33:43 +02:00
}
2025-08-14 17:50:11 +02:00
private Vector2 InterpolateVelocityAxes(
Vector2 current, Vector2 target, float accel, float decel, float delta)
{
2025-08-14 18:11:41 +02:00
float newX = current.X;
float newY = current.Y;
// --- X Axis ---
if (Mathf.Sign(target.X) == Mathf.Sign(current.X) || Mathf.IsZeroApprox(current.X))
{
// Same direction or stopped → normal accel/decel
float step = (Mathf.Abs(target.X) > Mathf.Abs(current.X)) ? accel : decel;
newX = Mathf.MoveToward(current.X, target.X, step * delta);
}
else
{
// Opposite direction → apply resistance multiplier to deceleration
float slowStep = decel * TurnResistance;
newX = Mathf.MoveToward(current.X, 0, slowStep * delta);
// Only start accelerating toward target if we've nearly stopped
if (Mathf.IsZeroApprox(newX))
newX = Mathf.MoveToward(newX, target.X, accel * delta);
}
// --- Y Axis ---
if (Mathf.Sign(target.Y) == Mathf.Sign(current.Y) || Mathf.IsZeroApprox(current.Y))
{
float step = (Mathf.Abs(target.Y) > Mathf.Abs(current.Y)) ? accel : decel;
newY = Mathf.MoveToward(current.Y, target.Y, step * delta);
}
else
{
float slowStep = decel * TurnResistance;
newY = Mathf.MoveToward(current.Y, 0, slowStep * delta);
if (Mathf.IsZeroApprox(newY))
newY = Mathf.MoveToward(newY, target.Y, accel * delta);
}
2025-08-14 17:50:11 +02:00
return new Vector2(newX, newY);
}
2025-06-10 16:33:43 +02:00
public override void PhysicsProcess(double delta)
{
2025-08-14 17:50:11 +02:00
var v3 = MainObject.Velocity;
Vector2 v = v3.ToVector2();
float dt = (float)delta;
Vector2 inputDir = PlayerStorage.MovementDirection;
2025-08-14 18:11:41 +02:00
if (inputDir.Length() > 1f)
inputDir = inputDir.Normalized();
2025-08-14 17:50:11 +02:00
2025-06-10 16:33:43 +02:00
if (_isStrafing)
{
2025-08-14 17:50:11 +02:00
v = inputDir * StrafeSpeed;
2025-06-10 16:33:43 +02:00
}
else
{
2025-08-14 17:50:11 +02:00
Vector2 targetVel = inputDir * Speed;
v = InterpolateVelocityAxes(v, targetVel, Acceleration, Deceleration, dt);
2025-06-10 16:33:43 +02:00
}
2025-08-14 17:50:11 +02:00
float vy = Mathf.Clamp(v3.Y + Gravity * dt, -FallSpeed, FallSpeed);
if (Input.IsKeyLabelPressed(Key.Z)) vy = 10;
2025-06-11 15:28:26 +02:00
2025-08-14 17:50:11 +02:00
MainObject.Velocity = v.ToVector3(vy);
2025-06-10 16:33:43 +02:00
MainObject.MoveAndSlide();
}
}