gd: added wall hugging

This commit is contained in:
2025-06-10 11:46:51 +02:00
parent 141688ef32
commit 0c015750fd
5 changed files with 135 additions and 28 deletions

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=34 format=3 uid="uid://bei4nhkf8lwdo"] [gd_scene load_steps=35 format=3 uid="uid://bei4nhkf8lwdo"]
[ext_resource type="Script" uid="uid://bbbrf5ckydfna" path="res://player_controller/Scripts/PlayerController.cs" id="1_poq2x"] [ext_resource type="Script" uid="uid://bbbrf5ckydfna" path="res://player_controller/Scripts/PlayerController.cs" id="1_poq2x"]
[ext_resource type="Resource" uid="uid://bl5crtu1gkrtr" path="res://systems/inputs/base_mode/base_mode.tres" id="3_cresl"] [ext_resource type="Resource" uid="uid://bl5crtu1gkrtr" path="res://systems/inputs/base_mode/base_mode.tres" id="3_cresl"]
@ -29,6 +29,7 @@
[ext_resource type="Script" uid="uid://jk2jm1g6q853" path="res://addons/godot_state_charts/compound_state.gd" id="26_infe6"] [ext_resource type="Script" uid="uid://jk2jm1g6q853" path="res://addons/godot_state_charts/compound_state.gd" id="26_infe6"]
[ext_resource type="Script" uid="uid://cytafq8i1y8qm" path="res://addons/godot_state_charts/atomic_state.gd" id="27_34snm"] [ext_resource type="Script" uid="uid://cytafq8i1y8qm" path="res://addons/godot_state_charts/atomic_state.gd" id="27_34snm"]
[ext_resource type="Script" uid="uid://c1vp0ojjvaby1" path="res://addons/godot_state_charts/parallel_state.gd" id="27_infe6"] [ext_resource type="Script" uid="uid://c1vp0ojjvaby1" path="res://addons/godot_state_charts/parallel_state.gd" id="27_infe6"]
[ext_resource type="Script" uid="uid://tjiji63wlom5" path="res://systems/wall_hug/WallHugSystem.cs" id="27_n7qhm"]
[ext_resource type="Script" uid="uid://cf1nsco3w0mf6" path="res://addons/godot_state_charts/transition.gd" id="28_n7qhm"] [ext_resource type="Script" uid="uid://cf1nsco3w0mf6" path="res://addons/godot_state_charts/transition.gd" id="28_n7qhm"]
[ext_resource type="PackedScene" uid="uid://ckm3d6k08a72u" path="res://systems/weapon/weapon.tscn" id="29_wv70j"] [ext_resource type="PackedScene" uid="uid://ckm3d6k08a72u" path="res://systems/weapon/weapon.tscn" id="29_wv70j"]
@ -141,6 +142,26 @@ StraightThrowDuration = 0.07
[node name="CoyoteTime" type="Timer" parent="."] [node name="CoyoteTime" type="Timer" parent="."]
wait_time = 0.2 wait_time = 0.2
[node name="WallHugSystem" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
script = ExtResource("27_n7qhm")
[node name="back" type="RayCast3D" parent="WallHugSystem"]
target_position = Vector3(0, 0, 1)
collision_mask = 2
[node name="front" type="RayCast3D" parent="WallHugSystem"]
target_position = Vector3(0, 0, -1)
collision_mask = 2
[node name="right" type="RayCast3D" parent="WallHugSystem"]
target_position = Vector3(1, 0, 0)
collision_mask = 2
[node name="left" type="RayCast3D" parent="WallHugSystem"]
target_position = Vector3(-1, 0, 0)
collision_mask = 2
[node name="StateChart" type="Node" parent="."] [node name="StateChart" type="Node" parent="."]
script = ExtResource("25_wv70j") script = ExtResource("25_wv70j")
metadata/_custom_type_script = "uid://couw105c3bde4" metadata/_custom_type_script = "uid://couw105c3bde4"
@ -273,7 +294,7 @@ script = ExtResource("27_34snm")
[node name="OnJump" type="Node" parent="StateChart/Root/Movement/Hanging"] [node name="OnJump" type="Node" parent="StateChart/Root/Movement/Hanging"]
script = ExtResource("28_n7qhm") script = ExtResource("28_n7qhm")
to = NodePath("../../Airborne/Jump") to = NodePath("../../Airborne/JumpFromWall")
event = &"jump" event = &"jump"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
@ -283,21 +304,6 @@ to = NodePath("../../Airborne/CoyoteEnabled")
event = &"drop" event = &"drop"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="WallHugging" type="Node" parent="StateChart/Root/Movement"]
script = ExtResource("27_34snm")
[node name="OnJump" type="Node" parent="StateChart/Root/Movement/WallHugging"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Airborne/Jump")
event = &"jump"
delay_in_seconds = "0.0"
[node name="OnDrop" type="Node" parent="StateChart/Root/Movement/WallHugging"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Airborne/CoyoteEnabled")
event = &"drop"
delay_in_seconds = "0.0"
[node name="Airborne" type="Node" parent="StateChart/Root/Movement"] [node name="Airborne" type="Node" parent="StateChart/Root/Movement"]
script = ExtResource("26_infe6") script = ExtResource("26_infe6")
initial_state = NodePath("CoyoteEnabled") initial_state = NodePath("CoyoteEnabled")
@ -308,6 +314,21 @@ to = NodePath("../../Grounded")
event = &"grounded" event = &"grounded"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="OnWallHug" type="Node" parent="StateChart/Root/Movement/Airborne"]
script = ExtResource("28_n7qhm")
to = NodePath("../WallHugging")
event = &"wall_hug"
delay_in_seconds = "0.0"
[node name="WallHugging" type="Node" parent="StateChart/Root/Movement/Airborne"]
script = ExtResource("27_34snm")
[node name="OnJump" type="Node" parent="StateChart/Root/Movement/Airborne/WallHugging"]
script = ExtResource("28_n7qhm")
to = NodePath("../../JumpFromWall")
event = &"jump"
delay_in_seconds = "0.0"
[node name="CoyoteEnabled" type="Node" parent="StateChart/Root/Movement/Airborne"] [node name="CoyoteEnabled" type="Node" parent="StateChart/Root/Movement/Airborne"]
script = ExtResource("27_34snm") script = ExtResource("27_34snm")
@ -332,6 +353,15 @@ to = NodePath("../../DoubleJumpEnabled")
event = &"to_double_jump" event = &"to_double_jump"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="JumpFromWall" type="Node" parent="StateChart/Root/Movement/Airborne"]
script = ExtResource("27_34snm")
[node name="ToDoubleJump" type="Node" parent="StateChart/Root/Movement/Airborne/JumpFromWall"]
script = ExtResource("28_n7qhm")
to = NodePath("../../DoubleJumpEnabled")
event = &"to_double_jump"
delay_in_seconds = "0.0"
[node name="DoubleJumpEnabled" type="Node" parent="StateChart/Root/Movement/Airborne"] [node name="DoubleJumpEnabled" type="Node" parent="StateChart/Root/Movement/Airborne"]
script = ExtResource("27_34snm") script = ExtResource("27_34snm")

View File

@ -21,6 +21,7 @@ public partial class PlayerController : CharacterBody3D
public TweenQueueSystem TweenQueueSystem; public TweenQueueSystem TweenQueueSystem;
public Node3D WeaponRoot; public Node3D WeaponRoot;
public WeaponSystem WeaponSystem; public WeaponSystem WeaponSystem;
public WallHugSystem WallHugSystem;
private bool _movementEnabled = true; private bool _movementEnabled = true;
@ -57,6 +58,7 @@ public partial class PlayerController : CharacterBody3D
private StateChartState _airborne; private StateChartState _airborne;
private StateChartState _coyoteEnabled; private StateChartState _coyoteEnabled;
private StateChartState _jump; private StateChartState _jump;
private StateChartState _jumpFromWall;
private StateChartState _doubleJumpEnabled; private StateChartState _doubleJumpEnabled;
private StateChartState _doubleJump; private StateChartState _doubleJump;
private StateChartState _falling; private StateChartState _falling;
@ -92,6 +94,7 @@ public partial class PlayerController : CharacterBody3D
MoveSystem = GetNode<MoveSystem>("MoveSystem"); MoveSystem = GetNode<MoveSystem>("MoveSystem");
DashSystem = GetNode<DashSystem>("DashSystem"); DashSystem = GetNode<DashSystem>("DashSystem");
StairsSystem = GetNode<StairsSystem>("StairsSystem"); StairsSystem = GetNode<StairsSystem>("StairsSystem");
WallHugSystem = GetNode<WallHugSystem>("WallHugSystem");
RayCast3D stairsBelowRayCast3D = GetNode<RayCast3D>("StairsBelowRayCast3D"); RayCast3D stairsBelowRayCast3D = GetNode<RayCast3D>("StairsBelowRayCast3D");
RayCast3D stairsAheadRayCast3D = GetNode<RayCast3D>("StairsAheadRayCast3D"); RayCast3D stairsAheadRayCast3D = GetNode<RayCast3D>("StairsAheadRayCast3D");
_headCollisionDetectors = new RayCast3D[NUM_OF_HEAD_COLLISION_DETECTORS]; _headCollisionDetectors = new RayCast3D[NUM_OF_HEAD_COLLISION_DETECTORS];
@ -117,10 +120,11 @@ public partial class PlayerController : CharacterBody3D
_grounded = StateChartState.Of(GetNode("StateChart/Root/Movement/Grounded")); _grounded = StateChartState.Of(GetNode("StateChart/Root/Movement/Grounded"));
_mantling = StateChartState.Of(GetNode("StateChart/Root/Movement/Mantling")); _mantling = StateChartState.Of(GetNode("StateChart/Root/Movement/Mantling"));
_movHanging = StateChartState.Of(GetNode("StateChart/Root/Movement/Hanging")); _movHanging = StateChartState.Of(GetNode("StateChart/Root/Movement/Hanging"));
_wallHugging = StateChartState.Of(GetNode("StateChart/Root/Movement/WallHugging"));
_airborne = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne")); _airborne = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne"));
_wallHugging = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/WallHugging"));
_coyoteEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/CoyoteEnabled")); _coyoteEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/CoyoteEnabled"));
_jump = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/Jump")); _jump = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/Jump"));
_jumpFromWall = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/JumpFromWall"));
_doubleJumpEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/DoubleJumpEnabled")); _doubleJumpEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/DoubleJumpEnabled"));
_doubleJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/DoubleJump")); _doubleJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/DoubleJump"));
_falling = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/Falling")); _falling = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/Falling"));
@ -150,6 +154,7 @@ public partial class PlayerController : CharacterBody3D
StairsSystem.Init(stairsBelowRayCast3D, stairsAheadRayCast3D, cameraSmooth); StairsSystem.Init(stairsBelowRayCast3D, stairsAheadRayCast3D, cameraSmooth);
DashSystem.Init(HeadSystem, camera, TweenQueueSystem); DashSystem.Init(HeadSystem, camera, TweenQueueSystem);
WeaponSystem.Init(HeadSystem, camera); WeaponSystem.Init(HeadSystem, camera);
WallHugSystem.Init();
// RPG Stuff // RPG Stuff
HealthSystem.HealthSystemInitParams healthSystemParams = new HealthSystem.HealthSystemInitParams() HealthSystem.HealthSystemInitParams healthSystemParams = new HealthSystem.HealthSystemInitParams()
@ -179,6 +184,7 @@ public partial class PlayerController : CharacterBody3D
_coyoteEnabled.StateEntered += StartCoyoteTime; _coyoteEnabled.StateEntered += StartCoyoteTime;
_coyoteTimer.Timeout += CoyoteExpired; _coyoteTimer.Timeout += CoyoteExpired;
_jump.StateEntered += Jump; _jump.StateEntered += Jump;
_jumpFromWall.StateEntered += JumpFromWall;
_doubleJump.StateEntered += DoubleJump; _doubleJump.StateEntered += DoubleJump;
_mantling.StateEntered += Mantle; _mantling.StateEntered += Mantle;
@ -252,17 +258,27 @@ public partial class PlayerController : CharacterBody3D
_playerState.SendEvent("to_double_jump"); _playerState.SendEvent("to_double_jump");
PerformJump(false); PerformJump(false);
} }
public void JumpFromWall()
{
_playerState.SendEvent("to_double_jump");
var wallNormal = WallHugSystem.GetWallNormal();
if (wallNormal.IsSome(out var normal))
PerformJump(false, normal);
PerformJump(false);
}
public void DoubleJump() public void DoubleJump()
{ {
_playerState.SendEvent("to_falling"); _playerState.SendEvent("to_falling");
PerformJump(true); PerformJump(true);
} }
private void PerformJump(bool isDoubleJump) private void PerformJump(bool isDoubleJump, Vector3? jumpDirection = null)
{ {
var effectiveJumpDirection = jumpDirection ?? Vector3.Up;
var jumpVector = (effectiveJumpDirection.Normalized() + Vector3.Up).Normalized();
bool doesCapsuleHaveCrouchingHeight = CapsuleCollider.IsCrouchingHeight(); bool doesCapsuleHaveCrouchingHeight = CapsuleCollider.IsCrouchingHeight();
bool isPlayerDead = HealthSystem.IsDead(); bool isPlayerDead = HealthSystem.IsDead();
if (!doesCapsuleHaveCrouchingHeight && !isPlayerDead) if (!doesCapsuleHaveCrouchingHeight && !isPlayerDead)
MoveSystem.Jump(isDoubleJump); MoveSystem.Jump(isDoubleJump, jumpVector);
} }
// Mantling // Mantling
@ -363,6 +379,8 @@ public partial class PlayerController : CharacterBody3D
{ {
if (isOnFloorCustom()) if (isOnFloorCustom())
_playerState.SendEvent("grounded"); _playerState.SendEvent("grounded");
if (WallHugSystem.IsWallHugging() && Velocity.Y < 0)
_playerState.SendEvent("wall_hug");
} }
/////////////////////////// ///////////////////////////
@ -382,7 +400,8 @@ public partial class PlayerController : CharacterBody3D
isOnFloorCustom(), isOnFloorCustom(),
HealthSystem.IsDead(), HealthSystem.IsDead(),
IsHeadTouchingCeiling(), IsHeadTouchingCeiling(),
_actionHanging.Active); _actionHanging.Active,
_wallHugging.Active);
MoveSystem.MoveAround(moveAroundParams); MoveSystem.MoveAround(moveAroundParams);
} }
@ -502,4 +521,5 @@ public partial class PlayerController : CharacterBody3D
CameraModifications((float) delta); CameraModifications((float) delta);
HandleStairs((float) delta); HandleStairs((float) delta);
} }
} }

View File

@ -20,7 +20,8 @@ public partial class MoveSystem : Node3D
bool IsOnFloor, bool IsOnFloor,
bool IsDead, bool IsDead,
bool IsHeadTouchingCeiling, bool IsHeadTouchingCeiling,
bool isHanging bool isHanging,
bool isWallHugging
); );
[Export(PropertyHint.Range, "0,20,0.1,or_greater")] [Export(PropertyHint.Range, "0,20,0.1,or_greater")]
@ -38,6 +39,8 @@ public partial class MoveSystem : Node3D
public float CrouchTransitionSpeed { get; set; } = 20.0f; public float CrouchTransitionSpeed { get; set; } = 20.0f;
[Export(PropertyHint.Range, "0,5,0.1,or_greater")] [Export(PropertyHint.Range, "0,5,0.1,or_greater")]
public float DoubleJumpSpeedFactor { get; set; } = 2f; public float DoubleJumpSpeedFactor { get; set; } = 2f;
[Export(PropertyHint.Range, "0,1,0.01,or_greater")]
public float WallHugGravityReducingFactor { get; set; } = 0.1f;
private Gravity _gravity; private Gravity _gravity;
@ -60,7 +63,7 @@ public partial class MoveSystem : Node3D
public void MoveAround(MoveAroundParameters param) public void MoveAround(MoveAroundParameters param)
{ {
var (delta, movementDirection, isOnFloor, isDead, isHeadTouchingCeiling, isHanging) = param; var (delta, movementDirection, isOnFloor, isDead, isHeadTouchingCeiling, isHanging, isWallHugging) = param;
var doesCapsuleHaveCrouchingHeight = _capsuleCollider.IsCrouchingHeight(); var doesCapsuleHaveCrouchingHeight = _capsuleCollider.IsCrouchingHeight();
var doesCapsuleHaveDefaultHeight = _capsuleCollider.IsDefaultHeight(); var doesCapsuleHaveDefaultHeight = _capsuleCollider.IsDefaultHeight();
@ -71,6 +74,14 @@ public partial class MoveSystem : Node3D
_parent.MoveAndSlide(); _parent.MoveAndSlide();
return; return;
} }
if (isWallHugging)
{
_parent.Velocity = new Vector3(
x: _parent.Velocity.X,
y: _parent.Velocity.Y - _gravity.CalculateGravityForce() * (float)delta * WallHugGravityReducingFactor,
z: _parent.Velocity.Z);
return;
}
// Adding the gravity // Adding the gravity
if (!isOnFloor) if (!isOnFloor)
@ -167,16 +178,16 @@ public partial class MoveSystem : Node3D
} }
} }
public void Jump(bool isDoubleJump) public void Jump(bool isDoubleJump, Vector3? jumpDirection = null)
{ {
var effectiveJumpDirection = jumpDirection ?? Vector3.Up;
var jumpForce = isDoubleJump var jumpForce = isDoubleJump
? _gravity.CalculateJumpForce() * DoubleJumpSpeedFactor ? _gravity.CalculateJumpForce() * DoubleJumpSpeedFactor
: _gravity.CalculateJumpForce(); : _gravity.CalculateJumpForce();
_parent.Velocity = new Vector3( var currentHorizontalVelocity = new Vector3(_parent.Velocity.X, 0, _parent.Velocity.Z);
x: _parent.Velocity.X, var jumpVelocity = jumpForce * effectiveJumpDirection;
y: jumpForce, _parent.Velocity = currentHorizontalVelocity + jumpVelocity;
z: _parent.Velocity.Z);
} }
public bool CanMantle() public bool CanMantle()

View File

@ -0,0 +1,45 @@
using Godot;
using System;
using System.Collections.Generic;
using RustyOptions;
namespace Movementtests.systems;
public partial class WallHugSystem : Node3D
{
private List<RayCast3D> _raycasts;
public void Init()
{
_raycasts = new List<RayCast3D>();
_raycasts.Add(GetNode<RayCast3D>("front"));
_raycasts.Add(GetNode<RayCast3D>("back"));
_raycasts.Add(GetNode<RayCast3D>("left"));
_raycasts.Add(GetNode<RayCast3D>("right"));
}
public bool IsWallHugging()
{
foreach (RayCast3D raycast in _raycasts)
{
if (raycast.IsColliding())
{
return true;
}
}
return false;
}
public Option<Vector3> GetWallNormal()
{
foreach (RayCast3D raycast in _raycasts)
{
if (raycast.IsColliding())
{
return raycast.GetCollisionNormal().Some();
}
}
return Option<Vector3>.None;
}
}

View File

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