NEON-29: Replace full-time descend gravity with stall + floor_block peel
Always-on gravity while on_floor and descending made platform→floor feel glued (slow vertical creep, no horizontal until landing). Remove that; use 7x gravity only when lip_stall>0, wallish slide, or prior move left low horizontal speed while still far from goal (carry_lip_from_prior_move). Track _descend_lip_stall_ticks after descend/nomap moves; reset on new goal, arrival, snap, clear_nav. Relax floor_block_on_wall when goal is above feet and wallish (floor→step) or goal below and (wallish or stall) so slides can clear terrace lips. Reset floor_block_on_wall when idle or arrival.pull/41/head
parent
cc63e9bdd7
commit
d1f738249d
|
|
@ -31,6 +31,10 @@ const DESCEND_GOAL_Y_MARGIN: float = 0.06
|
||||||
## Cap floor snap while descending from a step so Jolt cannot snap the capsule back onto the
|
## Cap floor snap while descending from a step so Jolt cannot snap the capsule back onto the
|
||||||
## upper surface across an internal edge (lip hang). Relaxed once feet are near the goal height.
|
## upper surface across an internal edge (lip hang). Relaxed once feet are near the goal height.
|
||||||
const DESCEND_LIP_SNAP_CAP: float = 0.12
|
const DESCEND_LIP_SNAP_CAP: float = 0.12
|
||||||
|
## Extra downward integration vs plain gravity when the descend path is stalled at a lip (ticks>0
|
||||||
|
## or wall contact). Full-time on-floor gravity was removed — it made platform→floor crossing
|
||||||
|
## feel glued until the capsule finally dropped.
|
||||||
|
const DESCEND_LIP_GRAVITY_MULT: float = 7.0
|
||||||
## Y lift when blocked by wall-ish slide but nav goal is higher (stepped bumps).
|
## Y lift when blocked by wall-ish slide but nav goal is higher (stepped bumps).
|
||||||
const WALK_STEP_ASSIST_DELTA: float = 0.11
|
const WALK_STEP_ASSIST_DELTA: float = 0.11
|
||||||
## Skip assist when horizontal velocity already aligns enough with want-dir (corners slide slowly).
|
## Skip assist when horizontal velocity already aligns enough with want-dir (corners slide slowly).
|
||||||
|
|
@ -72,6 +76,8 @@ var _debug_last_idle_xz: Vector2 = Vector2.INF
|
||||||
var _debug_idle_heartbeat: int = 0
|
var _debug_idle_heartbeat: int = 0
|
||||||
var _debug_last_transform_xz: Vector2 = Vector2.INF
|
var _debug_last_transform_xz: Vector2 = Vector2.INF
|
||||||
var _debug_trace_frame: int = 0
|
var _debug_trace_frame: int = 0
|
||||||
|
## Frames of low horizontal progress while descending; drives lip peel + floor_block relax.
|
||||||
|
var _descend_lip_stall_ticks: int = 0
|
||||||
|
|
||||||
@onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D
|
@onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D
|
||||||
|
|
||||||
|
|
@ -92,6 +98,7 @@ func set_authoritative_nav_goal(world_pos: Vector3) -> void:
|
||||||
_step_assist_active = false
|
_step_assist_active = false
|
||||||
_idle_anchor_active = false
|
_idle_anchor_active = false
|
||||||
_idle_manual_correction_ticks = 0
|
_idle_manual_correction_ticks = 0
|
||||||
|
_descend_lip_stall_ticks = 0
|
||||||
_nav_agent.set_target_position(world_pos)
|
_nav_agent.set_target_position(world_pos)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -102,6 +109,7 @@ func clear_nav_goal() -> void:
|
||||||
_step_assist_active = false
|
_step_assist_active = false
|
||||||
_idle_anchor_active = false
|
_idle_anchor_active = false
|
||||||
_idle_manual_correction_ticks = 0
|
_idle_manual_correction_ticks = 0
|
||||||
|
_descend_lip_stall_ticks = 0
|
||||||
_nav_agent.set_target_position(global_position)
|
_nav_agent.set_target_position(global_position)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -138,6 +146,7 @@ func snap_to_server(world_pos: Vector3) -> void:
|
||||||
_step_assist_active = false
|
_step_assist_active = false
|
||||||
_idle_anchor_active = false
|
_idle_anchor_active = false
|
||||||
_idle_manual_correction_ticks = 0
|
_idle_manual_correction_ticks = 0
|
||||||
|
_descend_lip_stall_ticks = 0
|
||||||
_nav_agent.set_target_position(settled)
|
_nav_agent.set_target_position(settled)
|
||||||
reset_physics_interpolation()
|
reset_physics_interpolation()
|
||||||
|
|
||||||
|
|
@ -256,18 +265,63 @@ func _apply_walk_air_gravity(delta: float) -> void:
|
||||||
var g_step: Vector3 = get_gravity() * delta
|
var g_step: Vector3 = get_gravity() * delta
|
||||||
if not is_on_floor():
|
if not is_on_floor():
|
||||||
velocity += g_step
|
velocity += g_step
|
||||||
|
|
||||||
|
|
||||||
|
func _walk_has_wallish_slide_contact() -> bool:
|
||||||
|
if is_on_wall():
|
||||||
|
return true
|
||||||
|
for i: int in get_slide_collision_count():
|
||||||
|
if get_slide_collision(i).get_normal().y < 0.52:
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
func _update_floor_block_on_wall_for_terraces(feet_y: float) -> void:
|
||||||
|
floor_block_on_wall = true
|
||||||
|
if not _has_walk_goal:
|
||||||
|
return
|
||||||
|
var goal_above: bool = _auth_walk_goal.y > feet_y + 0.06
|
||||||
|
var goal_below: bool = _auth_walk_goal.y < feet_y - DESCEND_GOAL_Y_MARGIN
|
||||||
|
if not goal_above and not goal_below:
|
||||||
|
return
|
||||||
|
var wallish: bool = _walk_has_wallish_slide_contact()
|
||||||
|
if goal_above and wallish:
|
||||||
|
floor_block_on_wall = false
|
||||||
|
elif goal_below and (wallish or _descend_lip_stall_ticks >= 1):
|
||||||
|
floor_block_on_wall = false
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_descend_lip_peel(
|
||||||
|
delta: float, feet_y: float, carry_lip_from_prior_move: bool = false
|
||||||
|
) -> void:
|
||||||
|
if _auth_walk_goal.y >= feet_y - DESCEND_GOAL_Y_MARGIN:
|
||||||
|
return
|
||||||
|
var g := get_gravity() * delta
|
||||||
|
if g.y >= 0.0:
|
||||||
|
return
|
||||||
|
if (
|
||||||
|
_descend_lip_stall_ticks >= 1
|
||||||
|
or _walk_has_wallish_slide_contact()
|
||||||
|
or carry_lip_from_prior_move
|
||||||
|
):
|
||||||
|
velocity.y += g.y * DESCEND_LIP_GRAVITY_MULT
|
||||||
|
|
||||||
|
|
||||||
|
func _descend_update_lip_stall_after_move() -> void:
|
||||||
|
if not _has_walk_goal:
|
||||||
|
_descend_lip_stall_ticks = 0
|
||||||
return
|
return
|
||||||
# Descending toward a lower surface: `is_on_floor()` can stay true on the upper step while
|
|
||||||
# horizontal motion is blocked (internal edge / lip). `is_on_wall()` is unreliable here, and
|
|
||||||
# a far floor click keeps large horiz delta so wall heuristics never ran. Always integrate
|
|
||||||
# downward acceleration while on the upper surface; `move_and_slide` + floor collision
|
|
||||||
# absorbs it on flat spans, and it peels the capsule off the lip when forward motion stalls.
|
|
||||||
var feet_y: float = capsule_feet_y(global_position.y, CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS)
|
var feet_y: float = capsule_feet_y(global_position.y, CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS)
|
||||||
var descending: bool = (
|
if _auth_walk_goal.y >= feet_y - DESCEND_GOAL_Y_MARGIN:
|
||||||
_has_walk_goal and _auth_walk_goal.y < feet_y - DESCEND_GOAL_Y_MARGIN
|
_descend_lip_stall_ticks = 0
|
||||||
)
|
return
|
||||||
if descending and g_step.y < 0.0:
|
var want_h := Vector2(_auth_walk_goal.x - global_position.x, _auth_walk_goal.z - global_position.z)
|
||||||
velocity.y += g_step.y
|
var hdist: float = want_h.length()
|
||||||
|
var vh: float = Vector2(velocity.x, velocity.z).length()
|
||||||
|
if hdist > 0.08 and vh < 0.52:
|
||||||
|
_descend_lip_stall_ticks = mini(_descend_lip_stall_ticks + 1, 72)
|
||||||
|
else:
|
||||||
|
_descend_lip_stall_ticks = 0
|
||||||
|
|
||||||
|
|
||||||
func _walk_floor_snap_length(feet_y: float) -> float:
|
func _walk_floor_snap_length(feet_y: float) -> float:
|
||||||
|
|
@ -559,6 +613,7 @@ func _physics_process(delta: float) -> void:
|
||||||
FLOOR_MAX_ANGLE_MOVING_DEG if use_loose_floor_angle else FLOOR_MAX_ANGLE_IDLE_DEG
|
FLOOR_MAX_ANGLE_MOVING_DEG if use_loose_floor_angle else FLOOR_MAX_ANGLE_IDLE_DEG
|
||||||
)
|
)
|
||||||
if not _has_walk_goal:
|
if not _has_walk_goal:
|
||||||
|
floor_block_on_wall = true
|
||||||
if _stable_idle_support():
|
if _stable_idle_support():
|
||||||
_idle_manual_correction_ticks = 0
|
_idle_manual_correction_ticks = 0
|
||||||
velocity = Vector3.ZERO
|
velocity = Vector3.ZERO
|
||||||
|
|
@ -615,6 +670,8 @@ func _physics_process(delta: float) -> void:
|
||||||
if horiz_dist <= ARRIVE_EPS and vert_err <= VERT_ARRIVE_EPS:
|
if horiz_dist <= ARRIVE_EPS and vert_err <= VERT_ARRIVE_EPS:
|
||||||
velocity = Vector3.ZERO
|
velocity = Vector3.ZERO
|
||||||
_has_walk_goal = false
|
_has_walk_goal = false
|
||||||
|
_descend_lip_stall_ticks = 0
|
||||||
|
floor_block_on_wall = true
|
||||||
_floor_angle_loose_ticks = FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP
|
_floor_angle_loose_ticks = FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP
|
||||||
_step_assist_active = false
|
_step_assist_active = false
|
||||||
_idle_anchor_active = false
|
_idle_anchor_active = false
|
||||||
|
|
@ -629,15 +686,25 @@ func _physics_process(delta: float) -> void:
|
||||||
|
|
||||||
_idle_manual_correction_ticks = 0
|
_idle_manual_correction_ticks = 0
|
||||||
|
|
||||||
|
var feet_y: float = capsule_feet_y(global_position.y, CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS)
|
||||||
|
_update_floor_block_on_wall_for_terraces(feet_y)
|
||||||
|
|
||||||
var nav_map: RID = _nav_agent.get_navigation_map()
|
var nav_map: RID = _nav_agent.get_navigation_map()
|
||||||
if NavigationServer3D.map_get_iteration_id(nav_map) == 0:
|
if NavigationServer3D.map_get_iteration_id(nav_map) == 0:
|
||||||
|
var h_peel: float = Vector2(want_goal_h.x, want_goal_h.z).length()
|
||||||
|
var pre_vh: float = Vector2(velocity.x, velocity.z).length()
|
||||||
|
var lip_carry: bool = (
|
||||||
|
_auth_walk_goal.y < feet_y - DESCEND_GOAL_Y_MARGIN
|
||||||
|
and h_peel > 0.1
|
||||||
|
and pre_vh < 0.55
|
||||||
|
)
|
||||||
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
|
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
|
||||||
_apply_walk_air_gravity(delta)
|
_apply_walk_air_gravity(delta)
|
||||||
floor_snap_length = _walk_floor_snap_length(
|
_apply_descend_lip_peel(delta, feet_y, lip_carry)
|
||||||
capsule_feet_y(global_position.y, CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS)
|
floor_snap_length = _walk_floor_snap_length(feet_y)
|
||||||
)
|
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
_after_walk_move_and_slide()
|
_after_walk_move_and_slide()
|
||||||
|
_descend_update_lip_stall_after_move()
|
||||||
if _step_assist_active and (
|
if _step_assist_active and (
|
||||||
(is_on_floor() and not is_on_wall())
|
(is_on_floor() and not is_on_wall())
|
||||||
or (not is_on_floor() and get_slide_collision_count() == 0)
|
or (not is_on_floor() and get_slide_collision_count() == 0)
|
||||||
|
|
@ -652,13 +719,17 @@ func _physics_process(delta: float) -> void:
|
||||||
# physical feet. Using CAPSULE_HALF_HEIGHT alone (0.5) was ~0.4 m too high: from the first
|
# physical feet. Using CAPSULE_HALF_HEIGHT alone (0.5) was ~0.4 m too high: from the first
|
||||||
# step (body 1.2 → code-feet 0.7) it incorrectly fired for the platform goal (Y=0.6 < 0.64),
|
# step (body 1.2 → code-feet 0.7) it incorrectly fired for the platform goal (Y=0.6 < 0.64),
|
||||||
# using FLOOR_SNAP_MOVING to pull the capsule back to the step after every assist lift.
|
# using FLOOR_SNAP_MOVING to pull the capsule back to the step after every assist lift.
|
||||||
var feet_y: float = capsule_feet_y(global_position.y, CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS)
|
|
||||||
if _auth_walk_goal.y < feet_y - DESCEND_GOAL_Y_MARGIN:
|
if _auth_walk_goal.y < feet_y - DESCEND_GOAL_Y_MARGIN:
|
||||||
|
var h_peel2: float = Vector2(want_goal_h.x, want_goal_h.z).length()
|
||||||
|
var pre_vh2: float = Vector2(velocity.x, velocity.z).length()
|
||||||
|
var lip_carry2: bool = h_peel2 > 0.1 and pre_vh2 < 0.55
|
||||||
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
|
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
|
||||||
_apply_walk_air_gravity(delta)
|
_apply_walk_air_gravity(delta)
|
||||||
|
_apply_descend_lip_peel(delta, feet_y, lip_carry2)
|
||||||
floor_snap_length = _walk_floor_snap_length(feet_y)
|
floor_snap_length = _walk_floor_snap_length(feet_y)
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
_after_walk_move_and_slide()
|
_after_walk_move_and_slide()
|
||||||
|
_descend_update_lip_stall_after_move()
|
||||||
if _step_assist_active and (
|
if _step_assist_active and (
|
||||||
(is_on_floor() and not is_on_wall())
|
(is_on_floor() and not is_on_wall())
|
||||||
or (not is_on_floor() and get_slide_collision_count() == 0)
|
or (not is_on_floor() and get_slide_collision_count() == 0)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue