god dayum wall hugging on point man

This commit is contained in:
2025-08-18 14:54:41 +02:00
parent 4f9005d016
commit 5087cb58bc
4 changed files with 305 additions and 225 deletions

View File

@ -55,19 +55,21 @@ AccelerationAir = 2.0
DecelerationAir = 0.1 DecelerationAir = 0.1
Weight = 5.0 Weight = 5.0
SimpleJumpStartVelocity = 8.0 SimpleJumpStartVelocity = 8.0
SimpleJumpHangTimeInFrames = 3 SimpleJumpHangTimeInFrames = 1
SimpleJumpGravityLesseningFactor = 2.5 SimpleJumpGravityLesseningFactor = 2.5
DoubleJumpStartVelocity = 15.0 DoubleJumpStartVelocity = 15.0
DoubleJumpGravityLesseningFactor = 1.2 DoubleJumpHangTimeInFrames = 3
DoubleJumpGravityLesseningFactor = 1.5
MegaJumpStartVelocity = 30.0 MegaJumpStartVelocity = 30.0
MegaJumpHangTimeInFrames = 15 MegaJumpHangTimeInFrames = 12
MegaJumpGravityLesseningFactor = 1.2 MegaJumpGravityLesseningFactor = 1.2
JumpFromDashSpeedFactor = 4.0 WallJumpStartVelocity = 8.0
WallHugHorizontalDeceleration = 3.0 SimpleDashStrength = 15.0
MaxJumpBoostAfterDashing = 0.7 PoweredDashStrength = 50.0
WallHugGravityLesseningFactor = 15.0
WallHugDownwardMaxSpeed = 8.0
WallHugHorizontalDeceleration = 0.5
MaxNumberOfDashActions = 2 MaxNumberOfDashActions = 2
PerfectlyTimedActionTimer = 0.3
BasicDashStrength = 15.0
DashTimeDilationCurve = ExtResource("2_2q0ik") DashTimeDilationCurve = ExtResource("2_2q0ik")
[node name="InputController" type="Node3D" parent="."] [node name="InputController" type="Node3D" parent="."]
@ -181,8 +183,8 @@ transform = Transform3D(1, 0, 0, 0, 0.173648, -0.984808, 0, 0.984808, 0.173648,
ThrowForce = 15.0 ThrowForce = 15.0
StraightThrowDuration = 0.05 StraightThrowDuration = 0.05
[node name="CoyoteTime" type="Timer" parent="."] [node name="DashCooldown" type="Timer" parent="."]
wait_time = 0.2 wait_time = 0.5
one_shot = true one_shot = true
[node name="PowerCooldown" type="Timer" parent="."] [node name="PowerCooldown" type="Timer" parent="."]
@ -194,14 +196,6 @@ wait_time = 2.0
one_shot = true one_shot = true
ignore_time_scale = true ignore_time_scale = true
[node name="TimeAfterDashing" type="Timer" parent="."]
wait_time = 0.3
one_shot = true
[node name="EmpowerTimeDownscale" type="Timer" parent="."]
one_shot = true
ignore_time_scale = true
[node name="StateChartDebugger" parent="." instance=ExtResource("24_q5h8a")] [node name="StateChartDebugger" parent="." instance=ExtResource("24_q5h8a")]
offset_left = 1524.0 offset_left = 1524.0
offset_top = 1.0 offset_top = 1.0
@ -318,25 +312,6 @@ to = NodePath("../../Off")
event = &"empower_released" event = &"empower_released"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="Actions" type="Node" parent="StateChart/Root"]
script = ExtResource("26_infe6")
initial_state = NodePath("Default")
[node name="Default" type="Node" parent="StateChart/Root/Actions"]
script = ExtResource("27_34snm")
[node name="Jumping" type="Node" parent="StateChart/Root/Actions"]
script = ExtResource("27_34snm")
[node name="Dashing" type="Node" parent="StateChart/Root/Actions"]
script = ExtResource("27_34snm")
[node name="Hitting" type="Node" parent="StateChart/Root/Actions"]
script = ExtResource("27_34snm")
[node name="Throwing" type="Node" parent="StateChart/Root/Actions"]
script = ExtResource("27_34snm")
[node name="PowerReserve" type="Node" parent="StateChart/Root"] [node name="PowerReserve" type="Node" parent="StateChart/Root"]
script = ExtResource("26_infe6") script = ExtResource("26_infe6")
initial_state = NodePath("Full") initial_state = NodePath("Full")
@ -416,6 +391,11 @@ script = ExtResource("27_34snm")
script = ExtResource("26_infe6") script = ExtResource("26_infe6")
initial_state = NodePath("Grounded") initial_state = NodePath("Grounded")
[node name="Reset" type="Node" parent="StateChart/Root/Movement"]
script = ExtResource("41_ruloh")
deep = true
default_state = NodePath("../Grounded")
[node name="OnFall" type="Node" parent="StateChart/Root/Movement"] [node name="OnFall" type="Node" parent="StateChart/Root/Movement"]
script = ExtResource("28_n7qhm") script = ExtResource("28_n7qhm")
to = NodePath("../Airborne/Falling") to = NodePath("../Airborne/Falling")
@ -434,22 +414,41 @@ to = NodePath("../OnWall/Hanging")
event = &"dash_to_planted" event = &"dash_to_planted"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="OnDash" type="Node" parent="StateChart/Root/Movement"] [node name="OnPoweredDash" type="Node" parent="StateChart/Root/Movement"]
script = ExtResource("28_n7qhm") script = ExtResource("28_n7qhm")
to = NodePath("../Dashing") to = NodePath("../Dashing/PoweredDash")
event = &"dash" event = &"powered_dash"
delay_in_seconds = "0.0"
[node name="Mantling" type="Node" parent="StateChart/Root/Movement"]
script = ExtResource("27_34snm")
[node name="OnMantleFinished" type="Node" parent="StateChart/Root/Movement/Mantling"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Grounded")
event = &"grounded"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="Dashing" type="Node" parent="StateChart/Root/Movement"] [node name="Dashing" type="Node" parent="StateChart/Root/Movement"]
script = ExtResource("27_34snm") script = ExtResource("26_infe6")
initial_state = NodePath("Dash")
[node name="OnDashEnded" type="Node" parent="StateChart/Root/Movement/Dashing"] [node name="OnDashEnded" type="Node" parent="StateChart/Root/Movement/Dashing"]
script = ExtResource("28_n7qhm") script = ExtResource("28_n7qhm")
to = NodePath("../../Airborne/Reset") to = NodePath("../../Airborne/Reset")
event = &"dash_ended" event = &"dash_finished"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="Mantling" type="Node" parent="StateChart/Root/Movement"] [node name="OnMantle" type="Node" parent="StateChart/Root/Movement/Dashing"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Mantling")
event = &"mantle"
delay_in_seconds = "0.0"
[node name="Dash" type="Node" parent="StateChart/Root/Movement/Dashing"]
script = ExtResource("27_34snm")
[node name="PoweredDash" type="Node" parent="StateChart/Root/Movement/Dashing"]
script = ExtResource("27_34snm") script = ExtResource("27_34snm")
[node name="Jump" type="Node" parent="StateChart/Root/Movement"] [node name="Jump" type="Node" parent="StateChart/Root/Movement"]
@ -498,6 +497,12 @@ delay_in_seconds = "0.0"
[node name="Grounded" type="Node" parent="StateChart/Root/Movement"] [node name="Grounded" type="Node" parent="StateChart/Root/Movement"]
script = ExtResource("27_34snm") script = ExtResource("27_34snm")
[node name="OnDash" type="Node" parent="StateChart/Root/Movement/Grounded"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Dashing/Dash")
event = &"dash"
delay_in_seconds = "0.0"
[node name="OnJump" type="Node" parent="StateChart/Root/Movement/Grounded"] [node name="OnJump" type="Node" parent="StateChart/Root/Movement/Grounded"]
script = ExtResource("28_n7qhm") script = ExtResource("28_n7qhm")
to = NodePath("../../Jump/SimpleJump") to = NodePath("../../Jump/SimpleJump")
@ -524,10 +529,16 @@ initial_state = NodePath("CoyoteEnabled")
script = ExtResource("41_ruloh") script = ExtResource("41_ruloh")
default_state = NodePath("../CoyoteEnabled") default_state = NodePath("../CoyoteEnabled")
[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Airborne"] [node name="OnWallHug" type="Node" parent="StateChart/Root/Movement/Airborne"]
script = ExtResource("28_n7qhm") script = ExtResource("28_n7qhm")
to = NodePath("../../Jump/MegaJump") to = NodePath("../../OnWall/Hugging")
event = &"megajump" event = &"wall_hug"
delay_in_seconds = "0.0"
[node name="OnDash" type="Node" parent="StateChart/Root/Movement/Airborne"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Dashing/Dash")
event = &"dash"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="OnGrounded" type="Node" parent="StateChart/Root/Movement/Airborne"] [node name="OnGrounded" type="Node" parent="StateChart/Root/Movement/Airborne"]
@ -536,27 +547,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("../../OnWall/Hugging")
event = &"wall_hug"
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")
[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Airborne/CoyoteEnabled"]
script = ExtResource("28_n7qhm")
to = NodePath("../../../Jump/MegaJump")
event = &"megajump"
delay_in_seconds = "0.0"
[node name="OnJump" type="Node" parent="StateChart/Root/Movement/Airborne/CoyoteEnabled"] [node name="OnJump" type="Node" parent="StateChart/Root/Movement/Airborne/CoyoteEnabled"]
script = ExtResource("28_n7qhm") script = ExtResource("28_n7qhm")
to = NodePath("../../../Jump/SimpleJump") to = NodePath("../../../Jump/SimpleJump")
event = &"jump" event = &"jump"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Airborne/CoyoteEnabled"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Falling")
event = &"megajump"
delay_in_seconds = "0.0"
[node name="OnExpiration" type="Node" parent="StateChart/Root/Movement/Airborne/CoyoteEnabled"] [node name="OnExpiration" type="Node" parent="StateChart/Root/Movement/Airborne/CoyoteEnabled"]
script = ExtResource("28_n7qhm") script = ExtResource("28_n7qhm")
to = NodePath("../../DoubleJumpEnabled") to = NodePath("../../DoubleJumpEnabled")
@ -566,18 +571,18 @@ 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")
[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Airborne/DoubleJumpEnabled"]
script = ExtResource("28_n7qhm")
to = NodePath("../../../Jump/MegaJump")
event = &"megajump"
delay_in_seconds = "0.0"
[node name="OnJump" type="Node" parent="StateChart/Root/Movement/Airborne/DoubleJumpEnabled"] [node name="OnJump" type="Node" parent="StateChart/Root/Movement/Airborne/DoubleJumpEnabled"]
script = ExtResource("28_n7qhm") script = ExtResource("28_n7qhm")
to = NodePath("../../../Jump/DoubleJump") to = NodePath("../../../Jump/DoubleJump")
event = &"jump" event = &"jump"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="OnDash" type="Node" parent="StateChart/Root/Movement/Airborne/DoubleJumpEnabled"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Falling")
event = &"dash"
delay_in_seconds = "0.0"
[node name="Falling" type="Node" parent="StateChart/Root/Movement/Airborne"] [node name="Falling" type="Node" parent="StateChart/Root/Movement/Airborne"]
script = ExtResource("27_34snm") script = ExtResource("27_34snm")
@ -591,12 +596,24 @@ delay_in_seconds = "0.0"
script = ExtResource("26_infe6") script = ExtResource("26_infe6")
initial_state = NodePath("Hugging") initial_state = NodePath("Hugging")
[node name="OnGrounded" type="Node" parent="StateChart/Root/Movement/OnWall"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Grounded")
event = &"grounded"
delay_in_seconds = "0.0"
[node name="OnJump" type="Node" parent="StateChart/Root/Movement/OnWall"] [node name="OnJump" type="Node" parent="StateChart/Root/Movement/OnWall"]
script = ExtResource("28_n7qhm") script = ExtResource("28_n7qhm")
to = NodePath("../../Airborne/DoubleJumpEnabled") to = NodePath("../../Jump/DoubleJump")
event = &"jump" event = &"jump"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/OnWall"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Jump/MegaJump")
event = &"megajump"
delay_in_seconds = "0.0"
[node name="HugCanceled" type="Node" parent="StateChart/Root/Movement/OnWall"] [node name="HugCanceled" type="Node" parent="StateChart/Root/Movement/OnWall"]
script = ExtResource("27_34snm") script = ExtResource("27_34snm")
@ -609,6 +626,12 @@ delay_in_seconds = "0.0"
[node name="Hugging" type="Node" parent="StateChart/Root/Movement/OnWall"] [node name="Hugging" type="Node" parent="StateChart/Root/Movement/OnWall"]
script = ExtResource("27_34snm") script = ExtResource("27_34snm")
[node name="OnGrounded" type="Node" parent="StateChart/Root/Movement/OnWall/Hugging"]
script = ExtResource("28_n7qhm")
to = NodePath("../../../Grounded")
event = &"grounded"
delay_in_seconds = "0.0"
[node name="OnLeaveWall" type="Node" parent="StateChart/Root/Movement/OnWall/Hugging"] [node name="OnLeaveWall" type="Node" parent="StateChart/Root/Movement/OnWall/Hugging"]
script = ExtResource("28_n7qhm") script = ExtResource("28_n7qhm")
to = NodePath("../../../Airborne/CoyoteEnabled") to = NodePath("../../../Airborne/CoyoteEnabled")
@ -645,3 +668,4 @@ delay_in_seconds = "0.0"
[connection signal="input_rotate_floorplane" from="InputController" to="." method="OnInputRotateFloorplane"] [connection signal="input_rotate_floorplane" from="InputController" to="." method="OnInputRotateFloorplane"]
[connection signal="input_rotate_y" from="InputController" to="." method="OnInputRotateY"] [connection signal="input_rotate_y" from="InputController" to="." method="OnInputRotateY"]
[connection signal="input_throw" from="InputController" to="." method="OnInputThrowPressed"] [connection signal="input_throw" from="InputController" to="." method="OnInputThrowPressed"]
[connection signal="WallDetected" from="WallHugSystem" to="." method="OnWallDetected"]

View File

@ -1,6 +1,7 @@
using System; using System;
using Godot; using Godot;
using GodotStateCharts; using GodotStateCharts;
using Movementtests.addons.godot_state_charts.csharp;
using Movementtests.systems; using Movementtests.systems;
using Movementtests.player_controller.Scripts; using Movementtests.player_controller.Scripts;
using RustyOptions; using RustyOptions;
@ -49,11 +50,9 @@ public partial class PlayerController : CharacterBody3D
private float _inputRotateFloorplane; private float _inputRotateFloorplane;
// Timers // Timers
private Timer _coyoteTimer;
private Timer _timeScaleAimInAirTimer; private Timer _timeScaleAimInAirTimer;
private Timer _timeAfterDashingTimer; private Timer _simpleDashCooldownTimer;
private Timer _powerCooldownTimer; private Timer _powerCooldownTimer;
private Timer _empowerTimeDownscale;
[ExportCategory("Movement")] [ExportCategory("Movement")]
[ExportGroup("Ground")] [ExportGroup("Ground")]
@ -72,7 +71,9 @@ public partial class PlayerController : CharacterBody3D
public float Weight { get; set; } = 3.0f; public float Weight { get; set; } = 3.0f;
// Jump // Jump
[ExportGroup("Jump")] [ExportGroup("Jump")]
[Export(PropertyHint.Range, "0,1,0.01")]
public float CoyoteTime { get; set; } = 0.2f;
// Simple jump // Simple jump
[ExportSubgroup("Simple jump")] [ExportSubgroup("Simple jump")]
@ -103,45 +104,48 @@ public partial class PlayerController : CharacterBody3D
[Export(PropertyHint.Range, "1,10,0.1,or_greater")] [Export(PropertyHint.Range, "1,10,0.1,or_greater")]
public float MegaJumpGravityLesseningFactor { get; set; } = 3f; public float MegaJumpGravityLesseningFactor { get; set; } = 3f;
// Other jump // Wall jump
[ExportSubgroup("Other jump")] [ExportSubgroup("Wall jump")]
[Export(PropertyHint.Range, "0,2,0.01,or_greater")] [Export(PropertyHint.Range, "0,100,1,or_greater")]
public float StartVelocity { get; set; } = 1.0f; public float WallJumpStartVelocity { get; set; } = 10.0f;
[Export(PropertyHint.Range, "0.1,10,0.1,or_greater")] [Export(PropertyHint.Range, "0,100,1,or_greater")]
public float DoubleJumpSpeedFactor { get; set; } = 2f; public float WallMegajumpStartVelocity { get; set; } = 20.0f;
[Export(PropertyHint.Range, "0.1,10,0.1,or_greater")]
public float JumpFromDashSpeedFactor { get; set; } = 2f; // Dash
[Export(PropertyHint.Range, "0.1,10,0.1,or_greater")] [ExportGroup("Dash")]
public float JumpFromWallSpeedFactor { get; set; } = 2f; // Simple dash
[ExportGroup("WallHug")] [ExportSubgroup("Simple")]
[Export(PropertyHint.Range, "0.1,10,0.1,or_greater")] [Export(PropertyHint.Range, "0,50,0.1")]
public float WallHugDownwardSpeed { get; set; } = 2f; public float SimpleDashStrength { get; set; } = 10f;
// Powered Dash
[ExportSubgroup("Powered")]
[Export(PropertyHint.Range, "0,1,0.01,or_greater")]
public float PoweredDashTime { get; set; } = 0.3f;
[Export(PropertyHint.Range, "0,100,0.1")]
public float PoweredDashStrength { get; set; } = 10f;
// Wall hug
[ExportGroup("Wall hug")]
[Export(PropertyHint.Range, "0,50,0.1,or_greater")]
public float WallHugGravityLesseningFactor { get; set; } = 2f;
[Export(PropertyHint.Range, "0.1,50,0.1,or_greater")]
public float WallHugDownwardMaxSpeed { get; set; } = 2f;
[Export(PropertyHint.Range, "0.1,10,0.1,or_greater")] [Export(PropertyHint.Range, "0.1,10,0.1,or_greater")]
public float WallHugHorizontalDeceleration { get; set; } = 5f; public float WallHugHorizontalDeceleration { get; set; } = 5f;
private float _targetSpeed; private float _targetSpeed;
private float _gravity; private float _gravity;
[ExportCategory("Other")] [ExportCategory("Other")]
[Export(PropertyHint.Range, "0,1,0.01,or_greater")] [Export(PropertyHint.Range, "0,1,0.01,or_greater")]
public float TimeScaleAimInAir { get; set; } = 0.05f; public float TimeScaleAimInAir { get; set; } = 0.05f;
[Export(PropertyHint.Range, "0,5,0.1,or_greater")]
public float MaxJumpBoostAfterDashing { get; set; } = 1f;
[Export(PropertyHint.Range, "0,5,1,or_greater")] [Export(PropertyHint.Range, "0,5,1,or_greater")]
public int MaxNumberOfDashActions { get; set; } = 1; public int MaxNumberOfDashActions { get; set; } = 1;
[Export(PropertyHint.Range, "0,200,1,or_greater")]
public int DashIndicatorStartSize { get; set; } = 100;
[Export(PropertyHint.Range, "0,1,0.01")]
public float PerfectlyTimedActionTimer { get; set; } = 0.8f;
[Export(PropertyHint.Range, "0,50,0.1")]
public float BasicDashStrength { get; set; } = 10f;
[Export] [Export]
public Curve DashTimeDilationCurve { get; set; } public Curve DashTimeDilationCurve { get; set; }
private bool _canDash = true; private bool _canDash = true;
private bool _isDashOnCooldown = false;
private int _empoweredActionsLeft; private int _empoweredActionsLeft;
public int EmpoweredActionsLeft public int EmpoweredActionsLeft
{ {
@ -160,7 +164,6 @@ public partial class PlayerController : CharacterBody3D
private StateChartState _weaponInHand; private StateChartState _weaponInHand;
private StateChartState _aiming; private StateChartState _aiming;
private StateChartState _dashing;
private StateChartState _weaponThrown; private StateChartState _weaponThrown;
private StateChartState _actionHanging; private StateChartState _actionHanging;
private StateChartState _empowerOn; private StateChartState _empowerOn;
@ -177,6 +180,8 @@ public partial class PlayerController : CharacterBody3D
private StateChartState _simpleJump; private StateChartState _simpleJump;
private StateChartState _doubleJump; private StateChartState _doubleJump;
private StateChartState _megaJump; private StateChartState _megaJump;
private StateChartState _simpleDash;
private StateChartState _poweredDash;
private StateChartState _doubleJumpEnabled; private StateChartState _doubleJumpEnabled;
private StateChartState _onWall; private StateChartState _onWall;
private StateChartState _onWallHugCanceled; private StateChartState _onWallHugCanceled;
@ -184,6 +189,9 @@ public partial class PlayerController : CharacterBody3D
private StateChartState _onWallHanging; private StateChartState _onWallHanging;
private StateChartState _falling; private StateChartState _falling;
private Transition _onJumpFromWall;
private Transition _onMegajumpFromWall;
public override void _Ready() public override void _Ready()
{ {
/////////////////////////// ///////////////////////////
@ -240,7 +248,8 @@ public partial class PlayerController : CharacterBody3D
_weaponInHand = StateChartState.Of(GetNode("StateChart/Root/WeaponState/InHand")); _weaponInHand = StateChartState.Of(GetNode("StateChart/Root/WeaponState/InHand"));
_weaponThrown = StateChartState.Of(GetNode("StateChart/Root/WeaponState/Flying")); _weaponThrown = StateChartState.Of(GetNode("StateChart/Root/WeaponState/Flying"));
_aiming = StateChartState.Of(GetNode("StateChart/Root/Aim/On")); _aiming = StateChartState.Of(GetNode("StateChart/Root/Aim/On"));
_dashing = StateChartState.Of(GetNode("StateChart/Root/Movement/Dashing")); _simpleDash = StateChartState.Of(GetNode("StateChart/Root/Movement/Dashing/Dash"));
_poweredDash = StateChartState.Of(GetNode("StateChart/Root/Movement/Dashing/PoweredDash"));
// _actionHanging = StateChartState.Of(GetNode("StateChart/Root/Actions/Hanging")); // _actionHanging = StateChartState.Of(GetNode("StateChart/Root/Actions/Hanging"));
_empowerOn = StateChartState.Of(GetNode("StateChart/Root/Empower/On")); _empowerOn = StateChartState.Of(GetNode("StateChart/Root/Empower/On"));
_empowerOff = StateChartState.Of(GetNode("StateChart/Root/Empower/Off")); _empowerOff = StateChartState.Of(GetNode("StateChart/Root/Empower/Off"));
@ -257,17 +266,17 @@ public partial class PlayerController : CharacterBody3D
_doubleJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Jump/DoubleJump")); _doubleJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Jump/DoubleJump"));
_megaJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Jump/MegaJump")); _megaJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Jump/MegaJump"));
_doubleJumpEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/DoubleJumpEnabled")); _doubleJumpEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/DoubleJumpEnabled"));
_falling = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/Falling"));
_onWall = StateChartState.Of(GetNode("StateChart/Root/Movement/OnWall")); _onWall = StateChartState.Of(GetNode("StateChart/Root/Movement/OnWall"));
_onJumpFromWall = Transition.Of(GetNode("StateChart/Root/Movement/OnWall/OnJump"));
_onMegajumpFromWall = Transition.Of(GetNode("StateChart/Root/Movement/OnWall/OnMegajump"));
_onWallHugging = StateChartState.Of(GetNode("StateChart/Root/Movement/OnWall/Hugging")); _onWallHugging = StateChartState.Of(GetNode("StateChart/Root/Movement/OnWall/Hugging"));
_onWallHugCanceled = StateChartState.Of(GetNode("StateChart/Root/Movement/OnWall/HugCanceled")); _onWallHugCanceled = StateChartState.Of(GetNode("StateChart/Root/Movement/OnWall/HugCanceled"));
_onWallHanging = StateChartState.Of(GetNode("StateChart/Root/Movement/OnWall/Hanging")); _onWallHanging = StateChartState.Of(GetNode("StateChart/Root/Movement/OnWall/Hanging"));
_falling = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/Falling"));
// State timers // State timers
_coyoteTimer = GetNode<Timer>("CoyoteTime");
_powerCooldownTimer = GetNode<Timer>("PowerCooldown"); _powerCooldownTimer = GetNode<Timer>("PowerCooldown");
_timeScaleAimInAirTimer = GetNode<Timer>("TimeScaleAimInAir"); _timeScaleAimInAirTimer = GetNode<Timer>("TimeScaleAimInAir");
_timeAfterDashingTimer = GetNode<Timer>("TimeAfterDashing"); _simpleDashCooldownTimer = GetNode<Timer>("DashCooldown");
_empowerTimeDownscale = GetNode<Timer>("EmpowerTimeDownscale");
/////////////////////////// ///////////////////////////
// Initialize components // // Initialize components //
@ -308,10 +317,6 @@ public partial class PlayerController : CharacterBody3D
/////////////////////////// ///////////////////////////
// Signal setup /////////// // Signal setup ///////////
/////////////////////////// ///////////////////////////
DashSystem.DashEnded += OnDashEnded;
DashSystem.DashProgress += OnDashProgress;
_weaponInHand.StateProcessing += HandleWeaponInHand; _weaponInHand.StateProcessing += HandleWeaponInHand;
_aiming.StateProcessing += HandleAiming; _aiming.StateProcessing += HandleAiming;
_aiming.StateEntered += OnAimingEntered; _aiming.StateEntered += OnAimingEntered;
@ -321,22 +326,12 @@ public partial class PlayerController : CharacterBody3D
_grounded.StateEntered += OnGrounded; _grounded.StateEntered += OnGrounded;
_grounded.StatePhysicsProcessing += HandleGrounded; _grounded.StatePhysicsProcessing += HandleGrounded;
_airborne.StatePhysicsProcessing += HandleAirborne; _airborne.StatePhysicsProcessing += HandleAirborne;
_onWallHugCanceled.StatePhysicsProcessing += HandleAirborne;
_onWallHugging.StatePhysicsProcessing += HandleWallHugging;
_onWallHanging.StatePhysicsProcessing += HandleWallHanging;
_coyoteEnabled.StateEntered += StartCoyoteTime; _coyoteEnabled.StateEntered += StartCoyoteTime;
_coyoteTimer.Timeout += CoyoteExpired;
_timeScaleAimInAirTimer.Timeout += ResetTimeScale; _timeScaleAimInAirTimer.Timeout += ResetTimeScale;
_dashing.StatePhysicsProcessing += Dashing;
// _weaponThrown.StateEntered += OnWeaponThrown; // _weaponThrown.StateEntered += OnWeaponThrown;
// _empowerOn.StateEntered += OnEmpowerStarted;
// _empowerOn.StateProcessing += HandleEmpower;
// _empowerOff.StateEntered += EmpowerStopped;
// _empowerTimeDownscale.Timeout += EmpowerTimerTimeout;
_powerFull.StateEntered += StopPowerCooldown; _powerFull.StateEntered += StopPowerCooldown;
_powerFull.StateExited += StartPowerCooldown; _powerFull.StateExited += StartPowerCooldown;
_powerRecharging.StateEntered += StartPowerCooldown; _powerRecharging.StateEntered += StartPowerCooldown;
@ -352,13 +347,43 @@ public partial class PlayerController : CharacterBody3D
_megaJump.StateEntered += OnMegaJumpStarted; _megaJump.StateEntered += OnMegaJumpStarted;
_megaJump.StatePhysicsProcessing += HandleMegaJump; _megaJump.StatePhysicsProcessing += HandleMegaJump;
_simpleDash.StateEntered += OnSimpleDashStarted;
_simpleDash.StatePhysicsProcessing += HandleSimpleDash;
_poweredDash.StateEntered += OnPoweredDashStarted;
_poweredDash.StatePhysicsProcessing += HandlePoweredDash;
_poweredDash.StateExited += OnPoweredDashFinished;
_simpleDashCooldownTimer.Timeout += DashCooldownTimeout;
_onWallHugCanceled.StatePhysicsProcessing += HandleAirborne;
_onWallHugging.StatePhysicsProcessing += HandleWallHugging;
_onWallHanging.StatePhysicsProcessing += HandleWallHanging;
_onJumpFromWall.Taken += OnJumpFromWall;
_onMegajumpFromWall.Taken += OnMegajumpFromWall;
}
public void OnWallDetected()
{
FinishPoweredDash();
}
public void OnGrounded()
{
_isWallJumpAvailable = true;
if (_simpleDashCooldownTimer.IsStopped())
_simpleDashCooldownTimer.Start();
}
public void DashCooldownTimeout()
{
_canDash = true;
} }
// Physics processes
public void HandleGrounded(float delta) public void HandleGrounded(float delta)
{ {
MoveOnGround(delta); MoveOnGround(delta);
_canDash = true;
if (!isOnFloorCustom()) if (!isOnFloorCustom())
_playerState.SendEvent("start_falling"); _playerState.SendEvent("start_falling");
} }
@ -378,7 +403,6 @@ public partial class PlayerController : CharacterBody3D
if (!WallHugSystem.IsWallHugging()) if (!WallHugSystem.IsWallHugging())
_playerState.SendEvent("start_falling"); _playerState.SendEvent("start_falling");
} }
public void HandleWallHanging(float delta) public void HandleWallHanging(float delta)
{ {
WallHang(delta); WallHang(delta);
@ -448,21 +472,57 @@ public partial class PlayerController : CharacterBody3D
} }
public void OnMegaJumpStarted() public void OnMegaJumpStarted()
{ {
PerformEmpoweredAction();
OnJumpStarted(MegaJumpStartVelocity); OnJumpStarted(MegaJumpStartVelocity);
} }
public Vector3 ComputeHVelocity(float delta, float accelerationFactor, float decelerationFactor) public void WallHug(float delta)
{ {
Vector3 direction = HeadSystem.Transform.Basis * _inputMove; var hvel = ComputeHVelocity(delta, WallHugHorizontalDeceleration, WallHugHorizontalDeceleration);
var acceleration = direction.Length() > 0 ? accelerationFactor : decelerationFactor; var vvel = Velocity.Y - (CalculateGravityForce() * delta / WallHugGravityLesseningFactor);
vvel = Math.Abs(vvel) > WallHugDownwardMaxSpeed ? -WallHugDownwardMaxSpeed : vvel;
Velocity = hvel + vvel*Vector3.Up;
}
public void WallHang(float delta)
{
Velocity = Vector3.Zero;
MoveAndSlide();
}
public void ComputeJumpFromWallHSpeed(float jumpStrength)
{
// if (!_isWallJumpAvailable)
// return;
// _isWallJumpAvailable = false;
// var isLookingTowardsWall = HeadSystem.GetForwardHorizontalVector().Dot(wallNormal) > 0.5;
// var jumpDirection = isLookingTowardsWall ? Vector3.Up : wallNormal;
var wallNormal = WallHugSystem.GetWallNormal().UnwrapOr(Vector3.Up);
var jumpVector = wallNormal * jumpStrength;
SetHorizontalVelocity(new Vector2(jumpVector.X, jumpVector.Z));
}
public void OnJumpFromWall()
{
ComputeJumpFromWallHSpeed(WallJumpStartVelocity);
}
public void OnMegajumpFromWall()
{
ComputeJumpFromWallHSpeed(WallMegajumpStartVelocity);
}
public Vector3 ComputeHVelocity(float delta, float accelerationFactor, float decelerationFactor, Vector3? direction = null)
{
var dir = direction ?? HeadSystem.Transform.Basis * _inputMove;
var acceleration = dir.Length() > 0 ? accelerationFactor : decelerationFactor;
float xAcceleration = Mathf.Lerp(Velocity.X, direction.X * _targetSpeed, delta * acceleration); float xAcceleration = Mathf.Lerp(Velocity.X, dir.X * _targetSpeed, delta * acceleration);
float zAcceleration = Mathf.Lerp(Velocity.Z, direction.Z * _targetSpeed, delta * acceleration); float zAcceleration = Mathf.Lerp(Velocity.Z, dir.Z * _targetSpeed, delta * acceleration);
return new Vector3(xAcceleration, 0, zAcceleration); return new Vector3(xAcceleration, 0, zAcceleration);
} }
public Vector3 ComputeHVelocityGround(float delta) public Vector3 ComputeHVelocityGround(float delta)
{ {
return ComputeHVelocity(delta, AccelerationFloor, DecelerationFloor); return ComputeHVelocity(delta, AccelerationFloor, DecelerationFloor);
} }
public Vector3 ComputeHVelocityAir(float delta) public Vector3 ComputeHVelocityAir(float delta)
@ -477,6 +537,13 @@ public partial class PlayerController : CharacterBody3D
y: verticalVelocity, y: verticalVelocity,
z: Velocity.Z); z: Velocity.Z);
} }
public void SetHorizontalVelocity(Vector2 velocity)
{
Velocity = new Vector3(
x: velocity.X,
y: Velocity.Y,
z: velocity.Y);
}
public void HandleJump(float delta, float gravityFactor, int hangFrames) public void HandleJump(float delta, float gravityFactor, int hangFrames)
{ {
@ -528,19 +595,16 @@ public partial class PlayerController : CharacterBody3D
var progress = (float) (_powerCooldownTimer.TimeLeft / _powerCooldownTimer.WaitTime); var progress = (float) (_powerCooldownTimer.TimeLeft / _powerCooldownTimer.WaitTime);
PowerCooldownIndicator.SetSize(new Vector2(100 * progress, 10)); PowerCooldownIndicator.SetSize(new Vector2(100 * progress, 10));
} }
public void StartPowerCooldown() public void StartPowerCooldown()
{ {
_powerCooldownTimer.Start(); _powerCooldownTimer.Start();
PowerCooldownIndicator.Visible = true; PowerCooldownIndicator.Visible = true;
} }
public void StopPowerCooldown() public void StopPowerCooldown()
{ {
_powerCooldownTimer.Stop(); _powerCooldownTimer.Stop();
PowerCooldownIndicator.Visible = false; PowerCooldownIndicator.Visible = false;
} }
public void PowerCooldownExpired() public void PowerCooldownExpired()
{ {
EmpoweredActionsLeft += 1; EmpoweredActionsLeft += 1;
@ -563,7 +627,6 @@ public partial class PlayerController : CharacterBody3D
{ {
_inputRotateFloorplane = value; _inputRotateFloorplane = value;
} }
public void OnInputAimPressed() public void OnInputAimPressed()
{ {
_playerState.SendEvent("aim_pressed"); _playerState.SendEvent("aim_pressed");
@ -592,11 +655,6 @@ public partial class PlayerController : CharacterBody3D
OnWeaponThrown(); OnWeaponThrown();
} }
} }
public void OnInputDashPressed()
{
_playerState.SendEvent("dash");
PerformDash(_empowerOn.Active);
}
public void OnInputEmpowerDown() public void OnInputEmpowerDown()
{ {
_playerState.SendEvent("empower_down"); _playerState.SendEvent("empower_down");
@ -606,43 +664,69 @@ public partial class PlayerController : CharacterBody3D
_playerState.SendEvent("empower_released"); _playerState.SendEvent("empower_released");
} }
public void PerformDash(bool isEmpowered) public void OnInputDashPressed()
{ {
if (_aiming.Active) if (_empowerOn.Active && CanPerformEmpoweredAction())
{
OnDashStarted();
return;
}
if (!_canDash)
return;
_canDash = false;
var dashStrength = BasicDashStrength;
if (isEmpowered && CanPerformEmpoweredAction())
{ {
PerformEmpoweredAction(); PerformEmpoweredAction();
dashStrength *= 2.5f; _playerState.SendEvent("powered_dash");
return;
} }
_playerState.SendEvent("dash");
}
public void OnSimpleDashStarted()
{
if (!_canDash)
return;
_canDash = false;
var dashStrength = SimpleDashStrength;
var direction = GetInputGlobalHDirection();
SetVelocity(direction * dashStrength);
}
public void HandleSimpleDash(float delta)
{
_playerState.SendEvent("dash_finished");
}
public Vector3 GetInputGlobalHDirection()
{
var direction = HeadSystem.Transform.Basis * _inputMove; var direction = HeadSystem.Transform.Basis * _inputMove;
var planarDirection = new Vector3(direction.X, 0, direction.Z).Normalized(); return new Vector3(direction.X, 0, direction.Z).Normalized();
SetVelocity(planarDirection * dashStrength);
} }
public void Dashing(float delta) public Vector3 GetInputLocalHDirection()
{ {
_playerState.SendEvent("dash_ended"); var direction = _inputMove;
return new Vector3(direction.X, 0, direction.Z).Normalized();
} }
public void OnPoweredDashStarted()
///////////////////////////
// Stateful logic /////////
///////////////////////////
public void OnGrounded()
{ {
_isWallJumpAvailable = true; Velocity = GetInputGlobalHDirection() * PoweredDashStrength;
GetTree().CreateTimer(PoweredDashTime).Timeout += FinishPoweredDash;
}
public void OnPoweredDashFinished()
{
// Try mantling here
}
public void FinishPoweredDash()
{
_playerState.SendEvent("dash_finished");
}
public void HandlePoweredDash(float delta)
{
var collision = MoveAndCollide(Velocity * delta, maxCollisions: 10);
if (collision != null)
{
FinishPoweredDash();
}
} }
public bool CanPerformEmpoweredAction() public bool CanPerformEmpoweredAction()
@ -660,7 +744,7 @@ public partial class PlayerController : CharacterBody3D
// Jumping // Jumping
public void StartCoyoteTime() public void StartCoyoteTime()
{ {
_coyoteTimer.Start(); GetTree().CreateTimer(CoyoteTime).Timeout += CoyoteExpired;
} }
public void CoyoteExpired() public void CoyoteExpired()
{ {
@ -699,25 +783,7 @@ public partial class PlayerController : CharacterBody3D
public void Jump(JumpTypes jumpType, Vector3? jumpDirection = null, float boost = 1.0f) public void Jump(JumpTypes jumpType, Vector3? jumpDirection = null, float boost = 1.0f)
{ {
var effectiveJumpDirection = jumpDirection ?? Vector3.Up; var effectiveJumpDirection = jumpDirection ?? Vector3.Up;
var jumpForce = 0.0f; var jumpForce = 100.0f;
switch (jumpType)
{
case JumpTypes.DoubleJump:
jumpForce = CalculateDoubleJumpForce();
break;
case JumpTypes.SimpleJump:
jumpForce = CalculateJumpForce();
break;
case JumpTypes.JumpFromDash:
jumpForce = CalculateJumpFromDashForce();
break;
case JumpTypes.JumpFromWall:
jumpForce = CalculateJumpFromWallForce();
break;
default:
jumpForce = CalculateJumpForce();
break;
}
var currentHorizontalVelocity = new Vector3(Velocity.X, 0, Velocity.Z); var currentHorizontalVelocity = new Vector3(Velocity.X, 0, Velocity.Z);
var jumpVelocity = jumpForce * effectiveJumpDirection * boost; var jumpVelocity = jumpForce * effectiveJumpDirection * boost;
@ -769,7 +835,6 @@ public partial class PlayerController : CharacterBody3D
} }
PerformEmpoweredAction(); PerformEmpoweredAction();
_timeAfterDashingTimer.Start();
if (WeaponSystem.FlyingState.Active) if (WeaponSystem.FlyingState.Active)
{ {
DashSystem.ShouldMantle = false; DashSystem.ShouldMantle = false;
@ -794,17 +859,6 @@ public partial class PlayerController : CharacterBody3D
_dashDirection = (DashSystem.PlannedPlayerLocation - GlobalPosition).Normalized(); _dashDirection = (DashSystem.PlannedPlayerLocation - GlobalPosition).Normalized();
DashSystem.Dash(); DashSystem.Dash();
} }
public void OnDashProgress(float progress)
{
return;
Engine.SetTimeScale(DashTimeDilationCurve.Sample(progress));
DashIndicator.SetCustomMinimumSize(Vector2.One * DashIndicatorStartSize * (1 - progress));
var indicatorColor = progress < PerfectlyTimedActionTimer ? new Color(1, 1, 1) : new Color(0, 1, 0);
DashIndicator.SetModulate(indicatorColor);
}
public void OnDashEnded() public void OnDashEnded()
{ {
// Regular dash // Regular dash
@ -911,25 +965,7 @@ public partial class PlayerController : CharacterBody3D
var verticalVelocity = Velocity.Y - (CalculateGravityForce() * (float)delta); var verticalVelocity = Velocity.Y - (CalculateGravityForce() * (float)delta);
Velocity = new Vector3(horizontalVelocity.X, verticalVelocity, horizontalVelocity.Z); Velocity = new Vector3(horizontalVelocity.X, verticalVelocity, horizontalVelocity.Z);
} }
public void WallHug(float delta)
{
float xAcceleration = Mathf.Lerp(Velocity.X, 0,
(float)delta * WallHugHorizontalDeceleration);
float zAcceleration = Mathf.Lerp(Velocity.Z, 0,
(float)delta * WallHugHorizontalDeceleration);
Velocity = new Vector3(
x: xAcceleration,
y: -WallHugDownwardSpeed,
z: zAcceleration);
}
public void WallHang(float delta)
{
Velocity = Vector3.Zero;
MoveAndSlide();
}
private void HandleStairs(float delta) private void HandleStairs(float delta)
{ {
StairsSystem.UpStairsCheckParams upStairsCheckParams = new StairsSystem.UpStairsCheckParams StairsSystem.UpStairsCheckParams upStairsCheckParams = new StairsSystem.UpStairsCheckParams
@ -1011,10 +1047,6 @@ public partial class PlayerController : CharacterBody3D
// Helpers //////////////// // Helpers ////////////////
/////////////////////////// ///////////////////////////
public float CalculateJumpForce() => _gravity * StartVelocity;
public float CalculateJumpFromDashForce() => CalculateJumpForce() * JumpFromDashSpeedFactor;
public float CalculateJumpFromWallForce() => CalculateJumpForce() * JumpFromWallSpeedFactor;
public float CalculateDoubleJumpForce() => CalculateJumpForce() * DoubleJumpSpeedFactor;
public float CalculateGravityForce() => _gravity * Weight; public float CalculateGravityForce() => _gravity * Weight;
public void ReduceTimeScaleWhileAiming() public void ReduceTimeScaleWhileAiming()

View File

@ -4,6 +4,9 @@ namespace Movementtests.systems;
public partial class DashSystem: Node3D public partial class DashSystem: Node3D
{ {
public record DashLocation(bool HasHit, Vector3 TargetLocation);
[Export(PropertyHint.Range, "0,0.2,0.01,or_greater")] [Export(PropertyHint.Range, "0,0.2,0.01,or_greater")]
public float DashSpeed { get; set; } = 0.1f; public float DashSpeed { get; set; } = 0.1f;
[Export(PropertyHint.Range, "0,1000,1,or_greater")] [Export(PropertyHint.Range, "0,1000,1,or_greater")]
@ -60,14 +63,14 @@ public partial class DashSystem: Node3D
_dashIndicatorAnim = GetNode<AnimationPlayer>("DashIndicator/AnimationPlayer"); _dashIndicatorAnim = GetNode<AnimationPlayer>("DashIndicator/AnimationPlayer");
} }
private void ComputeDashLocation() private DashLocation ComputeDashLocation()
{ {
TargetLocation = _dashCast3D.ToGlobal(_dashCast3D.TargetPosition);
HasHit = _dashCast3D.IsColliding(); var targetLocation = _dashCast3D.ToGlobal(_dashCast3D.TargetPosition);
if (!HasHit) var hasHit = _dashCast3D.IsColliding();
if (!hasHit)
{ {
PlannedPlayerLocation = TargetLocation; return new DashLocation(false, targetLocation);
return;
} }
CollisionPoint = _dashCast3D.GetCollisionPoint(0); CollisionPoint = _dashCast3D.GetCollisionPoint(0);
@ -80,12 +83,23 @@ public partial class DashSystem: Node3D
// Pushes the point down when dashing to under a platform so head doesn't clip // Pushes the point down when dashing to under a platform so head doesn't clip
var maxPushDownDistance = 0.9f; var maxPushDownDistance = 0.9f;
var correctionProportion = (float) Mathf.Remap(CollisionNormal.Y, -0.5, -1, 0, 1); var correctionProportion = (float) Mathf.Remap(CollisionNormal.Y, -0.5, -1, 0, 1);
var proportion = (float) Mathf.Remap(_dashCast3D.GlobalRotation.X, 0, 1.57, 0, 1); var proportion = (float)Mathf.Remap(_dashCast3D.GlobalRotation.X, 0, 1.57, 0, 1);
PlannedPlayerLocation = locationAlongPath var finalLocation = locationAlongPath
+ CollisionNormal + CollisionNormal
* maxPushDownDistance * maxPushDownDistance
* Mathf.Clamp(proportion, 0, 1) * Mathf.Clamp(proportion, 0, 1)
* Mathf.Clamp(correctionProportion, 0, 1); * Mathf.Clamp(correctionProportion, 0, 1);
return new DashLocation(true, finalLocation);
}
public DashLocation GetDashLocationInDirection(Vector3 direction)
{
var angle = Mathf.Atan2(direction.X, direction.Z);
GD.Print(angle);
var rotation = _head.Rotation.Y + angle*2.0f;
_dashCast3D.SetRotation(new Vector3(0, rotation, 0));
return ComputeDashLocation();
} }
public void PrepareDash() public void PrepareDash()
@ -97,7 +111,7 @@ public partial class DashSystem: Node3D
_head.Rotation.Y, _head.Rotation.Y,
_camera.Rotation.Z)); _camera.Rotation.Z));
ComputeDashLocation(); (HasHit, PlannedPlayerLocation) = ComputeDashLocation();
ShouldMantle = false; ShouldMantle = false;
var mantleLocation = Vector3.Zero; var mantleLocation = Vector3.Zero;

View File

@ -5,8 +5,11 @@ using RustyOptions;
namespace Movementtests.systems; namespace Movementtests.systems;
public partial class WallHugSystem : Node3D public partial class WallHugSystem : Node3D
{ {
[Signal]
public delegate void WallDetectedEventHandler();
private List<RayCast3D> _raycasts; private List<RayCast3D> _raycasts;
@ -19,6 +22,13 @@ public partial class WallHugSystem : Node3D
_raycasts.Add(GetNode<RayCast3D>("right")); _raycasts.Add(GetNode<RayCast3D>("right"));
} }
public override void _PhysicsProcess(double delta)
{
base._PhysicsProcess(delta);
if (IsWallHugging())
EmitSignal(SignalName.WallDetected);
}
public bool IsWallHugging() public bool IsWallHugging()
{ {
foreach (RayCast3D raycast in _raycasts) foreach (RayCast3D raycast in _raycasts)