NEON-29: Ledge peel via position — GROUNDED eats vy on floor
CharacterBody3D MOTION_MODE_GROUNDED move_and_slide clears downward velocity while is_on_floor(), so gravity on velocity never produced vertical motion off terrace lips (all prior probe/snap tweaks were no-ops). After each walk move_and_slide, when the goal surface is below the feet and probes/deep-descent say drop, accumulate peel_vy += g·dt and apply global_position.y += peel_vy·dt. Airborne walk still uses velocity gravity. Set physics/3d/default_gravity=9.81 explicitly in project.godot.pull/41/head
parent
55ce6f3e3b
commit
fb1ac80fb0
|
|
@ -67,6 +67,7 @@ dev_toggle_occluder_obstacle={
|
|||
[physics]
|
||||
|
||||
common/physics_ticks_per_second=120
|
||||
3d/default_gravity=9.81
|
||||
3d/physics_engine="Jolt Physics"
|
||||
|
||||
[rendering]
|
||||
|
|
|
|||
|
|
@ -126,6 +126,9 @@ var _debug_trace_frame: int = 0
|
|||
var _walk_nav_column_steering: bool = false
|
||||
## Schmitt latch for vertical routing (see [member WALK_VERT_ROUTE_LATCH_ON_SEP]).
|
||||
var _walk_vert_route_latched: bool = false
|
||||
## GROUNDED [method move_and_slide] clears downward [code]velocity.y[/code] on floor; accumulate peel
|
||||
## speed here and apply [code]global_position.y[/code] so terrace lips can actually drop.
|
||||
var _walk_ledge_peel_vy: float = 0.0
|
||||
@onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D
|
||||
|
||||
|
||||
|
|
@ -150,11 +153,13 @@ func set_authoritative_nav_goal(world_pos: Vector3) -> void:
|
|||
_idle_stable_enter_streak = 0
|
||||
_walk_nav_column_steering = false
|
||||
_walk_vert_route_latched = false
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
_nav_agent.set_target_position(world_pos)
|
||||
|
||||
|
||||
func clear_nav_goal() -> void:
|
||||
velocity = Vector3.ZERO
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
_has_walk_goal = false
|
||||
_floor_angle_loose_ticks = FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP
|
||||
_step_assist_active = false
|
||||
|
|
@ -195,6 +200,7 @@ func snap_to_server(world_pos: Vector3) -> void:
|
|||
global_position = settled
|
||||
velocity = Vector3.ZERO
|
||||
_has_walk_goal = false
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
# Force corrective idle ticks so Jolt resolves any residual physics state before walking.
|
||||
_floor_angle_loose_ticks = FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP
|
||||
_step_assist_cooldown = 0
|
||||
|
|
@ -443,30 +449,40 @@ func _walk_has_close_floor_probe_below(move_dir_xz: Vector2) -> bool:
|
|||
## toward a floor click and poisons horizontal motion / slide resolution.
|
||||
## Do apply gravity when [code]is_on_floor()[/code] is still true but there is no walkable surface
|
||||
## within [member WALK_SUPPORT_PROBE_DEPTH] under the foot disk (ledge / void while moving).
|
||||
## [param feet_y] is [method capsule_feet_y] for the current body origin (see [member CAPSULE_HALF_HEIGHT]).
|
||||
## [param move_dir_xz] steers multi-sample rays (see [method _walk_probe_xz_offsets]).
|
||||
func _apply_walk_air_gravity(
|
||||
delta: float, feet_y: float, move_dir_xz: Vector2 = Vector2.ZERO
|
||||
) -> void:
|
||||
var apply_gravity: bool = not is_on_floor()
|
||||
if (
|
||||
not apply_gravity
|
||||
and _has_walk_goal
|
||||
and is_on_floor()
|
||||
and not _walk_has_close_floor_probe_below(move_dir_xz)
|
||||
):
|
||||
apply_gravity = true
|
||||
if (
|
||||
not apply_gravity
|
||||
and _has_walk_goal
|
||||
and is_on_floor()
|
||||
and feet_y > _auth_walk_goal.y + WALK_DEEP_DESCENT_FEET_ABOVE_GOAL
|
||||
):
|
||||
apply_gravity = true
|
||||
if apply_gravity:
|
||||
## Only while **airborne**. With default [member MotionMode.MOTION_MODE_GROUNDED], [method move_and_slide]
|
||||
## clears downward [code]velocity.y[/code] every tick while [method is_on_floor] — adding gravity on the
|
||||
## velocity does **not** move the body off a terrace lip; use [method _apply_walk_post_slide_ledge_peel].
|
||||
func _apply_walk_air_gravity(delta: float, _feet_y: float, _move_dir_xz: Vector2 = Vector2.ZERO) -> void:
|
||||
if not is_on_floor():
|
||||
velocity += get_gravity() * delta
|
||||
|
||||
|
||||
## After [method move_and_slide], peel the capsule down when the authoritative goal is below the feet
|
||||
## but the engine still reports floor contact (GROUNDED mode ate negative [code]velocity.y[/code]).
|
||||
## Uses a small accumulated peel speed so displacement matches [code]∫ g dt[/code], not [code]g Δt[/code] (wrong units).
|
||||
func _apply_walk_post_slide_ledge_peel(
|
||||
delta: float, feet_y: float, move_dir_xz: Vector2
|
||||
) -> void:
|
||||
if not _has_walk_goal:
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
return
|
||||
if not is_on_floor():
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
return
|
||||
if _auth_walk_goal.y >= feet_y - DESCEND_GOAL_Y_MARGIN:
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
return
|
||||
var peel: bool = (
|
||||
feet_y > _auth_walk_goal.y + WALK_DEEP_DESCENT_FEET_ABOVE_GOAL
|
||||
or not _walk_has_close_floor_probe_below(move_dir_xz)
|
||||
)
|
||||
if not peel:
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
return
|
||||
_walk_ledge_peel_vy += get_gravity().y * delta
|
||||
global_position.y += _walk_ledge_peel_vy * delta
|
||||
|
||||
|
||||
func _walk_floor_snap_length(
|
||||
feet_y: float, want_goal_h: Vector3, move_dir_xz: Vector2 = Vector2.ZERO
|
||||
) -> float:
|
||||
|
|
@ -938,6 +954,7 @@ func _physics_process(delta: float) -> void:
|
|||
if horiz_dist <= ARRIVE_EPS and vert_err <= VERT_ARRIVE_EPS:
|
||||
velocity = Vector3.ZERO
|
||||
_has_walk_goal = false
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
floor_block_on_wall = true
|
||||
_floor_angle_loose_ticks = FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP
|
||||
_step_assist_active = false
|
||||
|
|
@ -1003,6 +1020,10 @@ func _physics_process(delta: float) -> void:
|
|||
_apply_walk_air_gravity(delta, feet_y, walk_move_dir_xz)
|
||||
floor_snap_length = _walk_floor_snap_length(feet_y, want_goal_h, walk_move_dir_xz)
|
||||
move_and_slide()
|
||||
var feet_after: float = capsule_feet_y(
|
||||
global_position.y, CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS
|
||||
)
|
||||
_apply_walk_post_slide_ledge_peel(delta, feet_after, walk_move_dir_xz)
|
||||
_after_walk_move_and_slide()
|
||||
_clear_step_assist_after_walk_move()
|
||||
_debug_trace_transform("physics")
|
||||
|
|
|
|||
Loading…
Reference in New Issue