refacto: moved systems from player controller physics process to their own signal based systems

This commit is contained in:
2025-06-02 17:58:40 +02:00
parent c3e2c974ca
commit 178553956d
13 changed files with 377 additions and 248 deletions

View File

@ -12,6 +12,7 @@ extends Node3D
@export var aim_pressed:GUIDEAction
@export var aim_released:GUIDEAction
@export var aim_canceled:GUIDEAction
@export var jump:GUIDEAction
signal input_move(value: Vector3)
signal input_rotate_y(value: float)
@ -20,6 +21,7 @@ signal input_rotate_floorplane(value: float)
signal input_aim_pressed
signal input_aim_released
signal input_aim_canceled
signal input_jump
func _ready() -> void:
GUIDE.enable_mapping_context(base_mode)
@ -27,8 +29,12 @@ func _ready() -> void:
aim_pressed.triggered.connect(on_input_aim_pressed)
aim_released.triggered.connect(on_input_aim_released)
aim_canceled.triggered.connect(on_input_aim_canceled)
jump.triggered.connect(on_input_jump)
func on_input_jump():
input_jump.emit()
func on_input_aim_pressed():
input_aim_pressed.emit()

View File

@ -1,4 +1,4 @@
[gd_resource type="Resource" script_class="GUIDEMappingContext" load_steps=53 format=3 uid="uid://bl5crtu1gkrtr"]
[gd_resource type="Resource" script_class="GUIDEMappingContext" load_steps=58 format=3 uid="uid://bl5crtu1gkrtr"]
[ext_resource type="Script" uid="uid://cpplm41b5bt6m" path="res://addons/guide/guide_action_mapping.gd" id="1_0pi3k"]
[ext_resource type="Script" uid="uid://dsa1dnifd6w32" path="res://addons/guide/guide_mapping_context.gd" id="2_ho3ad"]
@ -22,6 +22,7 @@
[ext_resource type="Script" uid="uid://rvttn472ix6v" path="res://addons/guide/inputs/guide_input_joy_button.gd" id="19_2murt"]
[ext_resource type="Script" uid="uid://brsxcrai2te83" path="res://addons/guide/triggers/guide_trigger_chorded_action.gd" id="20_xcfo4"]
[ext_resource type="Script" uid="uid://b52rqq28tuqpg" path="res://addons/guide/triggers/guide_trigger_pressed.gd" id="21_k8ji4"]
[ext_resource type="Resource" uid="uid://bdit2jy5gbpts" path="res://systems/inputs/walk_mode/jump.tres" id="22_ufouq"]
[sub_resource type="Resource" id="Resource_vkvga"]
script = ExtResource("4_oapce")
@ -52,6 +53,7 @@ triggers = Array[ExtResource("8_nf3uo")]([])
script = ExtResource("1_0pi3k")
action = ExtResource("2_p4e1v")
input_mappings = Array[ExtResource("3_ufouq")]([SubResource("Resource_1igva")])
metadata/_guide_input_mappings_collapsed = false
[sub_resource type="Resource" id="Resource_05q5j"]
script = ExtResource("10_500v3")
@ -88,6 +90,7 @@ triggers = Array[ExtResource("8_nf3uo")]([])
script = ExtResource("1_0pi3k")
action = ExtResource("9_paxxe")
input_mappings = Array[ExtResource("3_ufouq")]([SubResource("Resource_dew8i")])
metadata/_guide_input_mappings_collapsed = true
[sub_resource type="Resource" id="Resource_pf0ii"]
script = ExtResource("10_500v3")
@ -124,8 +127,9 @@ triggers = Array[ExtResource("8_nf3uo")]([])
script = ExtResource("1_0pi3k")
action = ExtResource("13_3y0c4")
input_mappings = Array[ExtResource("3_ufouq")]([SubResource("Resource_qu2wi")])
metadata/_guide_input_mappings_collapsed = true
[sub_resource type="Resource" id="Resource_lg6ir"]
[sub_resource type="Resource" id="Resource_ufouq"]
script = ExtResource("10_500v3")
axis = 4
joy_index = -1
@ -140,7 +144,7 @@ override_action_settings = false
is_remappable = false
display_name = ""
display_category = ""
input = SubResource("Resource_lg6ir")
input = SubResource("Resource_ufouq")
modifiers = Array[ExtResource("5_j3mg7")]([])
triggers = Array[ExtResource("8_nf3uo")]([SubResource("Resource_n42ky")])
@ -148,6 +152,7 @@ triggers = Array[ExtResource("8_nf3uo")]([SubResource("Resource_n42ky")])
script = ExtResource("1_0pi3k")
action = ExtResource("14_bi271")
input_mappings = Array[ExtResource("3_ufouq")]([SubResource("Resource_qbthx")])
metadata/_guide_input_mappings_collapsed = false
[sub_resource type="Resource" id="Resource_cqc4k"]
script = ExtResource("10_500v3")
@ -172,6 +177,7 @@ triggers = Array[ExtResource("8_nf3uo")]([SubResource("Resource_vanwy")])
script = ExtResource("1_0pi3k")
action = ExtResource("16_34gm1")
input_mappings = Array[ExtResource("3_ufouq")]([SubResource("Resource_bkx7d")])
metadata/_guide_input_mappings_collapsed = true
[sub_resource type="Resource" id="Resource_lfx76"]
script = ExtResource("19_2murt")
@ -202,8 +208,33 @@ script = ExtResource("1_0pi3k")
action = ExtResource("18_4dlli")
input_mappings = Array[ExtResource("3_ufouq")]([SubResource("Resource_4ee3d")])
[sub_resource type="Resource" id="Resource_oapce"]
script = ExtResource("19_2murt")
button = 0
joy_index = -1
[sub_resource type="Resource" id="Resource_j3mg7"]
script = ExtResource("21_k8ji4")
actuation_threshold = 0.5
[sub_resource type="Resource" id="Resource_8w5gu"]
script = ExtResource("3_ufouq")
override_action_settings = false
is_remappable = false
display_name = ""
display_category = ""
input = SubResource("Resource_oapce")
modifiers = Array[ExtResource("5_j3mg7")]([])
triggers = Array[ExtResource("8_nf3uo")]([SubResource("Resource_j3mg7")])
metadata/_guide_triggers_collapsed = false
[sub_resource type="Resource" id="Resource_xt1x5"]
script = ExtResource("1_0pi3k")
action = ExtResource("22_ufouq")
input_mappings = Array[ExtResource("3_ufouq")]([SubResource("Resource_8w5gu")])
[resource]
script = ExtResource("2_ho3ad")
display_name = ""
mappings = Array[ExtResource("1_0pi3k")]([SubResource("Resource_88x08"), SubResource("Resource_tgr2g"), SubResource("Resource_iarn8"), SubResource("Resource_0hmrk"), SubResource("Resource_iihs4"), SubResource("Resource_0s4kt")])
mappings = Array[ExtResource("1_0pi3k")]([SubResource("Resource_88x08"), SubResource("Resource_tgr2g"), SubResource("Resource_iarn8"), SubResource("Resource_0hmrk"), SubResource("Resource_iihs4"), SubResource("Resource_0s4kt"), SubResource("Resource_xt1x5")])
metadata/_custom_type_script = "uid://dsa1dnifd6w32"

View File

@ -0,0 +1,14 @@
[gd_resource type="Resource" script_class="GUIDEAction" load_steps=2 format=3 uid="uid://bdit2jy5gbpts"]
[ext_resource type="Script" uid="uid://cluhc11vixkf1" path="res://addons/guide/guide_action.gd" id="1_pxv2l"]
[resource]
script = ExtResource("1_pxv2l")
name = &""
action_value_type = 0
block_lower_priority_actions = true
emit_as_godot_actions = false
is_remappable = false
display_name = ""
display_category = ""
metadata/_custom_type_script = "uid://cluhc11vixkf1"

View File

@ -17,6 +17,8 @@ public partial class MantleSystem: Node3D
private ShapeCast3D _wallInFrontCast3D;
private ShapeCast3D _mantleCast3D;
private RayCast3D _mantleCheckCast3D;
private Option<Vector3> _mantleLocation;
public void Init(Node3D head)
{
@ -25,21 +27,29 @@ public partial class MantleSystem: Node3D
_mantleCast3D = GetNode<ShapeCast3D>("MantleCast3D");
}
public Option<Vector3> FindMantleInFrontOfPlayer()
public override void _PhysicsProcess(double delta)
{
base._PhysicsProcess(delta);
_wallInFrontCast3D.SetRotation(new Vector3(
_wallInFrontCast3D.Rotation.X,
_head.Rotation.Y,
_wallInFrontCast3D.Rotation.Z));
if (!_wallInFrontCast3D.IsColliding())
{
return Option<Vector3>.None;
_mantleLocation = Option<Vector3>.None;
return;
}
var collisionPoint = _wallInFrontCast3D.GetCollisionPoint(0);
var collisionNormal = _wallInFrontCast3D.GetCollisionNormal(0);
return FindMantleLocationAtPoint(collisionPoint, collisionNormal);
_mantleLocation = FindMantleLocationAtPoint(collisionPoint, collisionNormal);
}
public Option<Vector3> FindMantleInFrontOfPlayer()
{
return _mantleLocation;
}
public Option<Vector3> FindMantleLocationAtPoint(Vector3 point, Vector3 wallNormal)

View File

@ -22,7 +22,7 @@ debug_shape_custom_color = Color(1, 0, 0, 1)
[node name="WallInFrontCast3D" type="ShapeCast3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("CapsuleShape3D_qu4wy")
target_position = Vector3(0, 0, -1.5)
target_position = Vector3(0, 0, -2)
max_results = 1
collision_mask = 2
debug_shape_custom_color = Color(0.911631, 0.11884, 0.656218, 1)

184
systems/move/MoveSystem.cs Normal file
View File

@ -0,0 +1,184 @@
using Godot;
using PolarBears.PlayerControllerAddon;
public partial class MoveSystem : Node3D
{
public record MoveSystemParameters(
CharacterBody3D Parent,
Gravity Gravity,
MantleSystem MantleSystem,
TweenQueueSystem TweenQueueSystem,
HeadSystem HeadSystem,
CapsuleCollider CapsuleCollider);
[Export(PropertyHint.Range, "0,20,0.1,or_greater")]
public float WalkSpeed { get; set; } = 5.0f;
[Export(PropertyHint.Range, "0,20,0.1,or_greater")]
public float SprintSpeed { get; set; } = 7.2f;
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
public float CrouchSpeed { get; set; } = 2.5f;
[Export(PropertyHint.Range, "0,100,0.1,or_greater")]
public float _currentSpeed;
private const float DecelerationSpeedFactorFloor = 15.0f;
private const float DecelerationSpeedFactorAir = 7.0f;
public float CrouchTransitionSpeed { get; set; } = 20.0f;
[Export(PropertyHint.Range, "0,5,0.1,or_greater")]
public float DoubleJumpSpeedFactor { get; set; } = 2f;
private bool _canDoubleJump = true;
private float _lastFrameWasOnFloor = -Mathf.Inf;
private Gravity _gravity;
private CharacterBody3D _parent;
private MantleSystem _mantleSystem;
private TweenQueueSystem _tweenQueueSystem;
private CapsuleCollider _capsuleCollider;
private HeadSystem _headSystem;
public void Init(MoveSystemParameters parameters)
{
_parent = parameters.Parent;
_gravity = parameters.Gravity;
_mantleSystem = parameters.MantleSystem;
_tweenQueueSystem = parameters.TweenQueueSystem;
_capsuleCollider = parameters.CapsuleCollider;
_headSystem = parameters.HeadSystem;
_currentSpeed = WalkSpeed;
}
public void MoveAround(double delta, Vector3 movementDirection, bool isOnFloor, bool isDead, bool isHeadTouchingCeiling)
{
var doesCapsuleHaveCrouchingHeight = _capsuleCollider.IsCrouchingHeight();
var doesCapsuleHaveDefaultHeight = _capsuleCollider.IsDefaultHeight();
// Adding the gravity
if (!isOnFloor)
{
_parent.Velocity = new Vector3(
x: _parent.Velocity.X,
y: _parent.Velocity.Y - (_gravity.CalculateGravityForce() * (float)delta),
z: _parent.Velocity.Z);
}
if (isOnFloor)
{
_lastFrameWasOnFloor = Engine.GetPhysicsFrames();
_canDoubleJump = true;
}
// The code below is required to quickly adjust player's position on Y-axis when there's a ceiling on the
// trajectory of player's jump and player is standing
if (isHeadTouchingCeiling && doesCapsuleHaveDefaultHeight)
{
_parent.Velocity = new Vector3(
x: _parent.Velocity.X,
y: _parent.Velocity.Y - 2.0f,
z: _parent.Velocity.Z);
}
if (!isDead)
{
// Used both for detecting the moment when we enter into crouching mode and the moment when we're already
// in the crouching mode
if (Input.IsActionPressed("crouch") ||
(doesCapsuleHaveCrouchingHeight && isHeadTouchingCeiling))
{
_capsuleCollider.Crouch((float)delta, CrouchTransitionSpeed);
_currentSpeed = CrouchSpeed;
}
// Used both for the moment when we exit the crouching mode and for the moment when we just walk
else
{
_capsuleCollider.UndoCrouching((float)delta, CrouchTransitionSpeed);
_currentSpeed = WalkSpeed;
}
}
// Each component of the boolean statement for sprinting is required
if (Input.IsActionPressed("sprint") && !isHeadTouchingCeiling &&
!doesCapsuleHaveCrouchingHeight && !isDead)
{
_currentSpeed = SprintSpeed;
}
// Basis is a 3x4 matrix. It contains information about scaling and rotation of head.
// By multiplying our Vector3 by this matrix we're doing multiple things:
// a) We start to operate in global space;
// b) We're applying to Vector3 the current rotation of "head" object;
// c) We're applying to Vector3 the current scaling of "head" object;
Vector3 direction = _headSystem.Transform.Basis * movementDirection;
if (isDead)
{
direction = Vector3.Zero;
}
if (isOnFloor)
{
// Set velocity based on input direction when on the floor
if (direction.Length() > 0)
{
float newX = direction.X * _currentSpeed;
float newZ = direction.Z * _currentSpeed;
_parent.Velocity = new Vector3(newX, _parent.Velocity.Y, newZ);
}
// If there is no input, smoothly decelerate the character on the floor
else
{
float xDeceleration = Mathf.Lerp(_parent.Velocity.X, direction.X * _currentSpeed,
(float)delta * DecelerationSpeedFactorFloor);
float zDeceleration = Mathf.Lerp(_parent.Velocity.Z, direction.Z * _currentSpeed,
(float)delta * DecelerationSpeedFactorFloor);
_parent.Velocity = new Vector3(xDeceleration, _parent.Velocity.Y, zDeceleration);
}
}
else
{
float xDeceleration = Mathf.Lerp(_parent.Velocity.X, direction.X * _currentSpeed,
(float)delta * DecelerationSpeedFactorAir);
float zDeceleration = Mathf.Lerp(_parent.Velocity.Z, direction.Z * _currentSpeed,
(float)delta * DecelerationSpeedFactorAir);
_parent.Velocity = new Vector3(xDeceleration, _parent.Velocity.Y, zDeceleration);
}
if (isDead)
{
_parent.MoveAndSlide();
return;
}
}
public void Jump(bool isOnFloor)
{
var mantleLocationResult = _mantleSystem.FindMantleInFrontOfPlayer();
if (mantleLocationResult.IsSome(out var mantleLocation))
{
var duration = 0.1f * mantleLocation.DistanceTo(_parent.Position);
_tweenQueueSystem.QueueTween(mantleLocation, duration);
}
else if (isOnFloor)
{
_parent.Velocity = new Vector3(
x: _parent.Velocity.X,
y: _gravity.CalculateJumpForce(),
z: _parent.Velocity.Z);
}
else if (_canDoubleJump)
{
_canDoubleJump = false;
_parent.Velocity = new Vector3(
x: _parent.Velocity.X,
y: _gravity.CalculateJumpForce() * DoubleJumpSpeedFactor,
z: _parent.Velocity.Z);
}
}
}

View File

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

View File

@ -0,0 +1,52 @@
using Godot;
using System.Collections.Generic;
public partial class TweenQueueSystem : Node3D
{
public record TweenInputs(Vector3 Location, float Duration);
private Queue<TweenInputs> _tweenInputs = new Queue<TweenInputs>();
private Node3D _tweenObject;
private bool _isTweening = false;
public void Init(Node3D tweenObject)
{
_tweenObject = tweenObject;
}
public void EndTween()
{
_isTweening = false;
}
private void TweenToLocation(TweenInputs inputs)
{
var (location, duration) = inputs;
var tween = GetTree().CreateTween();
var callback = new Callable(this, MethodName.EndTween);
tween.TweenProperty(_tweenObject, "position", location, duration);
tween.TweenCallback(callback);
_isTweening = true;
tween.Play();
}
public void QueueTween(TweenInputs inputs)
{
_tweenInputs.Enqueue(inputs);
}
public void QueueTween(Vector3 location, float duration)
{
QueueTween(new TweenInputs(location, duration));
}
public void ProcessTweens()
{
if (_tweenInputs.Count > 0 && !_isTweening)
TweenToLocation(_tweenInputs.Dequeue());
}
}

View File

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

View File

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://dbe5f0p6lvqtr"]
[ext_resource type="Script" uid="uid://crm4u4r56hvg7" path="res://systems/tween_queue/TweenQueueSystem.cs" id="1_iqosd"]
[node name="TweenQueueSystem" type="Node3D"]
script = ExtResource("1_iqosd")