From 7b4c8c7574b1b000c0931004e9c5c8a8d3d7eec6 Mon Sep 17 00:00:00 2001 From: Kevin Date: Sun, 4 Jan 2026 14:01:19 +0200 Subject: [PATCH] NSANE MANTLE MECHANICS --- ...state-5006effbafa276c09596397f1dedfe17.cfg | 8 +- ...state-90e1ff3baa03524a506ff0f7a9d78dcf.cfg | 10 +- ...lding-90e1ff3baa03524a506ff0f7a9d78dcf.cfg | 2 +- AGame/.godot/editor/create_recent.Node | 1 + AGame/.godot/editor/editor_layout.cfg | 6 +- AGame/.godot/editor/filesystem_cache10 | 12 +- AGame/.godot/editor/filesystem_update4 | 1 - AGame/.godot/editor/project_metadata.cfg | 4 +- AGame/.godot/editor/script_editor_cache.cfg | 4 +- AGame/Floor.tscn | 2 +- AGame/Player.tscn | 16 +- AGame/Scripts/player.gd | 169 ++++++++++++++---- 12 files changed, 176 insertions(+), 59 deletions(-) diff --git a/AGame/.godot/editor/Floor.tscn-editstate-5006effbafa276c09596397f1dedfe17.cfg b/AGame/.godot/editor/Floor.tscn-editstate-5006effbafa276c09596397f1dedfe17.cfg index 5bf1a621..3498dc2e 100644 --- a/AGame/.godot/editor/Floor.tscn-editstate-5006effbafa276c09596397f1dedfe17.cfg +++ b/AGame/.godot/editor/Floor.tscn-editstate-5006effbafa276c09596397f1dedfe17.cfg @@ -114,12 +114,12 @@ Anim={ "listener": true, "lock_rotation": false, "orthogonal": false, -"position": Vector3(-0.7700564, 8.38965, 0.80201924), +"position": Vector3(-10.162007, 11.304508, -11.346103), "transform_gizmo": true, "use_environment": false, "view_type": 0, -"x_rotation": 0.45814914, -"y_rotation": -8.102711 +"x_rotation": 0.5759586, +"y_rotation": -9.202266 }, { "auto_orthogonal": false, "auto_orthogonal_enabled": true, @@ -187,4 +187,4 @@ Anim={ "zfar": 4000.01, "znear": 0.05 } -selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@20438/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@62/@VBoxContainer@63/@EditorMainScreen@103/MainScreen/@CanvasItemEditor@10871/@VSplitContainer@10516/@HSplitContainer@10518/@HSplitContainer@10520/@Control@10521/@SubViewportContainer@10522/@SubViewport@10523/Node3D")]) +selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@20438/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@62/@VBoxContainer@63/@EditorMainScreen@103/MainScreen/@CanvasItemEditor@10871/@VSplitContainer@10516/@HSplitContainer@10518/@HSplitContainer@10520/@Control@10521/@SubViewportContainer@10522/@SubViewport@10523/Node3D/Box2")]) diff --git a/AGame/.godot/editor/Player.tscn-editstate-90e1ff3baa03524a506ff0f7a9d78dcf.cfg b/AGame/.godot/editor/Player.tscn-editstate-90e1ff3baa03524a506ff0f7a9d78dcf.cfg index c1e4bc06..ac11ef4c 100644 --- a/AGame/.godot/editor/Player.tscn-editstate-90e1ff3baa03524a506ff0f7a9d78dcf.cfg +++ b/AGame/.godot/editor/Player.tscn-editstate-90e1ff3baa03524a506ff0f7a9d78dcf.cfg @@ -104,7 +104,7 @@ Anim={ "auto_orthogonal_enabled": true, "cinematic_preview": false, "display_mode": 22, -"distance": 3.4293556, +"distance": 4.0000005, "doppler": false, "frame_time": false, "gizmos": true, @@ -114,12 +114,12 @@ Anim={ "listener": true, "lock_rotation": false, "orthogonal": true, -"position": Vector3(0.25380337, 0.7973664, 1.5201828), +"position": Vector3(-2.2754533, 1.4412576, -0.76566285), "transform_gizmo": true, "use_environment": false, -"view_type": 6, +"view_type": 4, "x_rotation": 0.0, -"y_rotation": 3.1415927 +"y_rotation": -1.5707964 }, { "auto_orthogonal": false, "auto_orthogonal_enabled": true, @@ -187,4 +187,4 @@ Anim={ "zfar": 4000.01, "znear": 0.05 } -selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@20438/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@62/@VBoxContainer@63/@EditorMainScreen@103/MainScreen/@CanvasItemEditor@10871/@VSplitContainer@10516/@HSplitContainer@10518/@HSplitContainer@10520/@Control@10521/@SubViewportContainer@10522/@SubViewport@10523/Player/OnAir")]) +selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@20438/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@62/@VBoxContainer@63/@EditorMainScreen@103/MainScreen/@CanvasItemEditor@10871/@VSplitContainer@10516/@HSplitContainer@10518/@HSplitContainer@10520/@Control@10521/@SubViewportContainer@10522/@SubViewport@10523/Player/LedgeForwardRay")]) diff --git a/AGame/.godot/editor/Player.tscn-folding-90e1ff3baa03524a506ff0f7a9d78dcf.cfg b/AGame/.godot/editor/Player.tscn-folding-90e1ff3baa03524a506ff0f7a9d78dcf.cfg index 02a487f1..11aa09d2 100644 --- a/AGame/.godot/editor/Player.tscn-folding-90e1ff3baa03524a506ff0f7a9d78dcf.cfg +++ b/AGame/.godot/editor/Player.tscn-folding-90e1ff3baa03524a506ff0f7a9d78dcf.cfg @@ -1,5 +1,5 @@ [folding] -node_unfolds=[NodePath("CameraPivot/Camera3D"), PackedStringArray("Transform"), NodePath("OnAir"), PackedStringArray("shape")] +node_unfolds=[NodePath("CameraPivot/Camera3D"), PackedStringArray("Transform"), NodePath("ClimbCollider"), PackedStringArray("shape"), NodePath("LedgeForwardRay"), PackedStringArray("Transform")] resource_unfolds=["res://Player.tscn::CapsuleMesh_kpjcp", PackedStringArray(), "res://Player.tscn::CapsuleShape3D_kne1u", PackedStringArray(), "res://Player.tscn::CapsuleShape3D_xhfnw", PackedStringArray()] nodes_folded=[NodePath("CameraPivot")] diff --git a/AGame/.godot/editor/create_recent.Node b/AGame/.godot/editor/create_recent.Node index 1efacbb7..c65e3b72 100644 --- a/AGame/.godot/editor/create_recent.Node +++ b/AGame/.godot/editor/create_recent.Node @@ -1,3 +1,4 @@ +RayCast3D Panel CanvasLayer CollisionShape3D diff --git a/AGame/.godot/editor/editor_layout.cfg b/AGame/.godot/editor/editor_layout.cfg index 8ade9ad3..08f85e21 100644 --- a/AGame/.godot/editor/editor_layout.cfg +++ b/AGame/.godot/editor/editor_layout.cfg @@ -28,9 +28,9 @@ dock_5="Inspector,Node,History" [EditorNode] -open_scenes=PackedStringArray("res://DevTest.tscn", "res://Player.tscn") -current_scene="res://Player.tscn" -center_split_offset=0 +open_scenes=PackedStringArray("res://DevTest.tscn", "res://Player.tscn", "res://Floor.tscn") +current_scene="res://Floor.tscn" +center_split_offset=-257 selected_default_debugger_tab_idx=0 selected_main_editor_idx=2 selected_bottom_panel_item=0 diff --git a/AGame/.godot/editor/filesystem_cache10 b/AGame/.godot/editor/filesystem_cache10 index fcd4d3d4..154ea015 100644 --- a/AGame/.godot/editor/filesystem_cache10 +++ b/AGame/.godot/editor/filesystem_cache10 @@ -1,8 +1,8 @@ 63f7b34db8d8cdea90c76aacccf841ec -::res://::1767487807 -DevTest.tscn::PackedScene::9123247578013174228::1767411651::0::1::::<><><>0<>0<><>::uid://grcsjnqdsbmj::::res://Floor.tscn<>uid://dsj41jw17qh40::::res://Player.tscn -Floor.tscn::PackedScene::456381555447325093::1767482397::0::1::::<><><>0<>0<><>::uid://dwdp3f073q201::::res://assets/Materials/stone_pathway/textures/stone_pathway_02_diff_1k.png<>uid://dth2ctcqe0nri::::res://assets/Materials/stone_pathway/textures/stone_pathway_02_nor_gl_1k.png<>uid://c8vnh4apwdyy6::::res://assets/Materials/stone_pathway/textures/stone_pathway_02_rough_1k.png<>uid://c6c1fcbsll0mm::::res://assets/Materials/wood_planks/textures/wood_planks_diff_1k.png<>uid://dwxtktkem6kgb::::res://assets/Materials/wood_planks/textures/wood_planks_nor_gl_1k.png<>uid://54xt1ehp2lqm::::res://assets/Materials/wood_planks/textures/wood_planks_rough_1k.png -Player.tscn::PackedScene::8443048367702749007::1767481734::0::1::::<><><>0<>0<><>::uid://b8ddkhlwoiuvm::::res://Scripts/player.gd +::res://::1767524602 +DevTest.tscn::PackedScene::9123247578013174228::1767488108::0::1::::<><><>0<>0<><>::uid://grcsjnqdsbmj::::res://Floor.tscn<>uid://dsj41jw17qh40::::res://Player.tscn +Floor.tscn::PackedScene::456381555447325093::1767487883::0::1::::<><><>0<>0<><>::uid://dwdp3f073q201::::res://assets/Materials/stone_pathway/textures/stone_pathway_02_diff_1k.png<>uid://dth2ctcqe0nri::::res://assets/Materials/stone_pathway/textures/stone_pathway_02_nor_gl_1k.png<>uid://c8vnh4apwdyy6::::res://assets/Materials/stone_pathway/textures/stone_pathway_02_rough_1k.png<>uid://c6c1fcbsll0mm::::res://assets/Materials/wood_planks/textures/wood_planks_diff_1k.png<>uid://dwxtktkem6kgb::::res://assets/Materials/wood_planks/textures/wood_planks_nor_gl_1k.png<>uid://54xt1ehp2lqm::::res://assets/Materials/wood_planks/textures/wood_planks_rough_1k.png +Player.tscn::PackedScene::8443048367702749007::1767489077::0::1::::<><><>0<>0<><>::uid://b8ddkhlwoiuvm::::res://Scripts/player.gd ::res://assets/::1767416168 icon.svg::CompressedTexture2D::347234531240620840::1767410708::1767410719::1::::<><><>0<>0<>cd8caee1c8a7b4b3cbd67faa4c96c3c0<>res://.godot/imported/icon.svg-56083ea2a1f1a4f1e49773bdc6d7826c.ctex:: ::res://assets/Materials/::1767481029 @@ -18,5 +18,5 @@ wood_planks_diff_1k.png::CompressedTexture2D::6954411953879281808::1767480996::1 wood_planks_disp_1k.png::CompressedTexture2D::189196940330241145::1767480998::1767481050::1::::<><><>0<>0<>322da655e040dd3a43fbb683f8f6268a<>res://.godot/imported/wood_planks_disp_1k.png-210b824a140b4c19e9a2cb4661fb0a7e.ctex:: wood_planks_nor_gl_1k.png::CompressedTexture2D::8752069793851707053::1767481002::1767481442::1::::<><><>0<>0<>6f26dd0a61f6c720bb5117cbe149e0db<>res://.godot/imported/wood_planks_nor_gl_1k.png-d024cbb03b846bef5796ea2d214c03f5.s3tc.ctex:: wood_planks_rough_1k.png::CompressedTexture2D::2166964057052015616::1767481004::1767481487::1::::<><><>0<>0<>0cffb3c51099170e58ad0c6876de9261<>res://.godot/imported/wood_planks_rough_1k.png-1147de2f8d0257affd8f6fc6e09389b5.s3tc.ctex:: -::res://Scripts/::1767482349 -player.gd::GDScript::4709046051602623302::1767482349::0::1::::<>CharacterBody3D<><>0<>0<><>:: +::res://Scripts/::1767488944 +player.gd::GDScript::4709046051602623302::1767488944::0::1::::<>CharacterBody3D<><>0<>0<><>:: diff --git a/AGame/.godot/editor/filesystem_update4 b/AGame/.godot/editor/filesystem_update4 index 90527903..f78d968d 100644 --- a/AGame/.godot/editor/filesystem_update4 +++ b/AGame/.godot/editor/filesystem_update4 @@ -1,4 +1,3 @@ res://Floor.tscn res://Player.tscn -res://DevTest.tscn res://Scripts/player.gd diff --git a/AGame/.godot/editor/project_metadata.cfg b/AGame/.godot/editor/project_metadata.cfg index 90b26896..4514a4a0 100644 --- a/AGame/.godot/editor/project_metadata.cfg +++ b/AGame/.godot/editor/project_metadata.cfg @@ -1,7 +1,7 @@ [game_view] select_mode=1 -embed_on_play=true +embed_on_play=false [editor_metadata] @@ -15,7 +15,7 @@ search_help=Rect2(1920, 300, 960, 600) [recent_files] -scenes=["res://Player.tscn", "res://DevTest.tscn", "res://node_3d.tscn", "res://Floor.tscn"] +scenes=["res://Floor.tscn", "res://Player.tscn", "res://DevTest.tscn", "res://node_3d.tscn"] scripts=["res://Scripts/player.gd"] [script_setup] diff --git a/AGame/.godot/editor/script_editor_cache.cfg b/AGame/.godot/editor/script_editor_cache.cfg index 92e18ad9..5d02079e 100644 --- a/AGame/.godot/editor/script_editor_cache.cfg +++ b/AGame/.godot/editor/script_editor_cache.cfg @@ -6,8 +6,8 @@ state={ "column": 28, "folded_lines": Array[int]([]), "h_scroll_position": 0, -"row": 95, -"scroll_position": 78.0, +"row": 303, +"scroll_position": 284.0, "selection": false, "syntax_highlighter": "GDScript" } diff --git a/AGame/Floor.tscn b/AGame/Floor.tscn index 40f82fb0..3a0cd278 100644 --- a/AGame/Floor.tscn +++ b/AGame/Floor.tscn @@ -55,7 +55,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1.0000001, -0.0024477243, -0.001 shape = SubResource("BoxShape3D_mb0cl") [node name="Box2" type="StaticBody3D" parent="."] -transform = Transform3D(5, 0, 0, 0, 5, 0, 0, 0, 5, -16.142433, 1.5642476, 0) +transform = Transform3D(5, 0, 0, 0, 5, 0, 0, 0, 5, -14.108793, 0.0832777, 0) [node name="CSGMesh3D" type="CSGMesh3D" parent="Box2"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.029260352, 0) diff --git a/AGame/Player.tscn b/AGame/Player.tscn index 9cfa993c..c1d42614 100644 --- a/AGame/Player.tscn +++ b/AGame/Player.tscn @@ -9,8 +9,8 @@ radius = 0.49804688 height = 1.9969274 [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_xhfnw"] -radius = 0.49804688 -height = 1.0760319 +radius = 0.58447266 +height = 1.310791 [node name="Player" type="CharacterBody3D"] script = ExtResource("1_xhfnw") @@ -30,6 +30,14 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.54473054, 0) current = true fov = 52.5 -[node name="OnAir" type="CollisionShape3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.46046007, 0) +[node name="ClimbCollider" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.3495748, 0) +visible = false shape = SubResource("CapsuleShape3D_xhfnw") +disabled = true + +[node name="LedgeForwardRay" type="RayCast3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +target_position = Vector3(0, 0, -1.4) + +[node name="LedgeDownRay" type="RayCast3D" parent="."] diff --git a/AGame/Scripts/player.gd b/AGame/Scripts/player.gd index 2938ed63..0de2ae40 100644 --- a/AGame/Scripts/player.gd +++ b/AGame/Scripts/player.gd @@ -1,46 +1,65 @@ extends CharacterBody3D -@export var mouse_sensitivity := 1.0 +@export var mouse_sensitivity = 1.0 +# =================== +# Player +# =================== +@export var capsule_height = 1.8 +@export var mantle_forward_offset = 0.6 + + + # ===================== # Movement # ===================== -@export var walk_speed := 10.0 -@export var crouch_speed := 5.0 -@export var slide_speed := 18.0 -@export var jump_velocity := 8.5 +@export var walk_speed = 10.0 +@export var crouch_speed = 5.0 +@export var slide_speed= 18.0 +@export var jump_velocity = 8.5 -@export var ground_friction := 35.0 -@export var slide_friction := 15.0 +@export var ground_friction = 35.0 +@export var slide_friction = 15.0 # ===== Air Control -@export var air_turn_speed := 7.0 -@export var air_input_strength := 50.0 -@export var air_drag := 0.5 -@export var max_air_speed := 15.0 +@export var air_turn_speed = 7.0 +@export var air_input_strength = 50.0 +@export var air_drag = 0.5 +@export var max_air_speed = 15.0 # ===================== # Camera # ===================== -@export var base_fov := 70.0 -const SLIDE_FOV_ADD := 35.0 +@export var base_fov = 70.0 +const SLIDE_FOV_ADD = 35.0 -@export var crouch_height := 0.5 -@export var stand_height := 1.0 +@export var crouch_height = 0.5 +@export var stand_height = 1.0 + +const MAX_LOOK_UP = deg_to_rad(80) +const MAX_LOOK_DOWN= deg_to_rad(-80) +# ===================== +# Mantle +# ===================== + +@export var mantle_max_height = 4.0 +@export var mantle_min_height = 0.2 +@export var mantle_speed = 14.0 + +var is_mantling = false +var mantle_target = Vector3.ZERO -const MAX_LOOK_UP := deg_to_rad(80) -const MAX_LOOK_DOWN := deg_to_rad(-80) # ===================== # Stuff # ===================== -var is_crouching := false -var is_sliding := false -var preserve_air_momentum := false -var just_jumped := false +var is_crouching = false +var is_sliding = false +var preserve_air_momentum = false +var just_jumped = false -var pitch := 0.0 -var slide_direction := Vector3.ZERO +var pitch = 0.0 +var slide_direction = Vector3.ZERO @onready var camera_pivot: Node3D = $CameraPivot @onready var camera: Camera3D = $CameraPivot/Camera3D @@ -69,6 +88,21 @@ func _physics_process(delta): target_height, 10.0 * delta ) + + if is_mantling: + global_position = global_position.move_toward( + mantle_target, + mantle_speed * delta +) + + + + if global_position.distance_to(mantle_target) < 0.05: + is_mantling = false + + return + + # Gravity if not is_on_floor(): @@ -97,6 +131,7 @@ func _physics_process(delta): apply_air_control(input_dir, delta) move_and_slide() + try_mantle(delta) func _input(event): if event.is_action_pressed("ui_cancel"): @@ -138,7 +173,7 @@ func handle_ground(input_dir: Vector3, delta): camera.fov = lerp(camera.fov, base_fov, 10.0 * delta) if input_dir == Vector3.ZERO: - var horizontal := Vector3(velocity.x, 0, velocity.z) + var horizontal = Vector3(velocity.x, 0, velocity.z) horizontal = horizontal.move_toward(Vector3.ZERO, ground_friction * delta) velocity.x = horizontal.x velocity.z = horizontal.z @@ -159,11 +194,11 @@ func stop_crouch(): func apply_air_control(input_dir: Vector3, delta): - var horizontal := Vector3(velocity.x, 0, velocity.z) + var horizontal = Vector3(velocity.x, 0, velocity.z) - var forward := -global_transform.basis.z - var right := global_transform.basis.x + var forward = -global_transform.basis.z + var right = global_transform.basis.x forward.y = 0 right.y = 0 forward = forward.normalized() @@ -171,11 +206,11 @@ func apply_air_control(input_dir: Vector3, delta): if input_dir != Vector3.ZERO: - var air_wish_dir := (forward * input_dir.dot(forward)) + (right * input_dir.dot(right)) + var air_wish_dir = (forward * input_dir.dot(forward)) + (right * input_dir.dot(right)) horizontal += air_wish_dir.normalized() * air_input_strength * delta - var speed := horizontal.length() + var speed = horizontal.length() if speed > max_air_speed: horizontal = horizontal.normalized() * lerp( speed, @@ -189,7 +224,7 @@ func apply_air_control(input_dir: Vector3, delta): func get_input_direction() -> Vector3: - var dir := Vector3.ZERO + var dir = Vector3.ZERO if Input.is_action_pressed("move_forward"): dir -= global_transform.basis.z if Input.is_action_pressed("move_backward"): @@ -199,3 +234,77 @@ func get_input_direction() -> Vector3: if Input.is_action_pressed("move_right"): dir += global_transform.basis.x return dir.normalized() + +func try_mantle(delta): + if is_mantling: + return + + if is_on_floor(): + return + + # Falling or near apex only + if velocity.y > 1.0: + return + + var forward_ray = $LedgeForwardRay + if not forward_ray.is_colliding(): + return + + # Ensure it's a wall + var normal = forward_ray.get_collision_normal() + if normal.dot(Vector3.UP) > 0.2: + return + + var wall_point = forward_ray.get_collision_point() + + var space_state = get_world_3d().direct_space_state + var from = wall_point + Vector3.UP * mantle_max_height + var to = wall_point - Vector3.UP * 0.2 + + var query = PhysicsRayQueryParameters3D.create(from, to) + query.exclude = [self] + + var result = space_state.intersect_ray(query) + if result.is_empty(): + return + + var ledge_point = result.position + var ledge_y = ledge_point.y + + var feet_y = global_position.y + var chest_y = global_position.y + 1.0 + + # Feet below ledge + if feet_y > ledge_y - 0.2: + return + + # Chest close to ledge height + if abs(ledge_y - chest_y) > 0.8: + return + + start_mantle(ledge_point) + + + + + + +func start_mantle(ledge_point: Vector3): + print("MANTLE") + + is_mantling = true + velocity = Vector3.ZERO + + # Direction player is facing (flat) + var forward = -global_transform.basis.z + forward.y = 0 + forward = forward.normalized() + + # Move onto the ledge + mantle_target = ledge_point + mantle_target += forward * mantle_forward_offset + + # Place feet on top of ledge + mantle_target.y = ledge_point.y + (capsule_height * 0.5) + + print("TARGET:", mantle_target)