Add Movement library integration with comprehensive test environment: include Movement.csproj reference, create LibraryTest scene with player controller, multiple test areas (bunny hop platforms, surf ramps, vertical tower, speed corridor), advanced lighting setup, input mappings for WASD/gamepad movement controls, and DebugHUD integration

This commit is contained in:
William Stuckey
2025-12-30 20:42:57 -06:00
parent 1b5a3aeeb3
commit 1c12ea3567
178 changed files with 4934 additions and 6 deletions

216
Scripts/Player/Player.cs Normal file
View File

@@ -0,0 +1,216 @@
using EinSoftworks.Events;
using EinSoftworks.Input;
using EinSoftworks.Movement;
using Godot;
namespace Voider.Player;
public partial class Player : FirstPersonController
{
[Export]
public Label SpeedVal { get; set; }
[Export]
public Label StateVal { get; set; }
[Export]
public Label PositionVal { get; set; }
[Export]
public Label VelocityVal { get; set; }
private float _lastSpeed = 0f;
public override void _Ready()
{
// InputManager is initialized by the library
InputManager = InputManager.Instance;
// Assign CameraNode from scene before calling base._Ready()
if (CameraNode == null)
{
CameraNode = GetNode<Node3D>("CameraMount");
}
// Set FOV and camera settings before base._Ready()
BaseFov = 75f;
MouseSensitivity = 0.35f;
ControllerSensitivity = 3.0f;
MinPitch = -89f;
MaxPitch = 89f;
EnableDebugOutput = true;
base._Ready();
// Try to find UI labels if they're not already assigned
// This allows the scene to work standalone or with external UI
if (SpeedVal == null || StateVal == null || PositionVal == null || VelocityVal == null)
{
// Try to find DebugHUD in the scene tree
var ui = GetNodeOrNull<Control>("/root/LibraryTest/UI/DebugHUD");
if (ui == null)
{
ui = GetNodeOrNull<Control>("/root/Main/UI/DebugHUD");
}
if (ui != null)
{
SpeedVal = ui.GetNodeOrNull<Label>("StatsPanel/VBoxContainerValues/SpeedVal");
StateVal = ui.GetNodeOrNull<Label>("StatsPanel/VBoxContainerValues/StateVal");
PositionVal = ui.GetNodeOrNull<Label>("StatsPanel/VBoxContainerValues/PositionVal");
VelocityVal = ui.GetNodeOrNull<Label>("StatsPanel/VBoxContainerValues/VelocityVal");
}
}
Config = new MovementConfig
{
// Base speeds (HL2-style)
MaxSpeed = 5f,
MaxSprintSpeed = 8f,
MaxWalkSpeed = 3f,
MaxCrouchSpeed = 2f,
Acceleration = 8f,
AirAcceleration = 1.5f,
AirSpeedCap = 8f,
AirControl = 0.3f,
Friction = 6f,
StopSpeed = 1f,
JumpVelocity = 5f,
Gravity = 15f,
// Bunny hopping with momentum preservation
EnableBunnyHopping = true,
EnableAutoHop = false,
BunnyHopSpeedMultiplier = 1.05f,
// Crouching
EnableCrouching = true,
CrouchHeight = 0.5f,
// Titanfall 2-style sliding
EnableSliding = true,
SlideSpeed = 10f,
SlideAcceleration = 15f,
SlideFriction = 3f,
SlideMinSpeed = 5f,
SlideJumpBoost = 1.2f,
SlideDuration = 1.2f,
EnableSurfing = true,
};
MouseSensitivity = 0.35f;
EnableDebugOutput = false;
GD.Print($"InputManager assigned: {InputManager != null}");
GD.Print($"CameraNode assigned: {CameraNode != null}");
GD.Print($"Config assigned: {Config != null}");
SubscribeToStateChanges(OnMovementStateChanged);
MovementEvents.PlayerJumped += OnPlayerJumped;
MovementEvents.SpeedChanged += OnSpeedChanged;
GD.Print("TestPlayer initialized with all libraries!");
GD.Print("- Movement: Enabled");
GD.Print("- Input: Enabled");
GD.Print("- Events: Enabled");
GD.Print("- StateManagement: Enabled");
GD.Print("- Camera: Enabled");
}
public override void _Process(double delta)
{
base._Process(delta);
UpdateUI();
}
private void UpdateUI()
{
if (SpeedVal != null)
{
SpeedVal.Text = $"{CurrentSpeed:F1} u/s ";
}
if (StateVal != null)
{
string state = "Unknown";
// Check crouch first since IsOnFloor() is unreliable when crouched
if (IsCrouching)
{
state = "Crouching";
}
else if (IsOnFloor())
{
if (WishDirection.LengthSquared() > 0.01f)
state = "Walking";
else
state = "Idle";
}
else
{
state = "Airborne";
}
StateVal.Text = $"{state} ";
}
if (PositionVal != null)
{
PositionVal.Text =
$"({GlobalPosition.X:F1}, {GlobalPosition.Y:F1}, {GlobalPosition.Z:F1}) ";
}
if (VelocityVal != null)
{
VelocityVal.Text = $"({Velocity.X:F1}, {Velocity.Y:F1}, {Velocity.Z:F1}) ";
}
}
private void OnMovementStateChanged(MovementStateChangedEvent evt)
{
GD.Print($"[Movement] State changed to: {evt.StateName} (Speed: {evt.Speed:F1})");
MovementEvents.RaiseStateChanged(evt.StateName);
}
private void OnPlayerJumped()
{
GD.Print($"[Event] Player jumped at speed: {CurrentSpeed:F1}");
}
private void OnSpeedChanged(float speed)
{
if (Mathf.Abs(speed - _lastSpeed) > 50f)
{
GD.Print($"[Event] Speed changed significantly: {_lastSpeed:F1} -> {speed:F1}");
_lastSpeed = speed;
}
}
public override void _Input(InputEvent @event)
{
base._Input(@event);
if (@event.IsActionPressed("ui_cancel"))
{
if (Godot.Input.MouseMode == Godot.Input.MouseModeEnum.Captured)
{
Godot.Input.MouseMode = Godot.Input.MouseModeEnum.Visible;
}
else
{
Godot.Input.MouseMode = Godot.Input.MouseModeEnum.Captured;
}
}
}
public override void _ExitTree()
{
MovementEvents.PlayerJumped -= OnPlayerJumped;
MovementEvents.SpeedChanged -= OnSpeedChanged;
base._ExitTree();
}
}

View File

@@ -0,0 +1 @@
uid://ckqlkvlsfck0d