From b46b56c7998270f383c248b0224e5d23cadd01e0 Mon Sep 17 00:00:00 2001 From: Lucas Fryzek Date: Fri, 7 Feb 2025 18:02:39 +0000 Subject: Improve climbing Implement new follow camera logic that makes the climbing logic easier to implement. Also ensure player can climb around corners. --- .../third_person_camera/ThirdPersonCamera.gd | 8 ++- .../third_person_camera/ThirdPersonCamera.tscn | 6 +- prefab/player.tscn | 30 ++++++---- project.godot | 10 ++++ scripts/player.gd | 64 +++++++++++++++++----- 5 files changed, 90 insertions(+), 28 deletions(-) diff --git a/addons/third-person-camera/third_person_camera/ThirdPersonCamera.gd b/addons/third-person-camera/third_person_camera/ThirdPersonCamera.gd index a460835..7eab1e2 100644 --- a/addons/third-person-camera/third_person_camera/ThirdPersonCamera.gd +++ b/addons/third-person-camera/third_person_camera/ThirdPersonCamera.gd @@ -102,7 +102,13 @@ func _process_horizontal_rotation_input() : var camera_horizontal_rotation_variation = Input.get_action_strength("tp_camera_right") - Input.get_action_strength("tp_camera_left") camera_horizontal_rotation_variation = camera_horizontal_rotation_variation * get_process_delta_time() * 30 * horizontal_rotation_sensitiveness camera_horizontal_rotation_deg += camera_horizontal_rotation_variation - + +func focus_camera(local_pos: Vector3, target_pos: Vector3): + var local_2d = Vector2(local_pos.x, local_pos.z) + var target_2d = Vector2(target_pos.x, target_pos.z) + # This is the amount to tween the camera position + var target_rotation = target_2d.angle_to_point(local_2d) + PI/2.0 + camera_horizontal_rotation_deg = rad_to_deg(_camera.global_rotation.y - target_rotation) func _process_tilt_input() : if InputMap.has_action("tp_camera_up") and InputMap.has_action("tp_camera_down") : diff --git a/addons/third-person-camera/third_person_camera/ThirdPersonCamera.tscn b/addons/third-person-camera/third_person_camera/ThirdPersonCamera.tscn index f1faf2e..f59b361 100644 --- a/addons/third-person-camera/third_person_camera/ThirdPersonCamera.tscn +++ b/addons/third-person-camera/third_person_camera/ThirdPersonCamera.tscn @@ -25,7 +25,7 @@ transform = Transform3D(1, 0, 0, 0, 0.939693, 0.34202, 0, -0.34202, 0.939693, 0, top_level = true [node name="OffsetPivot" type="Node3D" parent="RotationPivot"] -transform = Transform3D(1, -3.9187e-07, 6.47546e-10, 3.94476e-07, 1, 5.65946e-05, -2.27374e-11, -5.65946e-05, 1, 0, 0, 0) +transform = Transform3D(1, -3.9187e-07, 6.47546e-10, 3.94476e-07, 1, 5.65648e-05, -2.27374e-11, -5.65648e-05, 1, 0, 0, 0) [node name="CameraSpringArm" type="SpringArm3D" parent="RotationPivot/OffsetPivot"] process_priority = 11 @@ -33,14 +33,14 @@ shape = SubResource("SeparationRayShape3D_84uqy") spring_length = 10.0 [node name="CameraMarker" type="Marker3D" parent="RotationPivot/OffsetPivot/CameraSpringArm"] -transform = Transform3D(1, 7.93407e-08, 3.5101e-07, 1.48521e-08, 1, -7.89762e-06, 2.08538e-07, 1.2219e-06, 1, -6.69741e-09, -0.000566006, 9.99999) +transform = Transform3D(1, 7.93283e-08, 3.5101e-07, 1.48522e-08, 0.999999, -8.73208e-06, 2.08538e-07, 1.2517e-06, 0.999999, -6.69706e-09, -0.000566006, 10) [node name="PivotDebug" type="MeshInstance3D" parent="RotationPivot/OffsetPivot"] visible = false mesh = SubResource("SphereMesh_ag7lb") [node name="Camera" type="Camera3D" parent="."] -transform = Transform3D(1, 5.38245e-15, -1.47882e-14, 0, 0.939693, 0.34202, 1.57372e-14, -0.34202, 0.939693, -1.47882e-13, 3.4202, 9.39693) +transform = Transform3D(1, -7.09579e-15, 1.94955e-14, 0, 0.939693, 0.34202, -2.07467e-14, -0.34202, 0.939693, 1.94955e-13, 3.4202, 9.39693) top_level = true [node name="CameraDebug" type="MeshInstance3D" parent="Camera"] diff --git a/prefab/player.tscn b/prefab/player.tscn index e4f28aa..760fb38 100644 --- a/prefab/player.tscn +++ b/prefab/player.tscn @@ -1,13 +1,12 @@ [gd_scene load_steps=6 format=3 uid="uid://dsq68sqy2ldjm"] [ext_resource type="Script" path="res://scripts/player.gd" id="1_l6xtg"] -[ext_resource type="PackedScene" uid="uid://wmf2eu0uuhrg" path="res://addons/third-person-camera/third_person_camera/ThirdPersonCamera.tscn" id="1_stkca"] -[sub_resource type="BoxShape3D" id="BoxShape3D_ibgtc"] -size = Vector3(1, 2, 1) +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ca87k"] -[sub_resource type="BoxMesh" id="BoxMesh_wkmld"] -size = Vector3(1, 2, 1) +[sub_resource type="CapsuleMesh" id="CapsuleMesh_6hvkb"] + +[sub_resource type="PrismMesh" id="PrismMesh_16psy"] [sub_resource type="BoxShape3D" id="BoxShape3D_eybym"] size = Vector3(1.28475, 2, 1) @@ -16,16 +15,19 @@ size = Vector3(1.28475, 2, 1) collision_layer = 5 script = ExtResource("1_l6xtg") +[node name="Camera3D" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 0.939693, 0.34202, 0, -0.34202, 0.939693, 0, 2.65407, 4.3752) + [node name="CollisionShape3D" type="CollisionShape3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) -shape = SubResource("BoxShape3D_ibgtc") +shape = SubResource("CapsuleShape3D_ca87k") [node name="MeshInstance3D" type="MeshInstance3D" parent="CollisionShape3D"] -mesh = SubResource("BoxMesh_wkmld") +mesh = SubResource("CapsuleMesh_6hvkb") -[node name="ThirdPersonCamera" parent="." instance=ExtResource("1_stkca")] -distance_from_pivot = 5.0 -pivot_offset = Vector2(0, 1) +[node name="MeshInstance3D2" type="MeshInstance3D" parent="CollisionShape3D"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.730899, -0.46888) +mesh = SubResource("PrismMesh_16psy") [node name="Picker" type="Area3D" parent="."] collision_layer = 10 @@ -37,3 +39,11 @@ shape = SubResource("BoxShape3D_eybym") [node name="Holder" type="Node3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3, 0) + +[node name="LowerRay" type="RayCast3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0) +target_position = Vector3(0, 0, 0.6) + +[node name="UpperRay" type="RayCast3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0) +target_position = Vector3(0, 0, 0.6) diff --git a/project.godot b/project.godot index 5cc5fb2..2094e18 100644 --- a/project.godot +++ b/project.godot @@ -56,6 +56,16 @@ run={ "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194325,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } +turn_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +turn_left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} [rendering] diff --git a/scripts/player.gd b/scripts/player.gd index 73490d3..69ba910 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -23,6 +23,9 @@ var holding: Node3D = null var climbing: bool = false var last_wall_direction: Vector3 = Vector3() +var inital_camera_offset: Vector3 = Vector3() +var camera_rotation: float = 0 + func got_body(body: Node3D): if current_node == null: print("Got body ", body) @@ -46,14 +49,22 @@ func pickup(): holding.freeze = true $Holder.add_child(holding) +func get_camera_fwd() -> Vector3: + var fwd: Vector3 = -Vector3(sin(camera_rotation), 0, cos(camera_rotation)) + return fwd + +func get_camera_right() -> Vector3: + var right: Vector3 = Vector3(cos(camera_rotation), 0, -sin(camera_rotation)) + return right + func throw(): var g_pos = holding.global_position holding.get_parent().remove_child(holding) get_parent().add_child(holding) holding.global_position = g_pos - var fwd = $ThirdPersonCamera.get_front_direction() - var right = Vector3.UP + var fwd = get_camera_fwd() + var right = get_camera_right() var dir = (fwd + right).normalized() holding.freeze = false holding.apply_impulse(THROW_FORCE*dir) @@ -64,14 +75,21 @@ func _ready(): assert(seed_text != null, "Seed text is null") Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) - $ThirdPersonCamera.mouse_follow = true $Picker.connect("body_entered", got_body) $Picker.connect("body_exited", leave_body) -func _process(_delta): + inital_camera_offset = $Camera3D.global_position - global_position + +func _process(delta: float): stam_bar.value = stamina seed_text.text = str(num_seeds) + + turn_camera(delta) + +func turn_camera(delta: float): + var turn_amt = Input.get_axis("turn_right", "turn_left") + camera_rotation += turn_amt * delta func _physics_process(delta): var speed = SPEED @@ -91,11 +109,8 @@ func _physics_process(delta): if not is_on_floor() and not climbing: velocity.y -= gravity * delta - var fwd = $ThirdPersonCamera.get_front_direction() - var right = $ThirdPersonCamera.get_right_direction() - - var climb_direction_fwd = Vector3() - var climb_direction_right = Vector3() + var fwd = get_camera_fwd() + var right = get_camera_right() var valid_climb = false if is_on_wall(): @@ -110,29 +125,46 @@ func _physics_process(delta): climbing = true elif climbing: climbing = false - + + var climb_direction_fwd = Vector3() + var climb_direction_right = Vector3() if climbing: climb_direction_fwd = (Vector3.UP - Vector3.UP.project(last_wall_direction)).normalized() + + # TODO + # This should not be relative to the camera, climbing should be fixed, I think I might need + # to take a cross product here to do that with the wall normal and the fwd/up direction on the wall climb_direction_right = (right - right.project(last_wall_direction)).normalized() # Handle Jump. if Input.is_action_just_pressed("ui_accept") and is_on_floor(): velocity.y = JUMP_VELOCITY - look_at(position + fwd, Vector3.UP) - + # Make sure player is looking in the forward direction + # TODO player should only start looking at the fwd direction when they start moving + look_at(global_position + fwd) + # Get the input direction and handle the movement/deceleration. # As good practice, you should replace UI actions with custom gameplay actions. var input_dir = Input.get_vector("left", "right", "backward", "forward") var direction = (input_dir.y * fwd + input_dir.x * right).normalized() var climb_direction: Vector3 = (input_dir.y * climb_direction_fwd + input_dir.x * climb_direction_right).normalized() if climbing: - #print("Climb direction is ", climb_direction_fwd, " ", climb_direction_right, " ", get_wall_normal()) velocity = climb_direction * speed + + # TODO there should be some way to force the player to be locked to the wall without + # just pushing into the wall slightly. velocity += -speed * last_wall_direction if input_dir.length_squared() > 0: stam_consumption += RUN_CONSUMPTION - #print("Velocity is ", velocity) + + # Force player to look toward wall + look_at(global_position - last_wall_direction) + + # Set camera rotation so it continues to face the wall + # TODO player should still be able to move the camera, this should probably just nudge the camera to this + # position while climbing and not controling the camera + camera_rotation = Vector2(last_wall_direction.x, last_wall_direction.z).angle_to(Vector2(inital_camera_offset.x, inital_camera_offset.z)) elif direction: velocity.x = direction.x * speed velocity.z = direction.z * speed @@ -142,6 +174,10 @@ func _physics_process(delta): move_and_slide() + # Position camera + $Camera3D.global_position = global_position + inital_camera_offset.rotated(Vector3.UP, camera_rotation) + $Camera3D.look_at(global_position) + if stam_consumption == 0 and not Input.is_action_pressed("run") and is_on_floor(): stam_consumption = -STAM_REGEN -- cgit