From b792e8721cc77f3a572db1964f6681b5bc8600c5 Mon Sep 17 00:00:00 2001 From: Minimata Date: Fri, 19 Dec 2025 18:59:34 +0100 Subject: [PATCH] mantle system fix amazing --- maps/city.tscn | 8 +- player_controller/PlayerController.tscn | 40 ++++---- player_controller/Scripts/PlayerController.cs | 91 +++++++++++++------ scenes/path/Path.cs | 25 +++++ scenes/path/Path.cs.uid | 1 + scenes/path/path.tscn | 13 +++ systems/mantle/MantleSystem.cs | 36 +++----- systems/mantle/mantle_system.tscn | 18 ++-- 8 files changed, 156 insertions(+), 76 deletions(-) create mode 100644 scenes/path/Path.cs create mode 100644 scenes/path/Path.cs.uid create mode 100644 scenes/path/path.tscn diff --git a/maps/city.tscn b/maps/city.tscn index 49f57f1b..9682bb92 100644 --- a/maps/city.tscn +++ b/maps/city.tscn @@ -1,8 +1,7 @@ -[gd_scene load_steps=41 format=3 uid="uid://dmkw8cmalm5k"] +[gd_scene load_steps=40 format=3 uid="uid://dmkw8cmalm5k"] [ext_resource type="PackedScene" uid="uid://bei4nhkf8lwdo" path="res://player_controller/PlayerController.tscn" id="1_2vsi6"] [ext_resource type="Script" uid="uid://blenis2y55fmg" path="res://tools/city_helpers.gd" id="1_qwuk2"] -[ext_resource type="Resource" uid="uid://bl5crtu1gkrtr" path="res://systems/inputs/base_mode/base_mode.tres" id="2_p287n"] [ext_resource type="Texture2D" uid="uid://ca4kkq3w8cd4n" path="res://assets/sky/sky_15_2k.png" id="2_ruo5i"] [ext_resource type="PackedScene" uid="uid://dkr80d2pi0d41" path="res://addons/guide/debugger/guide_debugger.tscn" id="2_uet8a"] [ext_resource type="Script" uid="uid://cyh0d64pfygbl" path="res://addons/maaacks_game_template/base/scripts/pause_menu_controller.gd" id="7_ukfuy"] @@ -92,7 +91,6 @@ adjustment_enabled = true [node name="Main" type="Node3D"] script = ExtResource("1_qwuk2") -base_mode = ExtResource("2_p287n") [node name="BackgroundMusicPlayer" parent="." instance=ExtResource("9_i2xii")] stream = ExtResource("10_eca4n") @@ -141,6 +139,7 @@ theme_override_constants/margin_bottom = 10 [node name="TutoMoveAndLook" type="VBoxContainer" parent="TutorialController/PanelContainer/MarginContainer"] unique_name_in_owner = true +visible = false layout_mode = 2 theme_override_constants/separation = 0 @@ -376,9 +375,10 @@ shadow_opacity = 0.95 shadow_blur = 2.435 [node name="Player" parent="." node_paths=PackedStringArray("TutorialWeaponTarget") instance=ExtResource("1_2vsi6")] -transform = Transform3D(0.054514527, 0, -0.9985129, 0, 1, 0, 0.9985129, 0, 0.054514527, 0, -132.75, 118) +transform = Transform3D(0.054514527, 0, -0.9985129, 0, 1, 0, 0.9985129, 0, 0.054514527, -6, 75.5, -13.5) collision_layer = 17 TutorialWeaponTarget = NodePath("../PlacedTutorialWeapon/WeaponLocationTarget") +TutorialDone = true AccelerationAir = 1.5 [node name="DebugLayer" type="CanvasLayer" parent="."] diff --git a/player_controller/PlayerController.tscn b/player_controller/PlayerController.tscn index 9867d443..6859afab 100644 --- a/player_controller/PlayerController.tscn +++ b/player_controller/PlayerController.tscn @@ -65,7 +65,7 @@ WalkSpeed = 7.5 AccelerationAir = 2.0 DecelerationAir = 0.1 Weight = 5.0 -MantleTime = 0.3 +MantleTime = 0.2 CoyoteTime = 0.3 SimpleJumpStartVelocity = 8.0 SimpleJumpHangTimeInFrames = 1 @@ -502,45 +502,45 @@ delay_in_seconds = "0.0" script = ExtResource("26_infe6") initial_state = NodePath("SimpleJump") +[node name="OnMantle" type="Node" parent="StateChart/Root/Movement/Jump"] +script = ExtResource("28_n7qhm") +to = NodePath("../../Mantling") +event = &"mantle" +delay_in_seconds = "0.0" + [node name="SimpleJump" type="Node" parent="StateChart/Root/Movement/Jump"] script = ExtResource("27_34snm") -[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Jump/SimpleJump"] -script = ExtResource("28_n7qhm") -to = NodePath("../../MegaJump") -event = &"megajump" -delay_in_seconds = "0.0" - [node name="OnJumpEnded" type="Node" parent="StateChart/Root/Movement/Jump/SimpleJump"] script = ExtResource("28_n7qhm") to = NodePath("../../../Airborne/DoubleJumpEnabled") event = &"jump_ended" delay_in_seconds = "0.0" -[node name="DoubleJump" type="Node" parent="StateChart/Root/Movement/Jump"] -script = ExtResource("27_34snm") - -[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Jump/DoubleJump"] +[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Jump/SimpleJump"] script = ExtResource("28_n7qhm") to = NodePath("../../MegaJump") event = &"megajump" delay_in_seconds = "0.0" +[node name="DoubleJump" type="Node" parent="StateChart/Root/Movement/Jump"] +script = ExtResource("27_34snm") + [node name="OnJumpEnded" type="Node" parent="StateChart/Root/Movement/Jump/DoubleJump"] script = ExtResource("28_n7qhm") to = NodePath("../../../Airborne/Falling") event = &"jump_ended" delay_in_seconds = "0.0" +[node name="OnMegajump" type="Node" parent="StateChart/Root/Movement/Jump/DoubleJump"] +script = ExtResource("28_n7qhm") +to = NodePath("../../MegaJump") +event = &"megajump" +delay_in_seconds = "0.0" + [node name="MegaJump" type="Node" parent="StateChart/Root/Movement/Jump"] script = ExtResource("27_34snm") -[node name="OnJumpEnded" type="Node" parent="StateChart/Root/Movement/Jump/MegaJump"] -script = ExtResource("28_n7qhm") -to = NodePath("../../../Airborne/Falling") -event = &"jump_ended" -delay_in_seconds = "0.0" - [node name="Dashing" type="Node" parent="StateChart/Root/Movement"] script = ExtResource("26_infe6") initial_state = NodePath("Dash") @@ -707,6 +707,12 @@ to = NodePath("../../Airborne/Reset") event = &"start_falling" delay_in_seconds = "0.0" +[node name="OnMantle" type="Node" parent="StateChart/Root/Movement/OnWall"] +script = ExtResource("28_n7qhm") +to = NodePath("../../Mantling") +event = &"mantle" +delay_in_seconds = "0.0" + [node name="HuggingCoyoteEnabled" type="Node" parent="StateChart/Root/Movement/OnWall"] script = ExtResource("27_34snm") diff --git a/player_controller/Scripts/PlayerController.cs b/player_controller/Scripts/PlayerController.cs index fc100337..ef4c3483 100644 --- a/player_controller/Scripts/PlayerController.cs +++ b/player_controller/Scripts/PlayerController.cs @@ -81,6 +81,8 @@ public partial class PlayerController : CharacterBody3D [ExportGroup("Mantle")] [Export(PropertyHint.Range, "0,1,0.01,or_greater")] public float MantleTime { get; set; } = 0.1f; + [Export] + public PackedScene MantlePath { get; set; } = GD.Load("res://scenes/path/Path.tscn"); // Jump [ExportGroup("Jump")] @@ -186,6 +188,11 @@ public partial class PlayerController : CharacterBody3D private bool _canDash = true; private bool _shouldMantleOnDashEnded; + private Vector3 _wallHugStartLocation = Vector3.Zero; + private Vector3 _wallHugStartNormal = Vector3.Zero; + private Vector3 _wallHugStartProjectedVelocity = Vector3.Zero; + private Vector3 _currentWallContactPoint = Vector3.Zero; + private StateChart _playerState; private StateChartState _aiming; @@ -197,7 +204,7 @@ public partial class PlayerController : CharacterBody3D private StateChartState _grounded; private StateChartState _airborne; private StateChartState _coyoteEnabled; - private StateChartState _doubleJumpEnabled; + // private StateChartState _doubleJumpEnabled; private StateChartState _simpleJump; private StateChartState _doubleJump; private StateChartState _megaJump; @@ -221,6 +228,7 @@ public partial class PlayerController : CharacterBody3D private float _playerHeight; private float _playerRadius; + private bool _isJumpInputPressed = false; private float _lookSensitivityMultiplier = 1.0f; private float _mouseSensitivityMultiplier = 1.0f; @@ -294,7 +302,7 @@ public partial class PlayerController : CharacterBody3D _grounded = StateChartState.Of(GetNode("StateChart/Root/Movement/Grounded")); _airborne = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne")); _coyoteEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/CoyoteEnabled")); - _doubleJumpEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/DoubleJumpEnabled")); + // _doubleJumpEnabled = StateChartState.Of(GetNode("StateChart/Root/Movement/Airborne/DoubleJumpEnabled")); _simpleJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Jump/SimpleJump")); _doubleJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Jump/DoubleJump")); _megaJump = StateChartState.Of(GetNode("StateChart/Root/Movement/Jump/MegaJump")); @@ -351,6 +359,7 @@ public partial class PlayerController : CharacterBody3D _grounded.StateEntered += OnGrounded; _grounded.StatePhysicsProcessing += HandleGrounded; _airborne.StatePhysicsProcessing += HandleAirborne; + _onWall.StatePhysicsProcessing += HandleOnWall; _coyoteEnabled.StateEntered += StartCoyoteTime; _timeScaleAimInAirTimer.Timeout += ResetTimeScale; @@ -454,20 +463,29 @@ public partial class PlayerController : CharacterBody3D { _canDash = true; } + + public bool IsTryingToMantle() + { + return MantleSystem.IsMantlePossible && GetMoveInput().Z < -0.5f && _isJumpInputPressed; + } public void HandleGrounded(float delta) { MoveOnGround(delta); + // if (IsTryingToMantle()) _playerState.SendEvent("mantle"); if (!isOnFloorCustom()) _playerState.SendEvent("start_falling"); } public void HandleAirborne(float delta) { + MoveInAir(delta); if (isOnFloorCustom()) _playerState.SendEvent("grounded"); + if (IsTryingToMantle()) _playerState.SendEvent("mantle"); + if (!WallHugSystem.IsWallHugging()) return; @@ -497,6 +515,11 @@ public partial class PlayerController : CharacterBody3D } } + public void HandleOnWall(float delta) + { + if (IsTryingToMantle()) _playerState.SendEvent("mantle"); + } + public void OnWallHuggingStarted() { GetTree().CreateTimer(CoyoteTime).Timeout += CoyoteExpired; @@ -507,11 +530,6 @@ public partial class PlayerController : CharacterBody3D GetTree().CreateTimer(CoyoteTime).Timeout += CoyoteExpired; } - private Vector3 _wallHugStartLocation = Vector3.Zero; - private Vector3 _wallHugStartNormal = Vector3.Zero; - private Vector3 _wallHugStartProjectedVelocity = Vector3.Zero; - private Vector3 _currentWallContactPoint = Vector3.Zero; - public void OnWallDetected() { FinishPoweredDash(); @@ -591,10 +609,11 @@ public partial class PlayerController : CharacterBody3D Velocity = Vector3.Zero; GlobalPosition = _wallHugStartLocation; } - + // Jump public void OnInputJumpStarted() { + _isJumpInputPressed = true; if (MantleSystem.IsMantlePossible) { _playerState.SendEvent("mantle"); @@ -620,6 +639,7 @@ public partial class PlayerController : CharacterBody3D public void OnInputJumpEnded() { + _isJumpInputPressed = false; _playerState.SendEvent("jump_ended"); } @@ -725,38 +745,48 @@ public partial class PlayerController : CharacterBody3D z: velocity.Y); } + private Path _mantlePath; public void OnMantleStarted() { HeadSystem.OnMantle(); + + _mantlePath = MantlePath.Instantiate() as Path; + if (_mantlePath == null) + { + GD.PrintErr("Failed to instantiate MantlePath"); + return; + }; - _preMantleVelocity = Velocity; + GetTree().GetRoot().AddChild(_mantlePath); + _mantlePath.Setup(MantleSystem.GlobalTransform, MantleSystem.MantleCurve); var tween = GetTree().CreateTween(); - tween.SetTrans(Tween.TransitionType.Cubic); + tween.SetTrans(Tween.TransitionType.Linear); tween.SetEase(Tween.EaseType.InOut); - var inbetweenDuration = MantleTime / MantleSystem.NumberOfValidPofiles; - for (int i = 0; i < MantleSystem.NumberOfValidPofiles; i += 1) - { - var position = MantleSystem.WallProfilePositions[i]; - tween.TweenProperty(this, "global_position", position, inbetweenDuration); - } - + tween.TweenProperty(_mantlePath.PathFollow, "progress_ratio", 1, MantleTime); tween.Finished += MantleFinished; } public void HandleMantling(float delta) { - + GlobalPosition = _mantlePath.Target.GlobalPosition; } public void MantleFinished() { - Velocity = _preMantleVelocity; + _mantlePath.Teardown(); + var direction = GetInputGlobalHDirection(); + if (direction.Length() > 0) + { + SetVelocity(direction * SimpleDashStrength); + } _playerState.SendEvent("grounded"); } public void HandleJump(float delta, float gravityFactor, int hangFrames) { + if (IsTryingToMantle()) _playerState.SendEvent("mantle"); + // Update horizontal velocity var horizontalVelocity = ComputeHVelocityAir(delta); Velocity = new Vector3(horizontalVelocity.X, Velocity.Y, horizontalVelocity.Z); @@ -986,9 +1016,7 @@ public partial class PlayerController : CharacterBody3D public void RecoverWeapon() { - GetTree().GetRoot().RemoveChild(WeaponRoot); - AddChild(WeaponRoot); - WeaponRoot.SetGlobalPosition(GlobalPosition); + RecoverChildNode(WeaponRoot); WeaponSystem.ResetWeapon(); } @@ -1051,14 +1079,25 @@ public partial class PlayerController : CharacterBody3D WeaponRoot.CallDeferred(Node3D.MethodName.SetGlobalPosition, TutorialWeaponTarget.GlobalPosition); WeaponSystem.CallDeferred(WeaponSystem.MethodName.PlaceWeaponForTutorial, TutorialWeaponTarget.GlobalPosition); } + + public void RemoveChildNode(Node3D node) + { + RemoveChild(node); + GetTree().GetRoot().AddChild(node); + node.SetGlobalPosition(GlobalPosition); + } + + public void RecoverChildNode(Node3D node) + { + GetTree().GetRoot().RemoveChild(node); + AddChild(node); + node.SetGlobalPosition(GlobalPosition); + } public void ThrowWeapon() { _playerState.SendEvent("cancel_aim"); - - RemoveChild(WeaponRoot); - GetTree().GetRoot().AddChild(WeaponRoot); - WeaponRoot.SetGlobalPosition(GlobalPosition); + RemoveChildNode(WeaponRoot); var weaponTargetLocation = DashSystem.HasHit ? DashSystem.CollisionPoint : DashSystem.PlannedLocation; WeaponSystem.ThrowWeapon( diff --git a/scenes/path/Path.cs b/scenes/path/Path.cs new file mode 100644 index 00000000..fae98bcf --- /dev/null +++ b/scenes/path/Path.cs @@ -0,0 +1,25 @@ +using Godot; + +public partial class Path : Path3D +{ + public PathFollow3D PathFollow { get; private set; } + public Marker3D Target { get; private set; } + + public override void _Ready() + { + PathFollow = GetNode("PathFollow"); + Target = GetNode("PathFollow/Target"); + } + + public void Setup(Transform3D globalTransform, Curve3D curve) + { + SetGlobalTransform(globalTransform); + SetCurve(curve); + PathFollow.ProgressRatio = 0; + } + + public void Teardown() + { + QueueFree(); + } +} \ No newline at end of file diff --git a/scenes/path/Path.cs.uid b/scenes/path/Path.cs.uid new file mode 100644 index 00000000..99596f1b --- /dev/null +++ b/scenes/path/Path.cs.uid @@ -0,0 +1 @@ +uid://djdr5bvfc8f0x diff --git a/scenes/path/path.tscn b/scenes/path/path.tscn new file mode 100644 index 00000000..017f3b13 --- /dev/null +++ b/scenes/path/path.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=3 format=3 uid="uid://cf3rrgr1imvv4"] + +[ext_resource type="Script" uid="uid://djdr5bvfc8f0x" path="res://scenes/path/Path.cs" id="1_kmlhi"] + +[sub_resource type="Curve3D" id="Curve3D_u4rfr"] + +[node name="Path" type="Path3D"] +curve = SubResource("Curve3D_u4rfr") +script = ExtResource("1_kmlhi") + +[node name="PathFollow" type="PathFollow3D" parent="."] + +[node name="Target" type="Marker3D" parent="PathFollow"] diff --git a/systems/mantle/MantleSystem.cs b/systems/mantle/MantleSystem.cs index 3893eb8a..4eac9b50 100644 --- a/systems/mantle/MantleSystem.cs +++ b/systems/mantle/MantleSystem.cs @@ -14,17 +14,15 @@ public partial class MantleSystem: Node3D private ShapeCast3D _wallInFrontCast3D; private ShapeCast3D _mantleCast3D; - + private ShapeCast3D _inAirWallDetect; private ShapeCast3D _groundedWallDetect; + public Curve3D MantleCurve { get; private set; } public bool IsMantlePossible { get; private set; } = false; - public int NumberOfValidPofiles { get; private set; } = -1; public const int WallProfileCastCount = 7; private ShapeCast3D[] _wallProfileShapecasts = new ShapeCast3D[WallProfileCastCount]; - public Vector3[] WallProfilePositions { get; private set; } = new Vector3[WallProfileCastCount]; - public Vector3[] WallProfileNormals { get; private set; } = new Vector3[WallProfileCastCount]; public void Init() { @@ -55,21 +53,15 @@ public partial class MantleSystem: Node3D SetCastsEnabled(isColliding); // Reset state - NumberOfValidPofiles = -1; IsMantlePossible = false; - if (!isColliding) - { - return; - } + if (!isColliding) return; // Check if collide with wall var collisionNormal = isGrounded ? _groundedWallDetect.GetCollisionNormal(0) : _inAirWallDetect.GetCollisionNormal(0); - if (collisionNormal.Y > 0.8f) - { - return; - } - - NumberOfValidPofiles = 0; + if (collisionNormal.Y > 0.9f) return; + + MantleCurve = new Curve3D(); + MantleCurve.AddPoint(Vector3.Zero); var hasFirstProfileHit = false; foreach (var wallProfileShapecast in _wallProfileShapecasts) { @@ -81,9 +73,7 @@ public partial class MantleSystem: Node3D // Got to the other side of the wall, we stop there if (!wallProfileShapecast.IsColliding()) { - WallProfilePositions[NumberOfValidPofiles] = globalTargetPosition; - WallProfileNormals[NumberOfValidPofiles] = Vector3.Zero; - NumberOfValidPofiles += 1; + MantleCurve.AddPoint(ToLocal(globalTargetPosition)); break; } @@ -92,16 +82,16 @@ public partial class MantleSystem: Node3D // Check if we collided parallel to a wall var isCollisionSameAsTarget = globalTargetPosition.IsEqualApprox(profilePoint); - var isCollidingWithWall = profileNormal.Y < 0.9f; + var isCollidingWithWall = profileNormal.Y < 0.1f; if (isCollisionSameAsTarget || isCollidingWithWall) continue; // We have a valid collision - WallProfilePositions[NumberOfValidPofiles] = profilePoint; - WallProfileNormals[NumberOfValidPofiles] = profileNormal; hasFirstProfileHit = true; - NumberOfValidPofiles += 1; + MantleCurve.AddPoint(ToLocal(profilePoint)); } - IsMantlePossible = NumberOfValidPofiles > 0; // Should always be true + if (MantleCurve.PointCount == 1) return; + + IsMantlePossible = true; } public Option FindMantle() diff --git a/systems/mantle/mantle_system.tscn b/systems/mantle/mantle_system.tscn index abeea388..c500c62a 100644 --- a/systems/mantle/mantle_system.tscn +++ b/systems/mantle/mantle_system.tscn @@ -1,7 +1,6 @@ -[gd_scene load_steps=6 format=3 uid="uid://wq1okogkhc5l"] +[gd_scene load_steps=7 format=3 uid="uid://wq1okogkhc5l"] [ext_resource type="Script" uid="uid://bja6tis1vaysu" path="res://systems/mantle/MantleSystem.cs" id="1_2oobp"] -[ext_resource type="Shape3D" uid="uid://dp2p8v7demb5j" path="res://systems/mantle/find_wall_shape.tres" id="2_i32qj"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_4coqe"] height = 1.7 @@ -9,6 +8,13 @@ height = 1.7 [sub_resource type="SphereShape3D" id="SphereShape3D_2oobp"] radius = 0.75 +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_2oobp"] +radius = 0.25 + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_i32qj"] +radius = 0.25 +height = 1.5 + [sub_resource type="SphereShape3D" id="SphereShape3D_i32qj"] radius = 0.125 @@ -35,14 +41,14 @@ collision_mask = 2 debug_shape_custom_color = Color(0.911631, 0.11884, 0.656218, 1) [node name="InAirWallDetect" type="ShapeCast3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.26, 0) -shape = ExtResource("2_i32qj") +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.01, 0) +shape = SubResource("CapsuleShape3D_2oobp") target_position = Vector3(0, 0, -2) collision_mask = 2 [node name="GroundedWallDetect" type="ShapeCast3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.76, 0) -shape = ExtResource("2_i32qj") +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.26, 0) +shape = SubResource("CapsuleShape3D_i32qj") target_position = Vector3(0, 0, -2) collision_mask = 2