refactor: movement system - MovementPreset, decouple abilities, fix timing

This commit is contained in:
2026-03-19 02:45:26 +01:00
parent adaeb35fdd
commit 814d9736d0
11 changed files with 58 additions and 95 deletions

View File

@@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using Godot; using Godot;
using Mr.BrickAdventures.scripts.components; using Mr.BrickAdventures.scripts.components;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts; namespace Mr.BrickAdventures.scripts;
@@ -8,6 +9,7 @@ namespace Mr.BrickAdventures.scripts;
public partial class PacXonLevel : Node public partial class PacXonLevel : Node
{ {
[Export] public PlayerController Player { get; set; } [Export] public PlayerController Player { get; set; }
[Export] public MovementPreset GridPreset { get; set; }
[Export] public PacXonGridManager GridManager { get; set; } [Export] public PacXonGridManager GridManager { get; set; }
[Export] public Node GhostContainer { get; set; } [Export] public Node GhostContainer { get; set; }
[Export] public Label PercentageLabel { get; set; } [Export] public Label PercentageLabel { get; set; }
@@ -17,8 +19,7 @@ public partial class PacXonLevel : Node
public override void _Ready() public override void _Ready()
{ {
var ghosts = GhostContainer.GetChildren().OfType<Node2D>().ToList(); var ghosts = GhostContainer.GetChildren().OfType<Node2D>().ToList();
Player.ClearMovementAbilities(); Player.ApplyPreset(GridPreset);
Player.SetGridMovement();
foreach (var ghost in ghosts) foreach (var ghost in ghosts)
{ {

View File

@@ -0,0 +1,9 @@
using Godot;
namespace Mr.BrickAdventures.scripts.Resources;
[GlobalClass]
public partial class MovementPreset : Resource
{
[Export] public PackedScene[] Abilities { get; set; } = [];
}

View File

@@ -5,26 +5,16 @@ namespace Mr.BrickAdventures.scripts.components;
[GlobalClass] [GlobalClass]
public partial class DoubleJumpAbility : MovementAbility public partial class DoubleJumpAbility : MovementAbility
{ {
[Export] public float JumpHeight { get; set; } = 100f;
[Export] public float JumpTimeToPeak { get; set; } = 0.5f;
private bool _hasDoubleJumped = false; private bool _hasDoubleJumped = false;
private float _jumpVelocity; private float _jumpVelocity;
public override void Initialize(PlayerController controller) public override void Initialize(PlayerController controller)
{ {
base.Initialize(controller); base.Initialize(controller);
_jumpVelocity = (2.0f * JumpHeight) / JumpTimeToPeak * -1.0f;
foreach (var ability in _controller.GetActiveAbilities())
{
if (ability is VariableJumpAbility jumpAbility)
{
_jumpVelocity = (2.0f * jumpAbility.JumpHeight) / jumpAbility.JumpTimeToPeak * -1.0f;
break;
}
}
if (_jumpVelocity == 0)
{
_jumpVelocity = -400.0f;
}
} }
public override Vector2 ProcessMovement(Vector2 velocity, double delta) public override Vector2 ProcessMovement(Vector2 velocity, double delta)

View File

@@ -5,8 +5,8 @@ namespace Mr.BrickAdventures.scripts.components;
[GlobalClass] [GlobalClass]
public partial class GravityAbility : MovementAbility public partial class GravityAbility : MovementAbility
{ {
public float AscendGravity { get; set; } [Export] public float AscendGravity { get; set; } = 980f;
public float DescendGravity { get; set; } [Export] public float DescendGravity { get; set; } = 1960f;
private float _gravity; private float _gravity;

View File

@@ -26,8 +26,6 @@ public partial class GridMovementAbility : MovementAbility
public override Vector2 ProcessMovement(Vector2 currentVelocity, double delta) public override Vector2 ProcessMovement(Vector2 currentVelocity, double delta)
{ {
GD.Print($"Player position: {_body.Position}, {_body.GlobalPosition}");
var inputDirection = _input.MoveDirection; var inputDirection = _input.MoveDirection;
var newDirection = Vector2.Zero; var newDirection = Vector2.Zero;

View File

@@ -30,8 +30,6 @@ public abstract partial class MovementAbility : Node
SetProcess(false); SetProcess(false);
SetPhysicsProcess(false); SetPhysicsProcess(false);
} }
_body.Velocity = Vector2.Zero;
} }
public abstract Vector2 ProcessMovement(Vector2 currentVelocity, double delta); public abstract Vector2 ProcessMovement(Vector2 currentVelocity, double delta);

View File

@@ -13,7 +13,7 @@ public partial class PlayerInputHandler : Node
public bool DownReleased { get; private set; } public bool DownReleased { get; private set; }
public bool DownHeld { get; private set; } public bool DownHeld { get; private set; }
public override void _Process(double delta) public override void _PhysicsProcess(double delta)
{ {
MoveDirection = Input.GetVector("left", "right", "up", "down"); MoveDirection = Input.GetVector("left", "right", "up", "down");

View File

@@ -12,14 +12,21 @@ public partial class WallJumpAbility : MovementAbility
[Export(PropertyHint.Range, "0.0, 1.0, 0.05")] public float WallSlideGravityMultiplier { get; set; } = 0.7f; [Export(PropertyHint.Range, "0.0, 1.0, 0.05")] public float WallSlideGravityMultiplier { get; set; } = 0.7f;
[Export] public float MaxWallSlideSpeed { get; set; } = 150.0f; [Export] public float MaxWallSlideSpeed { get; set; } = 150.0f;
private float _gravity;
public override void Initialize(PlayerController controller)
{
base.Initialize(controller);
_gravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity");
}
public override Vector2 ProcessMovement(Vector2 velocity, double delta) public override Vector2 ProcessMovement(Vector2 velocity, double delta)
{ {
var isOnWall = _body.IsOnWall(); var isOnWall = _body.IsOnWall();
if (isOnWall && !_body.IsOnFloor() && velocity.Y > 0f) if (isOnWall && !_body.IsOnFloor() && velocity.Y > 0f)
{ {
var gravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity"); var newYVelocity = velocity.Y + _gravity * WallSlideGravityMultiplier * (float)delta;
var newYVelocity = velocity.Y + gravity * WallSlideGravityMultiplier * (float)delta;
velocity.Y = Mathf.Min(newYVelocity, MaxWallSlideSpeed); velocity.Y = Mathf.Min(newYVelocity, MaxWallSlideSpeed);
} }

View File

@@ -1,8 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Godot; using Godot;
using Mr.BrickAdventures.Autoloads; using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components; namespace Mr.BrickAdventures.scripts.components;
@@ -10,15 +9,7 @@ namespace Mr.BrickAdventures.scripts.components;
public partial class PlayerController : CharacterBody2D public partial class PlayerController : CharacterBody2D
{ {
[Export] private Node MovementAbilitiesContainer { get; set; } [Export] private Node MovementAbilitiesContainer { get; set; }
[Export] public MovementPreset DefaultPreset { get; set; }
[ExportGroup("Movement Ability Scenes")]
[Export] public PackedScene GroundMovementScene { get; set; }
[Export] public PackedScene JumpMovementScene { get; set; }
[Export] public PackedScene GravityScene { get; set; }
[Export] public PackedScene OneWayPlatformScene { get; set; }
[Export] public PackedScene SpaceshipMovementScene { get; set; }
[Export] public PackedScene WallJumpScene { get; set; }
[Export] public PackedScene GridMovementScene { get; set; }
[Signal] public delegate void JumpInitiatedEventHandler(); [Signal] public delegate void JumpInitiatedEventHandler();
[Signal] public delegate void MovementAbilitiesChangedEventHandler(); [Signal] public delegate void MovementAbilitiesChangedEventHandler();
@@ -48,16 +39,10 @@ public partial class PlayerController : CharacterBody2D
} }
_inputHandler = GetNode<PlayerInputHandler>("PlayerInputHandler"); _inputHandler = GetNode<PlayerInputHandler>("PlayerInputHandler");
foreach (var child in MovementAbilitiesContainer.GetChildren())
{
if (child is MovementAbility ability)
{
ability.Initialize(this);
_abilities.Add(ability);
}
}
_ = ConnectJumpAndGravityAbilities(); if (DefaultPreset != null)
ApplyPreset(DefaultPreset);
EmitSignalMovementAbilitiesChanged(); EmitSignalMovementAbilitiesChanged();
EventBus.EmitPlayerSpawned(this); EventBus.EmitPlayerSpawned(this);
} }
@@ -81,6 +66,19 @@ public partial class PlayerController : CharacterBody2D
MoveAndSlide(); MoveAndSlide();
} }
public void ApplyPreset(MovementPreset preset)
{
if (preset == null) return;
ClearMovementAbilities();
Velocity = Vector2.Zero;
foreach (var scene in preset.Abilities)
{
if (scene != null)
AddAbility(scene.Instantiate<MovementAbility>());
}
EmitSignalMovementAbilitiesChanged();
}
public void AddAbility(MovementAbility ability) public void AddAbility(MovementAbility ability)
{ {
MovementAbilitiesContainer.AddChild(ability); MovementAbilitiesContainer.AddChild(ability);
@@ -110,46 +108,4 @@ public partial class PlayerController : CharacterBody2D
} }
} }
} }
public void SetPlatformMovement()
{
ClearMovementAbilities();
if (GroundMovementScene != null) AddAbility(GroundMovementScene.Instantiate<MovementAbility>());
if (JumpMovementScene != null) AddAbility(JumpMovementScene.Instantiate<MovementAbility>());
if (GravityScene != null) AddAbility(GravityScene.Instantiate<MovementAbility>());
if (OneWayPlatformScene != null) AddAbility(OneWayPlatformScene.Instantiate<MovementAbility>());
_ = ConnectJumpAndGravityAbilities();
EmitSignalMovementAbilitiesChanged();
}
public void SetSpaceshipMovement()
{
ClearMovementAbilities();
if (SpaceshipMovementScene != null) AddAbility(SpaceshipMovementScene.Instantiate<MovementAbility>());
EmitSignalMovementAbilitiesChanged();
}
public void SetGridMovement()
{
ClearMovementAbilities();
if (GridMovementScene != null) AddAbility(GridMovementScene.Instantiate<MovementAbility>());
EmitSignalMovementAbilitiesChanged();
}
private async Task ConnectJumpAndGravityAbilities()
{
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
var jumpAbility = _abilities.OfType<VariableJumpAbility>().FirstOrDefault();
var gravityAbility = _abilities.OfType<GravityAbility>().FirstOrDefault();
if (jumpAbility != null && gravityAbility != null)
{
gravityAbility.AscendGravity = jumpAbility.AscendGravity;
gravityAbility.DescendGravity = jumpAbility.DescendGravity;
}
}
} }

View File

@@ -1,10 +1,12 @@
using Godot; using Godot;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components; namespace Mr.BrickAdventures.scripts.components;
[GlobalClass] [GlobalClass]
public partial class SpaceshipEnterComponent : Area2D public partial class SpaceshipEnterComponent : Area2D
{ {
[Export] public MovementPreset Preset { get; set; }
[Signal] public delegate void SpaceshipEnteredEventHandler(); [Signal] public delegate void SpaceshipEnteredEventHandler();
public override void _Ready() public override void _Ready()
@@ -15,7 +17,7 @@ public partial class SpaceshipEnterComponent : Area2D
private void OnBodyEntered(Node2D body) private void OnBodyEntered(Node2D body)
{ {
if (body is not PlayerController player) return; if (body is not PlayerController player) return;
player.SetSpaceshipMovement(); player.ApplyPreset(Preset);
EmitSignalSpaceshipEntered(); EmitSignalSpaceshipEntered();
QueueFree(); QueueFree();
} }

View File

@@ -1,10 +1,12 @@
using Godot; using Godot;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components; namespace Mr.BrickAdventures.scripts.components;
[GlobalClass] [GlobalClass]
public partial class SpaceshipExitComponent : Area2D public partial class SpaceshipExitComponent : Area2D
{ {
[Export] public MovementPreset Preset { get; set; }
[Signal] public delegate void SpaceshipExitEventHandler(); [Signal] public delegate void SpaceshipExitEventHandler();
public override void _Ready() public override void _Ready()
@@ -16,6 +18,6 @@ public partial class SpaceshipExitComponent : Area2D
{ {
if (body is not PlayerController player) return; if (body is not PlayerController player) return;
EmitSignalSpaceshipExit(); EmitSignalSpaceshipExit();
player.SetPlatformMovement(); player.ApplyPreset(Preset);
} }
} }