NEON-29: Teal pad lips — wall normal assist, rim descend, +12mm lift
Step assist: use is_on_wall + get_wall_normal opposing want; keep slide loop with dot < -0.02. Descend: relax floor_block on shallow floor normal at internal edges; widen vertical-ish slide threshold slightly. Raise TerracePlatformA Y by 0.012 m vs main floor for coplanarity.pull/41/head
parent
b4c0defc29
commit
d81e915a0a
|
|
@ -40,7 +40,7 @@ Elevated walkables use **distinct albedo tints** in `scenes/main.tscn` so screen
|
||||||
|
|
||||||
### Height variation notes
|
### Height variation notes
|
||||||
|
|
||||||
- **Single-step terraces (A, C):** 0.3 m rise — within `agent_max_climb = 0.35`, so the nav mesh routes directly from floor onto the pad; one click suffices.
|
- **Single-step terraces (A, C):** 0.3 m rise — within `agent_max_climb = 0.35`, so the nav mesh routes directly from floor onto the pad; one click suffices. **`TerracePlatformA`** (teal) sits **12 mm** above the main floor slab in the scene to reduce coplanar internal edges with Jolt; nav rebakes at startup.
|
||||||
- **Two-level terrace B:** floor → `TerraceStepB` (0.3 m) → `TerracePlatformB` (additional 0.3 m). Each transition is within climb limit, so the agent can route the full ascent in a single click-to-move target anywhere on the platform.
|
- **Two-level terrace B:** floor → `TerraceStepB` (0.3 m) → `TerracePlatformB` (additional 0.3 m). Each transition is within climb limit, so the agent can route the full ascent in a single click-to-move target anywhere on the platform.
|
||||||
- **Occluders** are plain `StaticBody3D` with no `walkable` tag; the nav bake excludes their top surfaces, so they remain true obstacles for routing.
|
- **Occluders** are plain `StaticBody3D` with no `walkable` tag; the nav bake excludes their top surfaces, so they remain true obstacles for routing.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ shape = SubResource("BoxShape3D_obstacle_d")
|
||||||
|
|
||||||
[node name="TerracePlatformA" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=3000010 groups=["walkable"]]
|
[node name="TerracePlatformA" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=3000010 groups=["walkable"]]
|
||||||
collision_mask = 3
|
collision_mask = 3
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 0, -14)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 0.012, -14)
|
||||||
|
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA" unique_id=3000011]
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA" unique_id=3000011]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.15, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.15, 0)
|
||||||
|
|
|
||||||
|
|
@ -161,14 +161,22 @@ func _step_assist_has_support(want_dir_xz: Vector3) -> bool:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
## Returns true when slide collisions contain a wall-ish contact that opposes forward motion.
|
## True when a vertical-ish contact opposes motion toward the goal. Jolt often reports a wall
|
||||||
## The `is_on_wall()` short-circuit was removed: it returned true for any wall contact regardless
|
## via `is_on_wall()` + `get_wall_normal()` at box lips while slide normals are ambiguous; the
|
||||||
## of direction, firing the assist for sideways bump-skin contacts unrelated to step climbing.
|
## old slide-only loop missed head-on climbs onto e.g. TerracePlatformA (teal pad).
|
||||||
func _step_assist_wallish_blocks(want_dir_xz: Vector3) -> bool:
|
func _step_assist_wallish_blocks(want_dir_xz: Vector3) -> bool:
|
||||||
var w2 := Vector2(want_dir_xz.x, want_dir_xz.z)
|
var w2 := Vector2(want_dir_xz.x, want_dir_xz.z)
|
||||||
if w2.length_squared() < 1e-8:
|
if w2.length_squared() < 1e-8:
|
||||||
return false
|
return false
|
||||||
w2 = w2.normalized()
|
w2 = w2.normalized()
|
||||||
|
if is_on_wall():
|
||||||
|
var wn: Vector3 = get_wall_normal()
|
||||||
|
var wnh := Vector2(wn.x, wn.z)
|
||||||
|
if wnh.length_squared() > 1e-10:
|
||||||
|
wnh = wnh.normalized()
|
||||||
|
# Wall pushes back against forward (not parallel skimming along the face).
|
||||||
|
if wnh.dot(w2) < -0.02:
|
||||||
|
return true
|
||||||
for i: int in get_slide_collision_count():
|
for i: int in get_slide_collision_count():
|
||||||
var n: Vector3 = get_slide_collision(i).get_normal()
|
var n: Vector3 = get_slide_collision(i).get_normal()
|
||||||
if n.y > 0.52:
|
if n.y > 0.52:
|
||||||
|
|
@ -177,7 +185,7 @@ func _step_assist_wallish_blocks(want_dir_xz: Vector3) -> bool:
|
||||||
if n2.length_squared() < 1e-8:
|
if n2.length_squared() < 1e-8:
|
||||||
continue
|
continue
|
||||||
n2 = n2.normalized()
|
n2 = n2.normalized()
|
||||||
if n2.dot(w2) < -0.04:
|
if n2.dot(w2) < -0.02:
|
||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
@ -271,9 +279,13 @@ func _apply_floor_block_for_descend_at_wall(feet_y: float) -> void:
|
||||||
if is_on_wall():
|
if is_on_wall():
|
||||||
floor_block_on_wall = false
|
floor_block_on_wall = false
|
||||||
return
|
return
|
||||||
|
# Terrace lip: floor normal tilts off UP when straddling an internal edge (teal pad, etc.).
|
||||||
|
if is_on_floor() and get_floor_normal().dot(Vector3.UP) < 0.992:
|
||||||
|
floor_block_on_wall = false
|
||||||
|
return
|
||||||
for i: int in range(get_slide_collision_count()):
|
for i: int in range(get_slide_collision_count()):
|
||||||
var n: Vector3 = get_slide_collision(i).get_normal()
|
var n: Vector3 = get_slide_collision(i).get_normal()
|
||||||
if absf(n.y) < 0.52:
|
if absf(n.y) < 0.58:
|
||||||
floor_block_on_wall = false
|
floor_block_on_wall = false
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ No new automated GDScript tests are added for this story — there is no new GDS
|
||||||
- **Step assist undone by floor snap (resolved):** Each `_try_walk_step_assist()` cycle lifts the capsule 0.11 m. The prior `floor_snap_length = FLOOR_SNAP_MOVING = 0.32 m` meant the snap always reached the lower floor (0.11 m below the lifted bottom), pulling the capsule straight back down every tick. The player "nudged and stopped" because each lift was immediately cancelled. Fixed: added `WALK_STEP_ASSIST_SNAP = 0.09 m` constant (below 0.11 m so the snap cannot reach the previous floor, but above 0.03 m so it catches the step surface once cleared) and a `_step_assist_active` boolean flag. During active climbing, `floor_snap_length` is set to `WALK_STEP_ASSIST_SNAP` in both `_after_walk_move_and_slide()` and the main physics loop. The flag is cleared when the player lands cleanly on a floor without wall contact (`is_on_floor() and not is_on_wall()`), or when the nav goal is cleared/snapped.
|
- **Step assist undone by floor snap (resolved):** Each `_try_walk_step_assist()` cycle lifts the capsule 0.11 m. The prior `floor_snap_length = FLOOR_SNAP_MOVING = 0.32 m` meant the snap always reached the lower floor (0.11 m below the lifted bottom), pulling the capsule straight back down every tick. The player "nudged and stopped" because each lift was immediately cancelled. Fixed: added `WALK_STEP_ASSIST_SNAP = 0.09 m` constant (below 0.11 m so the snap cannot reach the previous floor, but above 0.03 m so it catches the step surface once cleared) and a `_step_assist_active` boolean flag. During active climbing, `floor_snap_length` is set to `WALK_STEP_ASSIST_SNAP` in both `_after_walk_move_and_slide()` and the main physics loop. The flag is cleared when the player lands cleanly on a floor without wall contact (`is_on_floor() and not is_on_wall()`), or when the nav goal is cleared/snapped.
|
||||||
- **Terrace B descend / climb (resolved, iterated):** Scene lifts and **descend-bypass** bee-lines caused regressions (stuck climbs, lip oscillation, wrong-direction slides). **Removed the descend bypass:** vertical moves (e.g. platform ↔ floor) use the **baked nav path + step assist** only. **Walk gravity** is **`!is_on_floor()`** again — never “goal below feet” while still walking on an upper surface (that applied gravity across the whole **TerracePlatformB** deck and destroyed horizontal motion). **`velocity.y`** is cleared by the horizontal steer each tick as before.
|
- **Terrace B descend / climb (resolved, iterated):** Scene lifts and **descend-bypass** bee-lines caused regressions (stuck climbs, lip oscillation, wrong-direction slides). **Removed the descend bypass:** vertical moves (e.g. platform ↔ floor) use the **baked nav path + step assist** only. **Walk gravity** is **`!is_on_floor()`** again — never “goal below feet” while still walking on an upper surface (that applied gravity across the whole **TerracePlatformB** deck and destroyed horizontal motion). **`velocity.y`** is cleared by the horizontal steer each tick as before.
|
||||||
- **Lip stuck (floor→platform / platform→floor):** QA screenshots for this were on **`TerracePlatformA`** (10 × 10 large SE pad, **0.3 m** single step — not the NW **Terrace B** step/platform pair). Step assist required **`vel_h.dot(want) ≤ 0.52`**, so a **head-on** push into the vertical face (dot ≈ 1) **never** triggered the lift. **Removed** that gate. For **platform→floor**, **`floor_block_on_wall = false`** only when the nav goal is **below** the feet **and** **`is_on_wall()`** or a **wall-ish slide normal** exists, so open deck walking stays stable.
|
- **Lip stuck (floor→platform / platform→floor):** QA screenshots for this were on **`TerracePlatformA`** (10 × 10 large SE pad, **0.3 m** single step — not the NW **Terrace B** step/platform pair). Step assist required **`vel_h.dot(want) ≤ 0.52`**, so a **head-on** push into the vertical face (dot ≈ 1) **never** triggered the lift. **Removed** that gate. For **platform→floor**, **`floor_block_on_wall = false`** only when the nav goal is **below** the feet **and** **`is_on_wall()`** or a **wall-ish slide normal** exists, so open deck walking stays stable.
|
||||||
|
- **Teal pad still stuck (follow-up):** Step assist used **slide normals only**; Jolt often exposes the lip via **`is_on_wall()`** / **`get_wall_normal()`** instead — re-added a **directional** wall check (`wnh.dot(want) < -0.02`). Descend relax now also triggers on **shallow floor normal** (`get_floor_normal().dot(UP) < 0.992`) and slightly wider vertical-ish slide band. **`TerracePlatformA`** root **Y = 0.012 m** breaks **coplanarity** with the main floor without the larger gap that hurt **TerraceStepB** approach.
|
||||||
|
|
||||||
## Open questions / risks
|
## Open questions / risks
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue