From a4100e3026a934efcaba5d7bbb701a43bc032b30 Mon Sep 17 00:00:00 2001 From: SchimmelSpreu83 Date: Sun, 21 Sep 2025 23:48:46 +0200 Subject: [PATCH] Changes to the player - Made the character inherit from StairsCharacter3D (Plugin) - Made the players head smoothly adjust when stepping. - Reintroduced the previously broken headbobbing. - Adjusted collisions. --- source/src/gameplay/characters/character.gd | 16 ++--- source/src/gameplay/characters/player/head.gd | 65 ++++++++++++++++--- .../characters/player/player_character.gd | 27 ++++++-- .../characters/player/player_character.tscn | 55 +++++++++++----- 4 files changed, 125 insertions(+), 38 deletions(-) diff --git a/source/src/gameplay/characters/character.gd b/source/src/gameplay/characters/character.gd index 22f1be3..77cbcfd 100644 --- a/source/src/gameplay/characters/character.gd +++ b/source/src/gameplay/characters/character.gd @@ -1,5 +1,5 @@ class_name Character3D -extends CharacterBody3D +extends StairsCharacter3D @export var movement: CharacterMovement @export var speed_scale: float = 1.0 @@ -12,11 +12,11 @@ var air_control: float = 1.0 var freeze_air_control: bool = false var last_platform_velocity := Vector3.ZERO var _was_on_floor: bool = false -var _previous_velocity: Vector3 +var previous_velocity: Vector3 func move(direction: Vector3, delta: float) -> void: - direction *= (speed * speed_scale) + direction *= speed * speed_scale var on_floor: bool = is_on_floor() @@ -56,9 +56,9 @@ func move(direction: Vector3, delta: float) -> void: velocity = velocity.move_toward(direction, speed_change * delta) await get_tree().physics_frame - _previous_velocity = velocity + previous_velocity = velocity #DebugDraw3D.draw_arrow(global_position, global_position + velocity, Color.BLUE, 0.5, true) - #SPrint.print_msg("%s Velocity: %s" %[self , velocity], 0.02) + #SPrint.print_msg("%s Velocity: %s" % [self , velocity], 0.02) ## @tutorial: https://gmtk.itch.io/platformer-toolkit/devlog/395523/behind-the-code @@ -67,13 +67,13 @@ func apply_gravity(delta: float) -> void: var gravity_multiplier: float = 1.0 if velocity.y < -margin: - #SPrint.print_msg("Falling\n%s" %velocity.y, 0.02) + #SPrint.print_msg("Falling\n%s" % velocity.y, 0.02) gravity_multiplier = movement.fall_gravity_multiplier elif velocity.y > margin: - #SPrint.print_msg("Rising\n%s" %velocity.y, 0.02) + #SPrint.print_msg("Rising\n%s" % velocity.y, 0.02) gravity_multiplier = movement.rise_gravity_multiplier else: - #SPrint.print_msg("Peaking\n%s" %velocity.y, 0.02) + #SPrint.print_msg("Peaking\n%s" % velocity.y, 0.02) gravity_multiplier = movement.peak_gravity_multiplier velocity.y -= (movement.get_gravity() * gravity_multiplier) * delta diff --git a/source/src/gameplay/characters/player/head.gd b/source/src/gameplay/characters/player/head.gd index 91e5f74..60f2495 100644 --- a/source/src/gameplay/characters/player/head.gd +++ b/source/src/gameplay/characters/player/head.gd @@ -1,4 +1,4 @@ -class_name GameCamera3D +class_name PlayerHead extends Node3D const ACTION_CAMERA_UP: StringName = &"camera_up" @@ -38,13 +38,24 @@ const CONTROLLER_INVERT_Y_PATH: StringName = &"game/input/camera_controller_inve @export var headbob_frequency: float = 0.5 @export var headbob_multiplier: float = 1.0 @export var headbob_character: Character3D +@export var headbob_target: Node3D + +@export_group("Step Smoothing", "step_") +@export var step_smoothing_target: Node3D +@export var step_speed: float = 8.0 var rotational_direction: Vector2 var rotational_velocity: Vector2 var input_event: InputEvent var using_controller: bool = false +# Headbobbing var headbob_time: float = 0.0 var headbob_enabled: bool = true +# Stair smooting +var stair_camera_offset_height: float = 0.0 +var _previous_position: Vector3 +var _stair_camera_target_height: float = 0.0 +var _stair_camera_step_smoothing: bool = false func _ready() -> void: @@ -62,18 +73,23 @@ func _ready() -> void: ProjectSettings.settings_changed.connect(_update_settings) _update_settings.call() + _setup_step_smoothing() -func _physics_process(delta: float) -> void: + +func _process(delta: float) -> void: if not GameGlobals.in_cutscene: _process_input(delta) _perform_head_bob(delta) + _process_step_smoothing(delta) + func _unhandled_input(event: InputEvent) -> void: if event is InputEventMouseMotion or event is InputEventJoypadMotion: input_event = event +#region Mouse/Controller rotation func apply_rotation(rot: Vector2) -> void: rotate_y(-rot.x) rotation.x = rotation.x - rot.y @@ -156,12 +172,14 @@ func _process_controller(delta: float) -> void: func _lerp_rotational_velocity(sensitivity: float, friction: float, delta: float) -> Vector2: return rotational_velocity.lerp(rotational_direction * sensitivity, friction * delta) +#endregion +#region Headbobbing func _perform_head_bob(delta: float) -> void: if not headbob_enabled: - camera.position = Vector3.ZERO - camera.rotation.z = 0.0 + headbob_target.position = Vector3.ZERO + headbob_target.rotation.z = 0.0 headbob_time = 0.0 return @@ -172,7 +190,38 @@ func _perform_head_bob(delta: float) -> void: headbob_time += delta * headbob_speed * headbob_multiplier headbob_time = fposmod(headbob_time, TAU) - camera.position.y = sin(headbob_time * headbob_frequency) * headbob_range - camera.position.x = cos(headbob_time * headbob_frequency / 2) * headbob_range - camera.rotation.z = deg_to_rad(lerp(camera.position.x, camera.position.y, 0.5)) * TAU - SPrint.print_msgf(str("Head Z-Rotation: ", camera.rotation.z), true) + headbob_target.position.y = sin(headbob_time * headbob_frequency) * headbob_range + headbob_target.position.x = cos(headbob_time * headbob_frequency / 2) * headbob_range + headbob_target.rotation.z = deg_to_rad(lerp(headbob_target.position.x, headbob_target.position.y, 0.5)) * TAU + SPrint.print_msgf(str("Head Z-Rotation: ", headbob_target.rotation.z), true) +#endregion + + +#region Step Smoothing +func smooth_step(height_change: float) -> void: + _stair_camera_target_height -= height_change + _stair_camera_step_smoothing = true + + +func _setup_step_smoothing() -> void: + headbob_character.on_stair_step.connect(_on_stair_step) + stair_camera_offset_height = step_smoothing_target.position.y + + +func _on_stair_step() -> void: + smooth_step(global_position.y - _previous_position.y) + + +func _process_step_smoothing(delta: float) -> void: + if _stair_camera_step_smoothing and is_instance_valid(step_smoothing_target): + _stair_camera_target_height = lerp(_stair_camera_target_height, 0.0, step_speed * delta) + + if absf(_stair_camera_target_height) < 0.0025: + _stair_camera_target_height = 0.0 + _stair_camera_step_smoothing = false + + var target_pos := Vector3.UP * (stair_camera_offset_height + _stair_camera_target_height) + step_smoothing_target.position = target_pos.rotated(Vector3.LEFT, rotation.x) + + _previous_position = global_position +#endregion diff --git a/source/src/gameplay/characters/player/player_character.gd b/source/src/gameplay/characters/player/player_character.gd index 5223730..e1ea560 100644 --- a/source/src/gameplay/characters/player/player_character.gd +++ b/source/src/gameplay/characters/player/player_character.gd @@ -25,9 +25,10 @@ var has_control: bool = true var is_crouching: bool = false var crouch_speed_affection: float = 0.0 -@onready var head: GameCamera3D = $Head -@onready var collision_shape: CollisionShape3D = $CollisionShape3D +@onready var head: PlayerHead = $Head +@onready var collision_shape: CollisionShape3D = $CylinderCollider @onready var standup_checker: Area3D = $StandupChecker +@onready var stair_stepper: StairStepper = $StairStepper func _ready() -> void: @@ -51,12 +52,15 @@ func _physics_process(delta: float) -> void: if not is_on_floor(): apply_gravity(delta) elif not _was_on_floor: - if _previous_velocity.y <= -2.0: + if previous_velocity.y <= -2.0: head.camera.shake(0.025, 0.175) if not has_control: move(Vector3.ZERO, delta) - move_and_slide() + move_and_stair_step() + + #if is_on_floor(): + #stair_stepper.handle_step_climbing() return # Handle jump. @@ -78,8 +82,15 @@ func _physics_process(delta: float) -> void: speed *= remap(crouch_speed_affection, 0.0, 1.0, 1.0, standup_speed_affection) move(movement_direction, delta) + move_and_stair_step() - move_and_slide() + SPrint.print_msgf( + "Player Horizontal-Velocity: %s\nPlayer Vertical-Velocity: %s" + % [(velocity * Utils.VEC3_HOR).length(), velocity.y], true + ) + + #if is_on_floor(): + #stair_stepper.handle_step_climbing() func apply_gravity(delta: float) -> void: @@ -119,4 +130,8 @@ func handle_crouching(delta: float) -> void: head.position.y = lerp(head.position.y, target_camera_height, weight) crouch_speed_affection = lerp(crouch_speed_affection, float(is_crouching), weight * standup_speed_affection_speed_multiplier if not is_crouching else 1.0) - SPrint.print_msgf("Crouching Speed Affection: %s\n%s" % [crouch_speed_affection, is_crouching], true) + + if crouch_speed_affection <= 0.05: + crouch_speed_affection = 0.0 + + SPrint.print_msgf("Crouching Speed Affection: %s\nIs Crouching: %s" % [crouch_speed_affection, is_crouching], true) diff --git a/source/src/gameplay/characters/player/player_character.tscn b/source/src/gameplay/characters/player/player_character.tscn index bb965a5..70bd6c3 100644 --- a/source/src/gameplay/characters/player/player_character.tscn +++ b/source/src/gameplay/characters/player/player_character.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=9 format=3 uid="uid://clhy3kiceqf2o"] +[gd_scene load_steps=10 format=3 uid="uid://clhy3kiceqf2o"] [ext_resource type="Script" uid="uid://day6rhxicaxqf" path="res://src/gameplay/characters/player/player_character.gd" id="1_hqu6r"] [ext_resource type="Script" uid="uid://dsjlv8midt2g2" path="res://src/gameplay/characters/character_movement.gd" id="2_1ixuj"] @@ -13,14 +13,24 @@ max_turning_speed = 70.0 peak_gravity_multiplier = 0.3 metadata/_custom_type_script = "uid://dsjlv8midt2g2" -[sub_resource type="CylinderShape3D" id="CylinderShape3D_fjt7c"] +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_bdj5f"] +margin = 0.01 +radius = 0.46 height = 1.8 -[sub_resource type="CylinderShape3D" id="CylinderShape3D_gy1j0"] -height = 1.59 -radius = 0.49 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_bdj5f"] +margin = 0.01 +height = 1.8 +radius = 0.46 -[node name="PlayerCharacter" type="CharacterBody3D"] +[sub_resource type="CylinderShape3D" id="CylinderShape3D_ntkcp"] +height = 1.59 +radius = 0.45 + +[node name="PlayerCharacter" type="StairsCharacter3D"] +collider = NodePath("CylinderCollider") +step_height_up = 0.7 +step_height_down = 0.7 collision_layer = 2 script = ExtResource("1_hqu6r") standup_speed_affection = 0.15 @@ -28,30 +38,43 @@ standup_speed_affection_speed_multiplier = 0.5 movement = SubResource("Resource_vq0uu") metadata/_custom_type_script = "uid://day6rhxicaxqf" -[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +[node name="CapsuleCollider" type="CollisionShape3D" parent="."] +process_mode = 4 transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9, 0) -shape = SubResource("CylinderShape3D_fjt7c") +visible = false +shape = SubResource("CapsuleShape3D_bdj5f") +disabled = true -[node name="Head" type="Node3D" parent="." node_paths=PackedStringArray("camera", "headbob_character")] +[node name="CylinderCollider" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9, 0) +shape = SubResource("CylinderShape3D_bdj5f") + +[node name="Head" type="Node3D" parent="." node_paths=PackedStringArray("camera", "headbob_character", "headbob_target", "step_smoothing_target")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.6, 0) script = ExtResource("2_fjt7c") -camera = NodePath("ShakingCamera") +camera = NodePath("Headbobbing/StairStepping/ShakingCamera") headbob_range = 0.05 headbob_frequency = 2.0 headbob_character = NodePath("..") +headbob_target = NodePath("Headbobbing") +step_smoothing_target = NodePath("Headbobbing/StairStepping") -[node name="ShakingCamera" type="Camera3D" parent="Head"] +[node name="Headbobbing" type="Node3D" parent="Head"] + +[node name="StairStepping" type="Node3D" parent="Head/Headbobbing"] + +[node name="ShakingCamera" type="Camera3D" parent="Head/Headbobbing/StairStepping"] +current = true fov = 85.0 script = ExtResource("4_ci1ud") metadata/_custom_type_script = "uid://44s0v8mukxk4" -[node name="FootstepComponent" type="Node" parent="." node_paths=PackedStringArray("character")] -script = ExtResource("4_vq0uu") -character = NodePath("..") - [node name="StandupChecker" type="Area3D" parent="."] input_ray_pickable = false [node name="CollisionShape3D" type="CollisionShape3D" parent="StandupChecker"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.99, 0) -shape = SubResource("CylinderShape3D_gy1j0") +shape = SubResource("CylinderShape3D_ntkcp") + +[node name="FootstepComponent" type="Node" parent="."] +script = ExtResource("4_vq0uu")