NEON-29: Require forward floor probe to keep walk snap/gravity sane off ledges
Walk support used any ray hit under the foot disk, so the center sample could still see the platform while the leading edge was over a void. is_on_floor() stayed true from the lip, snap stayed strong, and vy was zeroed each tick — horizontal skate until XZ arrival. Now require a center hit plus at least one forward-biased sample in the movement direction when horizontal intent is non-zero; use velocity XZ for probe direction so nav/path steering matches the rays.pull/41/head
parent
1f6625c659
commit
6bd1acd9e7
|
|
@ -365,16 +365,30 @@ func _walk_ray_hit_up_floor(space: PhysicsDirectSpaceState3D, off_xz: Vector2) -
|
||||||
return n.dot(Vector3.UP) > WALK_SUPPORT_PROBE_MIN_UP_DOT
|
return n.dot(Vector3.UP) > WALK_SUPPORT_PROBE_MIN_UP_DOT
|
||||||
|
|
||||||
|
|
||||||
## True if **any** sample under the foot disk hits walk-like ground within [member WALK_SUPPORT_PROBE_DEPTH].
|
## Walkable support for **continued** forward motion within [member WALK_SUPPORT_PROBE_DEPTH].
|
||||||
## Uses [param move_dir_xz] so the **leading** edge off a deck is checked (center-only rays stayed over mesh).
|
## Requires a hit under the foot origin **and** at least one sample **ahead** in [param move_dir_xz]
|
||||||
|
## (same construction as [method _walk_probe_xz_offsets]). If only the center hits while the leading
|
||||||
|
## foot is over a void, [method CharacterBody3D.is_on_floor] can stay true from a lip — returning
|
||||||
|
## true here would suppress gravity and keep moving snap until XZ arrival (ledge skate).
|
||||||
|
## When [param move_dir_xz] is ~zero (idle / no horizontal intent), center-only is enough.
|
||||||
func _walk_has_close_floor_probe_below(move_dir_xz: Vector2) -> bool:
|
func _walk_has_close_floor_probe_below(move_dir_xz: Vector2) -> bool:
|
||||||
var w3d := get_world_3d()
|
var w3d := get_world_3d()
|
||||||
if w3d == null:
|
if w3d == null:
|
||||||
return true
|
return true
|
||||||
var space: PhysicsDirectSpaceState3D = w3d.direct_space_state
|
var space: PhysicsDirectSpaceState3D = w3d.direct_space_state
|
||||||
|
if not _walk_ray_hit_up_floor(space, Vector2.ZERO):
|
||||||
|
return false
|
||||||
|
var len2: float = move_dir_xz.length_squared()
|
||||||
|
if len2 < 1e-10:
|
||||||
|
return true
|
||||||
|
var inv_len: float = 1.0 / sqrt(len2)
|
||||||
|
var md := Vector2(move_dir_xz.x * inv_len, move_dir_xz.y * inv_len)
|
||||||
for off2: Vector2 in _walk_probe_xz_offsets(move_dir_xz):
|
for off2: Vector2 in _walk_probe_xz_offsets(move_dir_xz):
|
||||||
if _walk_ray_hit_up_floor(space, off2):
|
if off2.length_squared() < 1e-12:
|
||||||
return true
|
continue
|
||||||
|
if off2.dot(md) > 0.0:
|
||||||
|
if _walk_ray_hit_up_floor(space, off2):
|
||||||
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -922,9 +936,10 @@ func _physics_process(delta: float) -> void:
|
||||||
else:
|
else:
|
||||||
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
|
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
|
||||||
|
|
||||||
var walk_move_dir_xz := Vector2(want_goal_h.x, want_goal_h.z)
|
# Prefer actual horizontal velocity so probes match nav/path steering, not only click bearing.
|
||||||
|
var walk_move_dir_xz := Vector2(velocity.x, velocity.z)
|
||||||
if walk_move_dir_xz.length_squared() < 1e-10:
|
if walk_move_dir_xz.length_squared() < 1e-10:
|
||||||
walk_move_dir_xz = Vector2(velocity.x, velocity.z)
|
walk_move_dir_xz = Vector2(want_goal_h.x, want_goal_h.z)
|
||||||
|
|
||||||
_apply_walk_air_gravity(delta, walk_move_dir_xz)
|
_apply_walk_air_gravity(delta, walk_move_dir_xz)
|
||||||
floor_snap_length = _walk_floor_snap_length(feet_y, want_goal_h, walk_move_dir_xz)
|
floor_snap_length = _walk_floor_snap_length(feet_y, want_goal_h, walk_move_dir_xz)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue