stair stepping !

This commit is contained in:
veclavtalica 2025-02-12 13:54:48 +03:00
parent 14d2399163
commit a02c118ced
3 changed files with 140 additions and 2 deletions

View File

@ -85,6 +85,8 @@ func _physics_process(delta: float) -> void:
velocity.x = move_toward(velocity.x, 0, SPEED) velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED) velocity.z = move_toward(velocity.z, 0, SPEED)
call("stair_step_up", direction)
if move_and_slide() and is_on_floor(): if move_and_slide() and is_on_floor():
for idx in range(get_slide_collision_count()): for idx in range(get_slide_collision_count()):
var collision := get_slide_collision(idx) var collision := get_slide_collision(idx)
@ -94,6 +96,8 @@ func _physics_process(delta: float) -> void:
global_position = spawn_point.global_position global_position = spawn_point.global_position
reset_physics_interpolation() reset_physics_interpolation()
call("stair_step_down")
@rpc("any_peer", "call_local", "reliable") @rpc("any_peer", "call_local", "reliable")
func hold_thing() -> void: func hold_thing() -> void:

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=12 format=3 uid="uid://cs8c570bxh6u"] [gd_scene load_steps=12 format=3 uid="uid://cs8c570bxh6u"]
[ext_resource type="Script" path="res://src/ingame/player.gd" id="1_isrmf"] [ext_resource type="Script" path="res://src/lib/player_character.gd" id="1_sba4x"]
[ext_resource type="PackedScene" uid="uid://tdsbo3e5ic86" path="res://src/ingame/water_bomb.tscn" id="2_naek4"] [ext_resource type="PackedScene" uid="uid://tdsbo3e5ic86" path="res://src/ingame/water_bomb.tscn" id="2_naek4"]
[ext_resource type="AudioStream" uid="uid://3dlhs18w1fa2" path="res://assets/sfx/boom.wav" id="3_u2hxa"] [ext_resource type="AudioStream" uid="uid://3dlhs18w1fa2" path="res://assets/sfx/boom.wav" id="3_u2hxa"]
[ext_resource type="Shader" path="res://scenes/interactivity_outline.gdshader" id="4_a2qfj"] [ext_resource type="Shader" path="res://scenes/interactivity_outline.gdshader" id="4_a2qfj"]
@ -49,7 +49,7 @@ roughness = 0.4
[node name="Player" type="CharacterBody3D" node_paths=PackedStringArray("_projectile_holder", "_projectile_point", "_camera_pivot", "_camera", "_line_of_sight", "_shot_sound")] [node name="Player" type="CharacterBody3D" node_paths=PackedStringArray("_projectile_holder", "_projectile_point", "_camera_pivot", "_camera", "_line_of_sight", "_shot_sound")]
collision_layer = 2 collision_layer = 2
script = ExtResource("1_isrmf") script = ExtResource("1_sba4x")
_projectile_scene = ExtResource("2_naek4") _projectile_scene = ExtResource("2_naek4")
_projectile_holder = NodePath("ProjectileHolder") _projectile_holder = NodePath("ProjectileHolder")
_projectile_point = NodePath("ProjectilePoint") _projectile_point = NodePath("ProjectilePoint")

134
src/lib/player_character.gd Normal file
View File

@ -0,0 +1,134 @@
class_name StairStepper extends Player
## Credits:
# Special thanks to Majikayo Games for original solution to stair_step_down!
# (https://youtu.be/-WjM1uksPIk)
#
# Special thanks to Myria666 for their paper on Quake movement mechanics (used for stair_step_up)!
# (https://github.com/myria666/qMovementDoc)
#
# Special thanks to Andicraft for their help with implementation stair_step_up!
# (https://github.com/Andicraft)
## Notes:
# 0. All shape colliders are supported. Although, I would recommend Capsule colliders for enemies
# as it works better with the Navigation Meshes. Its up to you what shape you want to use
# for players.
#
# 1. To adjust the step-up/down height, just change the MAX_STEP_UP/MAX_STEP_DOWN values below.
#
# 2. This uses Jolt Physics as the default Godot Physics has a few bugs:
# 1: Small gaps that you should be able to fit through both ways will block you in Godot Physics.
# You can see this demonstrated with the floating boxes in front of the big stairs.
# 2: Walking into some objects may push the player downward by a small amount which causes
# jittering and causes the floor to be detected as a step.
# TLDR: This still works with default Godot Physics, although it feels a lot better in Jolt Physics.
@export var MAX_STEP_UP := 0.5 # Maximum height in meters the player can step up.
@export var MAX_STEP_DOWN := -0.5 # Maximum height in meters the player can step down.
var was_grounded: bool = true
var is_grounded: bool = true
func _physics_process(delta: float) -> void:
was_grounded = is_grounded
is_grounded = is_on_floor()
super._physics_process(delta)
# Function: Handle walking down stairs
func stair_step_down():
if is_on_floor():
return
# If we're falling from a step
if velocity.y <= 0 and was_grounded:
# Initialize body test variables
var body_test_result = PhysicsTestMotionResult3D.new()
var body_test_params = PhysicsTestMotionParameters3D.new()
body_test_params.from = self.global_transform ## We get the player's current global_transform
body_test_params.motion = Vector3(0, MAX_STEP_DOWN, 0) ## We project the player downward
if PhysicsServer3D.body_test_motion(self.get_rid(), body_test_params, body_test_result):
# Enters if a collision is detected by body_test_motion
# Get distance to step and move player downward by that much
position.y += body_test_result.get_travel().y
apply_floor_snap()
is_grounded = true
# Function: Handle walking up stairs
func stair_step_up(wish_dir: Vector3):
if wish_dir == Vector3.ZERO:
return
# 0. Initialize testing variables
var body_test_params = PhysicsTestMotionParameters3D.new()
var body_test_result = PhysicsTestMotionResult3D.new()
var test_transform = global_transform ## Storing current global_transform for testing
var distance = wish_dir * 0.1 ## Distance forward we want to check
body_test_params.from = self.global_transform ## Self as origin point
body_test_params.motion = distance ## Go forward by current distance
# Pre-check: Are we colliding?
if !PhysicsServer3D.body_test_motion(self.get_rid(), body_test_params, body_test_result):
## If we don't collide, return
return
# 1. Move test_transform to collision location
var remainder = body_test_result.get_remainder() ## Get remainder from collision
test_transform = test_transform.translated(body_test_result.get_travel()) ## Move test_transform by distance traveled before collision
# 2. Move test_transform up to ceiling (if any)
var step_up = MAX_STEP_UP * Vector3.UP
body_test_params.from = test_transform
body_test_params.motion = step_up
PhysicsServer3D.body_test_motion(self.get_rid(), body_test_params, body_test_result)
test_transform = test_transform.translated(body_test_result.get_travel())
# 3. Move test_transform forward by remaining distance
body_test_params.from = test_transform
body_test_params.motion = remainder
PhysicsServer3D.body_test_motion(self.get_rid(), body_test_params, body_test_result)
test_transform = test_transform.translated(body_test_result.get_travel())
# 3.5 Project remaining along wall normal (if any)
## So you can walk into wall and up a step
if body_test_result.get_collision_count() != 0:
remainder = body_test_result.get_remainder().length()
### Uh, there may be a better way to calculate this in Godot.
var wall_normal = body_test_result.get_collision_normal()
var dot_div_mag = wish_dir.dot(wall_normal) / (wall_normal * wall_normal).length()
var projected_vector = (wish_dir - dot_div_mag * wall_normal).normalized()
body_test_params.from = test_transform
body_test_params.motion = remainder * projected_vector
PhysicsServer3D.body_test_motion(self.get_rid(), body_test_params, body_test_result)
test_transform = test_transform.translated(body_test_result.get_travel())
# 4. Move test_transform down onto step
body_test_params.from = test_transform
body_test_params.motion = MAX_STEP_UP * -Vector3.UP
# Return if no collision
if !PhysicsServer3D.body_test_motion(self.get_rid(), body_test_params, body_test_result):
return
test_transform = test_transform.translated(body_test_result.get_travel())
# 5. Check floor normal for un-walkable slope
var surface_normal = body_test_result.get_collision_normal()
var temp_floor_max_angle = floor_max_angle + deg_to_rad(20)
if (snappedf(surface_normal.angle_to(Vector3.UP), 0.001) > temp_floor_max_angle):
return
# 6. Move player up
var global_pos = global_position
var step_up_dist = test_transform.origin.y - global_pos.y
global_pos.y = test_transform.origin.y
global_position = global_pos