Compare commits
3
Commits
af98795cae
...
eba1af5ff0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eba1af5ff0 | ||
|
|
76694a40f6 | ||
|
|
45c66ca7db |
@@ -61,6 +61,11 @@ toggle_console={
|
|||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
move_down={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
[physics]
|
[physics]
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public sealed class ClearCommand : ICommand
|
|||||||
{
|
{
|
||||||
public string Name => "clear";
|
public string Name => "clear";
|
||||||
public string Description => "Clears the console output.";
|
public string Description => "Clears the console output.";
|
||||||
public IReadOnlyList<CommandArgSpec> Args { get; } = new CommandArgsBuilder().Build();
|
public IReadOnlyList<CommandArgSpec> Args { get; } = [];
|
||||||
|
|
||||||
public CommandResult Execute(CommandArgValues args) => CommandResult.Ok(clearOutput: true);
|
public CommandResult Execute(CommandArgValues args) => CommandResult.Ok(clearOutput: true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public sealed class HelpCommand(ICommandRegistry registry) : ICommand
|
|||||||
{
|
{
|
||||||
public string Name => "help";
|
public string Name => "help";
|
||||||
public string Description => "Lists all available commands.";
|
public string Description => "Lists all available commands.";
|
||||||
public IReadOnlyList<CommandArgSpec> Args { get; } = new CommandArgsBuilder().Build();
|
public IReadOnlyList<CommandArgSpec> Args { get; } = [];
|
||||||
|
|
||||||
private static string FormatUsage(ICommand command)
|
private static string FormatUsage(ICommand command)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Game.Features.Console.Contracts;
|
||||||
|
using Game.Features.Players.Contracts;
|
||||||
|
|
||||||
|
namespace Game.Features.Console.Application.Commands;
|
||||||
|
|
||||||
|
public sealed class NoclipCommand(IMovementModeSwitcher movementModeSwitcher) : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "noclip";
|
||||||
|
public string Description => "Toggles noclip movement mode.";
|
||||||
|
public IReadOnlyList<CommandArgSpec> Args { get; } = [];
|
||||||
|
|
||||||
|
public CommandResult Execute(CommandArgValues args)
|
||||||
|
{
|
||||||
|
MovementMode next = movementModeSwitcher.ActiveMovementMode == MovementMode.Noclip ? MovementMode.Ground : MovementMode.Noclip;
|
||||||
|
movementModeSwitcher.SetMovementMode(next);
|
||||||
|
|
||||||
|
return CommandResult.Ok(next == MovementMode.Noclip ? "Noclip enabled." : "Noclip disabled.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://de2nlceiv1per
|
||||||
@@ -2,6 +2,15 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Game.Features.Console.Contracts;
|
namespace Game.Features.Console.Contracts;
|
||||||
|
|
||||||
|
public enum CommandArgType
|
||||||
|
{
|
||||||
|
String,
|
||||||
|
Int,
|
||||||
|
Float,
|
||||||
|
Bool,
|
||||||
|
Choice
|
||||||
|
}
|
||||||
|
|
||||||
public sealed record CommandArgSpec(string Name, CommandArgType Type, IReadOnlyList<string> Choices = null)
|
public sealed record CommandArgSpec(string Name, CommandArgType Type, IReadOnlyList<string> Choices = null)
|
||||||
{
|
{
|
||||||
public override string ToString() =>
|
public override string ToString() =>
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace Game.Features.Console.Contracts;
|
|
||||||
|
|
||||||
public enum CommandArgType
|
|
||||||
{
|
|
||||||
String,
|
|
||||||
Int,
|
|
||||||
Float,
|
|
||||||
Bool,
|
|
||||||
Choice
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
uid://dnt7hwlwqtdyq
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Game.Features.Players.Contracts;
|
||||||
|
|
||||||
|
public enum MovementMode
|
||||||
|
{
|
||||||
|
Ground,
|
||||||
|
Noclip
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IMovementModeSwitcher
|
||||||
|
{
|
||||||
|
MovementMode ActiveMovementMode { get; }
|
||||||
|
void SetMovementMode(MovementMode movementMode);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://c5nmjyb5iw6rh
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Game.Features.Players.Domain.Movement;
|
||||||
|
|
||||||
|
public sealed class GroundMovement(float moveSpeed, float jumpVelocity, float gravity) : IMovementStrategy
|
||||||
|
{
|
||||||
|
public Vector3 CalculateVelocity(Vector3 currentVelocity, MovementInput input)
|
||||||
|
{
|
||||||
|
Vector3 velocity = currentVelocity;
|
||||||
|
|
||||||
|
if (!input.IsOnFloor)
|
||||||
|
{
|
||||||
|
velocity.Y -= gravity * input.Delta;
|
||||||
|
}
|
||||||
|
else if (input.JumpPressed)
|
||||||
|
{
|
||||||
|
velocity.Y = jumpVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 moveDirection = input.Right * input.Direction.X + input.Back * input.Direction.Y;
|
||||||
|
Vector3 moveDirectionNormalized = moveDirection.Normalized();
|
||||||
|
velocity.X = moveDirectionNormalized.X * moveSpeed;
|
||||||
|
velocity.Z = moveDirectionNormalized.Z * moveSpeed;
|
||||||
|
|
||||||
|
return velocity;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://v4pc5k482usm
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Game.Features.Players.Domain.Movement;
|
||||||
|
|
||||||
|
public readonly record struct MovementInput(
|
||||||
|
Vector3 Right,
|
||||||
|
Vector3 Back,
|
||||||
|
Vector2 Direction,
|
||||||
|
bool IsOnFloor,
|
||||||
|
bool JumpPressed,
|
||||||
|
bool AscendPressed,
|
||||||
|
bool DescendPressed,
|
||||||
|
float Delta
|
||||||
|
);
|
||||||
|
|
||||||
|
public interface IMovementStrategy
|
||||||
|
{
|
||||||
|
Vector3 CalculateVelocity(Vector3 currentVelocity, MovementInput input);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://b3bcwkq278ojo
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Game.Features.Players.Domain.Movement;
|
||||||
|
|
||||||
|
public sealed class NoclipMovement(float flySpeed) : IMovementStrategy
|
||||||
|
{
|
||||||
|
public Vector3 CalculateVelocity(Vector3 currentVelocity, MovementInput input)
|
||||||
|
{
|
||||||
|
Vector3 moveDirection = input.Right * input.Direction.X + input.Back * input.Direction.Y;
|
||||||
|
|
||||||
|
if (input.AscendPressed)
|
||||||
|
{
|
||||||
|
moveDirection += Vector3.Up;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.DescendPressed)
|
||||||
|
{
|
||||||
|
moveDirection += Vector3.Down;
|
||||||
|
}
|
||||||
|
|
||||||
|
return moveDirection.Length() > 0f ? moveDirection.Normalized() * flySpeed : Vector3.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://c27csmmeunnld
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
using Godot;
|
|
||||||
|
|
||||||
namespace Game.Features.Players.Domain;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Per-frame input for movement calculation.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Right">World-space right direction of the body.</param>
|
|
||||||
/// <param name="Back">World-space back direction of the body.</param>
|
|
||||||
/// <param name="Direction">Movement input, X = left/right, Y = forward/back.</param>
|
|
||||||
/// <param name="IsOnFloor">Whether the body is currently grounded.</param>
|
|
||||||
/// <param name="JumpPressed">Whether jump was just pressed this frame.</param>
|
|
||||||
/// <param name="Delta">Physics frame delta time in seconds.</param>
|
|
||||||
public readonly record struct MovementInput(
|
|
||||||
Vector3 Right,
|
|
||||||
Vector3 Back,
|
|
||||||
Vector2 Direction,
|
|
||||||
bool IsOnFloor,
|
|
||||||
bool JumpPressed,
|
|
||||||
float Delta
|
|
||||||
);
|
|
||||||
|
|
||||||
public sealed class PlayerMovement(float moveSpeed, float jumpVelocity, float gravity)
|
|
||||||
{
|
|
||||||
public float MoveSpeed { get; } = moveSpeed;
|
|
||||||
public float JumpVelocity { get; } = jumpVelocity;
|
|
||||||
public float Gravity { get; } = gravity;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates next-frame velocity from input and ground state.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="currentVelocity">Body's velocity before this frame.</param>
|
|
||||||
/// <param name="input">Per-frame movement input.</param>
|
|
||||||
public Vector3 CalculateVelocity(Vector3 currentVelocity, MovementInput input)
|
|
||||||
{
|
|
||||||
Vector3 velocity = currentVelocity;
|
|
||||||
|
|
||||||
if (!input.IsOnFloor)
|
|
||||||
{
|
|
||||||
velocity.Y -= Gravity * input.Delta;
|
|
||||||
}
|
|
||||||
else if (input.JumpPressed)
|
|
||||||
{
|
|
||||||
velocity.Y = JumpVelocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 moveDirection = input.Right * input.Direction.X + input.Back * input.Direction.Y;
|
|
||||||
Vector3 moveDirectionNormalized = moveDirection.Normalized();
|
|
||||||
velocity.X = moveDirectionNormalized.X * MoveSpeed;
|
|
||||||
velocity.Z = moveDirectionNormalized.Z * MoveSpeed;
|
|
||||||
|
|
||||||
return velocity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
uid://chnyf8efw3xw2
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using System.Linq;
|
using System.Collections.Generic;
|
||||||
|
using Game.Features.Players.Contracts;
|
||||||
using Game.Features.Players.Domain;
|
using Game.Features.Players.Domain;
|
||||||
|
using Game.Features.Players.Domain.Movement;
|
||||||
using Game.Godot;
|
using Game.Godot;
|
||||||
using Game.Godot.Extensions;
|
using Game.Godot.Extensions;
|
||||||
using Game.Godot.Mappings;
|
using Game.Godot.Mappings;
|
||||||
@@ -7,26 +9,44 @@ using Godot;
|
|||||||
|
|
||||||
namespace Game.Features.Players.Godot;
|
namespace Game.Features.Players.Godot;
|
||||||
|
|
||||||
public partial class PlayerController : Node3D
|
public partial class PlayerController : Node3D, IMovementModeSwitcher
|
||||||
{
|
{
|
||||||
[Export] public CharacterBody3D Body;
|
[Export] public CharacterBody3D Body;
|
||||||
|
[Export] public CollisionShape3D Collision;
|
||||||
[Export] public Camera3D Camera;
|
[Export] public Camera3D Camera;
|
||||||
|
|
||||||
[Export] public float MoveSpeed = 4.0f;
|
[Export] public float MoveSpeed = 4.0f;
|
||||||
[Export] public float JumpVelocity = 4.5f;
|
[Export] public float JumpVelocity = 4.5f;
|
||||||
|
[Export] public float NoclipFlySpeed = 8.0f;
|
||||||
[Export] public float MouseSensitivity = 0.003f;
|
[Export] public float MouseSensitivity = 0.003f;
|
||||||
[Export] public float MaxLookAngleDegrees = 89.0f;
|
[Export] public float MaxLookAngleDegrees = 89.0f;
|
||||||
|
|
||||||
private PlayerMovement _movement;
|
private Dictionary<MovementMode, IMovementStrategy> _movementStrategies;
|
||||||
|
private IMovementStrategy _currentMovementStrategy;
|
||||||
private PlayerLook _look;
|
private PlayerLook _look;
|
||||||
|
|
||||||
|
public MovementMode ActiveMovementMode { get; private set; } = MovementMode.Ground;
|
||||||
|
|
||||||
|
public void SetMovementMode(MovementMode movementMode)
|
||||||
|
{
|
||||||
|
ActiveMovementMode = movementMode;
|
||||||
|
_currentMovementStrategy = _movementStrategies[movementMode];
|
||||||
|
Collision.Disabled = movementMode == MovementMode.Noclip;
|
||||||
|
}
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
this.AssertRequiredExports();
|
this.AssertRequiredExports();
|
||||||
|
|
||||||
float gravity = (float)ProjectSettings.GetSetting(ProjectSetting.Gravity.SettingPath());
|
|
||||||
_movement = new PlayerMovement(MoveSpeed, JumpVelocity, gravity);
|
|
||||||
_look = new PlayerLook(MouseSensitivity, MaxLookAngleDegrees);
|
_look = new PlayerLook(MouseSensitivity, MaxLookAngleDegrees);
|
||||||
|
|
||||||
|
float gravity = (float)ProjectSettings.GetSetting(ProjectSetting.Gravity.SettingPath());
|
||||||
|
_movementStrategies = new Dictionary<MovementMode, IMovementStrategy>
|
||||||
|
{
|
||||||
|
[MovementMode.Ground] = new GroundMovement(MoveSpeed, JumpVelocity, gravity),
|
||||||
|
[MovementMode.Noclip] = new NoclipMovement(NoclipFlySpeed)
|
||||||
|
};
|
||||||
|
_currentMovementStrategy = _movementStrategies[ActiveMovementMode];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _UnhandledInput(InputEvent @event)
|
public override void _UnhandledInput(InputEvent @event)
|
||||||
@@ -53,10 +73,12 @@ public partial class PlayerController : Node3D
|
|||||||
Direction: inputDirection,
|
Direction: inputDirection,
|
||||||
IsOnFloor: Body.IsOnFloor(),
|
IsOnFloor: Body.IsOnFloor(),
|
||||||
JumpPressed: Input.IsActionJustPressed(InputAction.Jump.ActionName()),
|
JumpPressed: Input.IsActionJustPressed(InputAction.Jump.ActionName()),
|
||||||
|
AscendPressed: Input.IsActionPressed(InputAction.Jump.ActionName()),
|
||||||
|
DescendPressed: Input.IsActionPressed(InputAction.MoveDown.ActionName()),
|
||||||
Delta: (float)delta
|
Delta: (float)delta
|
||||||
);
|
);
|
||||||
|
|
||||||
Body.Velocity = _movement.CalculateVelocity(Body.Velocity, input);
|
Body.Velocity = _currentMovementStrategy.CalculateVelocity(Body.Velocity, input);
|
||||||
Body.MoveAndSlide();
|
Body.MoveAndSlide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,9 +290,10 @@ _surfaces = [{
|
|||||||
}]
|
}]
|
||||||
blend_shape_mode = 0
|
blend_shape_mode = 0
|
||||||
|
|
||||||
[node name="Player" type="Node3D" unique_id=26823676 node_paths=PackedStringArray("Body", "Camera")]
|
[node name="Player" type="Node3D" unique_id=26823676 node_paths=PackedStringArray("Body", "Collision", "Camera")]
|
||||||
script = ExtResource("1_gkqfc")
|
script = ExtResource("1_gkqfc")
|
||||||
Body = NodePath("CharacterBody3D")
|
Body = NodePath("CharacterBody3D")
|
||||||
|
Collision = NodePath("CharacterBody3D/CollisionShape3D")
|
||||||
Camera = NodePath("CharacterBody3D/Camera3D")
|
Camera = NodePath("CharacterBody3D/Camera3D")
|
||||||
|
|
||||||
[node name="CharacterBody3D" type="CharacterBody3D" parent="." unique_id=1327672780]
|
[node name="CharacterBody3D" type="CharacterBody3D" parent="." unique_id=1327672780]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Game.Features.Console.Application.Commands;
|
using Game.Features.Console.Application.Commands;
|
||||||
using Game.Features.Console.Godot;
|
using Game.Features.Console.Godot;
|
||||||
using Game.Features.Console.Infrastructure;
|
using Game.Features.Console.Infrastructure;
|
||||||
|
using Game.Features.Players.Godot;
|
||||||
using Game.Godot.Extensions;
|
using Game.Godot.Extensions;
|
||||||
using Game.Godot.Mappings;
|
using Game.Godot.Mappings;
|
||||||
using Godot;
|
using Godot;
|
||||||
@@ -10,6 +11,7 @@ namespace Game.Godot;
|
|||||||
public partial class GameBootstrap : Node
|
public partial class GameBootstrap : Node
|
||||||
{
|
{
|
||||||
[Export] public ConsoleController Console;
|
[Export] public ConsoleController Console;
|
||||||
|
[Export] public PlayerController Player;
|
||||||
|
|
||||||
private void RegisterConsoleCommands()
|
private void RegisterConsoleCommands()
|
||||||
{
|
{
|
||||||
@@ -17,6 +19,7 @@ public partial class GameBootstrap : Node
|
|||||||
registry.Register(new HelpCommand(registry));
|
registry.Register(new HelpCommand(registry));
|
||||||
registry.Register(new EchoCommand());
|
registry.Register(new EchoCommand());
|
||||||
registry.Register(new ClearCommand());
|
registry.Register(new ClearCommand());
|
||||||
|
registry.Register(new NoclipCommand(Player));
|
||||||
|
|
||||||
Console.UseCommandRegistry(registry);
|
Console.UseCommandRegistry(registry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public enum InputAction
|
|||||||
MoveRight,
|
MoveRight,
|
||||||
MoveForward,
|
MoveForward,
|
||||||
MoveBack,
|
MoveBack,
|
||||||
|
MoveDown,
|
||||||
Jump,
|
Jump,
|
||||||
Cancel,
|
Cancel,
|
||||||
ToggleConsole
|
ToggleConsole
|
||||||
@@ -24,6 +25,7 @@ public static class InputActionExtensions
|
|||||||
InputAction.MoveRight => "move_right",
|
InputAction.MoveRight => "move_right",
|
||||||
InputAction.MoveForward => "move_forward",
|
InputAction.MoveForward => "move_forward",
|
||||||
InputAction.MoveBack => "move_back",
|
InputAction.MoveBack => "move_back",
|
||||||
|
InputAction.MoveDown => "move_down",
|
||||||
InputAction.Jump => "jump",
|
InputAction.Jump => "jump",
|
||||||
InputAction.Cancel => "ui_cancel",
|
InputAction.Cancel => "ui_cancel",
|
||||||
InputAction.ToggleConsole => "toggle_console",
|
InputAction.ToggleConsole => "toggle_console",
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ background_mode = 2
|
|||||||
sky = SubResource("Sky_1kpcd")
|
sky = SubResource("Sky_1kpcd")
|
||||||
glow_enabled = true
|
glow_enabled = true
|
||||||
|
|
||||||
[node name="GameWorld" type="Node" unique_id=222644234 node_paths=PackedStringArray("Console")]
|
[node name="GameWorld" type="Node" unique_id=222644234 node_paths=PackedStringArray("Console", "Player")]
|
||||||
script = ExtResource("3_bootstrap")
|
script = ExtResource("3_bootstrap")
|
||||||
Console = NodePath("Console")
|
Console = NodePath("Console")
|
||||||
|
Player = NodePath("Player")
|
||||||
|
|
||||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1669211403]
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1669211403]
|
||||||
transform = Transform3D(-0.8660254, -0.43301278, 0.25, 0, 0.49999997, 0.86602545, -0.50000006, 0.75, -0.43301266, 0, 0, 0)
|
transform = Transform3D(-0.8660254, -0.43301278, 0.25, 0, 0.49999997, 0.86602545, -0.50000006, 0.75, -0.43301266, 0, 0, 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user