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.
This commit is contained in:
SchimmelSpreu83 2025-09-21 23:48:46 +02:00
parent 31363b1d52
commit a4100e3026
4 changed files with 125 additions and 38 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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")