From b0c6bc753004a7323b0a95b236eed7def97ac007 Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Mon, 29 Sep 2025 16:21:21 -0600 Subject: [PATCH] Added strafe banking & fall kick camera effects --- src/player/{ => camera}/camera_controller.gd | 0 .../{ => camera}/camera_controller.gd.uid | 0 src/player/camera/fall_kick_effect.gd | 60 +++++++++++++++++++ src/player/camera/fall_kick_effect.gd.uid | 1 + .../{ => camera}/mouselook_controller.gd | 0 .../{ => camera}/mouselook_controller.gd.uid | 0 src/player/camera/strafe_bank_effect.gd | 26 ++++++++ src/player/camera/strafe_bank_effect.gd.uid | 1 + src/player/player.tscn | 38 +++++++++++- 9 files changed, 123 insertions(+), 3 deletions(-) rename src/player/{ => camera}/camera_controller.gd (100%) rename src/player/{ => camera}/camera_controller.gd.uid (100%) create mode 100644 src/player/camera/fall_kick_effect.gd create mode 100644 src/player/camera/fall_kick_effect.gd.uid rename src/player/{ => camera}/mouselook_controller.gd (100%) rename src/player/{ => camera}/mouselook_controller.gd.uid (100%) create mode 100644 src/player/camera/strafe_bank_effect.gd create mode 100644 src/player/camera/strafe_bank_effect.gd.uid diff --git a/src/player/camera_controller.gd b/src/player/camera/camera_controller.gd similarity index 100% rename from src/player/camera_controller.gd rename to src/player/camera/camera_controller.gd diff --git a/src/player/camera_controller.gd.uid b/src/player/camera/camera_controller.gd.uid similarity index 100% rename from src/player/camera_controller.gd.uid rename to src/player/camera/camera_controller.gd.uid diff --git a/src/player/camera/fall_kick_effect.gd b/src/player/camera/fall_kick_effect.gd new file mode 100644 index 0000000..0f45a1e --- /dev/null +++ b/src/player/camera/fall_kick_effect.gd @@ -0,0 +1,60 @@ +extends Node3D +## Give the camera a little kick when the player falls, proportional to landing velocity + +@export var enabled := true +@export var character: CharacterBody3D + +## Minimum landing speed before kick is applied, in m/s +@export var min_landing_speed := 4.0 + +## How far the camera gets pitched on kick in degrees per m/s of landing velocity +@export var angle_curve: Curve + +## How far down the camera gets kicked in meters per m/s of landing velocity +@export var offset_curve: Curve + +## How long the rebound takes in seconds per m/s of landing velocity +@export var rebound_time_curve: Curve + +var _last_speed := 0.0 +var _in_air := false +var _tween: Tween + + +func _get_tween() -> Tween: + if _tween and _tween.is_valid(): + _tween.kill() + _tween = create_tween() + return _tween + + +func do_kick(landing_speed: float) -> void: + print_debug("Landing speed: ", landing_speed) + if landing_speed < min_landing_speed: + return + + # Initial kick + rotation.x = -deg_to_rad(angle_curve.sample(landing_speed)) + position.y = -offset_curve.sample(landing_speed) + + # Rebound + var rebound_time := rebound_time_curve.sample(landing_speed) + var tween := _get_tween() + tween.set_parallel(true) + tween.tween_property(self, "rotation:x", 0, rebound_time).set_trans(Tween.TRANS_CIRC).set_ease( + Tween.EASE_OUT + ) + tween.tween_property(self, "position:y", 0, rebound_time).set_trans(Tween.TRANS_CIRC).set_ease( + Tween.EASE_OUT + ) + + +func _process(_delta: float) -> void: + if character.is_on_floor(): + if _in_air: + # First frame after land + do_kick(absf(_last_speed)) + _in_air = false + else: + _in_air = true + _last_speed = character.velocity.y diff --git a/src/player/camera/fall_kick_effect.gd.uid b/src/player/camera/fall_kick_effect.gd.uid new file mode 100644 index 0000000..e3d1bbf --- /dev/null +++ b/src/player/camera/fall_kick_effect.gd.uid @@ -0,0 +1 @@ +uid://bndyjh51gkqeo diff --git a/src/player/mouselook_controller.gd b/src/player/camera/mouselook_controller.gd similarity index 100% rename from src/player/mouselook_controller.gd rename to src/player/camera/mouselook_controller.gd diff --git a/src/player/mouselook_controller.gd.uid b/src/player/camera/mouselook_controller.gd.uid similarity index 100% rename from src/player/mouselook_controller.gd.uid rename to src/player/camera/mouselook_controller.gd.uid diff --git a/src/player/camera/strafe_bank_effect.gd b/src/player/camera/strafe_bank_effect.gd new file mode 100644 index 0000000..e6a2eba --- /dev/null +++ b/src/player/camera/strafe_bank_effect.gd @@ -0,0 +1,26 @@ +extends Node3D +## Bank the camera to the sides when the player strafes, and pitch up and down with forward motion + +const ACCELERATION := 16.0 + +@export var enabled := true + +@export var character: CharacterBody3D + +## Camera pitch effect in degrees per m/s of forward movement +@export var pitch_curve: Curve + +## Camera roll effect in degrees per m/s of side movement +@export var roll_curve: Curve + + +func _process(delta: float) -> void: + var weight := 1.0 - exp(-ACCELERATION * delta) + + var forward_dot := character.velocity.dot(global_transform.basis.z) + var target_x := deg_to_rad(pitch_curve.sample(forward_dot)) + rotation.x = lerp_angle(rotation.x, target_x, weight) + + var right_dot := character.velocity.dot(global_transform.basis.x) + var target_z := deg_to_rad(roll_curve.sample(right_dot)) + rotation.z = lerp_angle(rotation.z, target_z, weight) diff --git a/src/player/camera/strafe_bank_effect.gd.uid b/src/player/camera/strafe_bank_effect.gd.uid new file mode 100644 index 0000000..b704df2 --- /dev/null +++ b/src/player/camera/strafe_bank_effect.gd.uid @@ -0,0 +1 @@ +uid://buq2c8inefgdl diff --git a/src/player/player.tscn b/src/player/player.tscn index 6bf5b24..ffad472 100644 --- a/src/player/player.tscn +++ b/src/player/player.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=17 format=3 uid="uid://dtbulshrxetes"] +[gd_scene load_steps=22 format=3 uid="uid://dtbulshrxetes"] [ext_resource type="Script" uid="uid://bihaf1xlwpkdo" path="res://src/player/player.gd" id="1_5f2sf"] [ext_resource type="Script" uid="uid://f3kssctvn2bo" path="res://src/player/camera/camera_controller.gd" id="1_mf2ua"] [ext_resource type="Script" uid="uid://csjccrhj5wnx7" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd" id="1_rxibo"] [ext_resource type="Script" uid="uid://8umksf8e80fw" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="2_mf2ua"] [ext_resource type="Script" uid="uid://buq2c8inefgdl" path="res://src/player/camera/strafe_bank_effect.gd" id="3_x42xx"] +[ext_resource type="Script" uid="uid://bndyjh51gkqeo" path="res://src/player/camera/fall_kick_effect.gd" id="4_8ydov"] [ext_resource type="Script" uid="uid://032jyhgkb2rv" path="res://src/player/camera/mouselook_controller.gd" id="4_244u8"] [ext_resource type="Script" uid="uid://c0nxcunx0fo4r" path="res://src/player/movement_controller.gd" id="6_jiejy"] [ext_resource type="Script" uid="uid://d11erhxna68vd" path="res://src/player/player_input_controller.gd" id="7_o822w"] @@ -15,6 +16,29 @@ [sub_resource type="CapsuleMesh" id="CapsuleMesh_4anbu"] [sub_resource type="Curve" id="Curve_x42xx"] +_limits = [-1.0, 1.0, -7.0, 7.0] +_data = [Vector2(-7, -1), 0.0, 0.0, 0, 0, Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(7, 1), 0.0, 0.0, 0, 0] +point_count = 3 + +[sub_resource type="Curve" id="Curve_l271a"] +_limits = [-2.5, 2.5, -7.0, 7.0] +_data = [Vector2(-7, 2.5), 0.0, 0.0, 0, 0, Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(7, -2.5), 0.0, 0.0, 0, 0] +point_count = 3 + +[sub_resource type="Curve" id="Curve_8ydov"] +_limits = [0.0, 4.0, 0.0, 10.0] +_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(10, 4), 0.0, 0.0, 0, 0] +point_count = 2 + +[sub_resource type="Curve" id="Curve_cwolq"] +_limits = [0.0, 0.05, 0.0, 10.0] +_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(10, 0.05), 0.0, 0.0, 0, 0] +point_count = 2 + +[sub_resource type="Curve" id="Curve_ylhto"] +_limits = [0.0, 0.6, 0.0, 10.0] +_data = [Vector2(0, 0.1), 0.0, 0.0, 0, 0, Vector2(10, 0.6), 0.0, 0.0, 0, 0] +point_count = 2 [sub_resource type="Resource" id="Resource_244u8"] script = ExtResource("2_mf2ua") @@ -55,15 +79,23 @@ yaw_root = NodePath("../..") script = ExtResource("3_x42xx") character = NodePath("../../..") pitch_curve = SubResource("Curve_x42xx") +roll_curve = SubResource("Curve_l271a") -[node name="PhantomCamera3D" type="Node3D" parent="CameraOffset/CameraController/StrafeBankEffect"] +[node name="FallKickEffect" type="Node3D" parent="CameraOffset/CameraController/StrafeBankEffect" node_paths=PackedStringArray("character")] +script = ExtResource("4_8ydov") +character = NodePath("../../../..") +angle_curve = SubResource("Curve_8ydov") +offset_curve = SubResource("Curve_cwolq") +rebound_time_curve = SubResource("Curve_ylhto") + +[node name="PhantomCamera3D" type="Node3D" parent="CameraOffset/CameraController/StrafeBankEffect/FallKickEffect"] script = ExtResource("1_rxibo") priority = 1 tween_resource = SubResource("Resource_244u8") noise_emitter_layer = 1 metadata/_custom_type_script = "uid://csjccrhj5wnx7" -[node name="MeshInstance3D" type="MeshInstance3D" parent="CameraOffset/CameraController/StrafeBankEffect/PhantomCamera3D"] +[node name="MeshInstance3D" type="MeshInstance3D" parent="CameraOffset/CameraController/StrafeBankEffect/FallKickEffect/PhantomCamera3D"] transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, 0, -0.45) gi_mode = 0 mesh = SubResource("CapsuleMesh_o822w")