Skip to content

fix: foot-IK pelvis drop ignores feet over drop-offs (+ solver perf)#80

Merged
blugart-dev merged 3 commits into
mainfrom
fix/foot-ik-pelvis-drop
Jun 19, 2026
Merged

fix: foot-IK pelvis drop ignores feet over drop-offs (+ solver perf)#80
blugart-dev merged 3 commits into
mainfrom
fix/foot-ik-pelvis-drop

Conversation

@blugart-dev

Copy link
Copy Markdown
Owner

Summary

Fixes a foot-IK pelvis-drop bug where one foot over a gap/drop-off dragged the whole body down, plus removes the solver's per-frame allocations. Part of the 0.3.x finalization gate.

The bug (correctness)

The pelvis lowers to accommodate the lowest planted foot. But offset_* (which feed the pelvis drop) come from a ground raycast that can hit up to foot_ik_max_adjustment (0.5 m) below the foot. So a foot stepping out over a ledge/pit — its raycast hitting ground far below — was treated as a deep plant and pulled the pelvis down to foot_ik_max_pelvis_drop (0.35 m). That sank the body and lifted the other, genuinely-planted foot off its ground.

The fix

A foot now only informs the pelvis drop when it found ground the body can actually reach (within foot_ik_max_pelvis_drop). Feet over a gap (no raycast hit) or a deeper drop-off are excluded from the minf. Key property: offset_* feed only the pelvis drop — the per-leg IK solves already target the raw ground hit (gpos.y) — so legs still reach toward real ground; only the spurious whole-body sink is removed. On flat/normal terrain both feet are supported, so behavior is identical (existing flat-ground tests unchanged).

Regression test

test_pelvis_ignores_foot_over_dropoff builds split ground — solid under the left foot, a ~1 m drop-off under the right — and asserts the left foot still plants and the pelvis stays near neutral (> -0.1). On the old code the pelvis converged to -0.35, so the test genuinely distinguishes the fix.

Solver per-frame perf (no behavior change)

  • The target-override Dictionary handed to the SpringResolver each solve is now a reused buffer instead of a fresh {}. Safe because the controller's solve and the spring's read never interleave within a physics frame (each node's _physics_process runs to completion in turn).
  • Animation bone globals are memoized per solve (_anim_global / _anim_cache), so the hip/leg reads and the full-body shift no longer re-walk shared parent chains.

Validation

  • Headless import: clean
  • GUT: 113/113, 3× stable, no orphans (was 112; +1 new test)
  • Scene-smoke (--quit-after 150): clean on foot_ik_demo

Visual sign-off

Wants an in-editor pass on varied terrain before merge — specifically: stand a character with one foot on a platform edge and the other over a gap/lower step, and confirm the body no longer sinks/crouches when a foot overhangs a drop-off, while normal uneven ground (stairs/slopes within reach) still drops the pelvis as before.

🤖 Generated with Claude Code

Pelvis-drop correctness:
The pelvis lowers to accommodate the lowest planted foot. But a foot over a
gap or a drop-off — whose ground raycast can hit up to foot_ik_max_adjustment
(0.5 m) below the foot — was treated as a deep plant and dragged the whole
pelvis down to foot_ik_max_pelvis_drop (0.35 m), sinking the body and breaking
the OTHER, genuinely-planted foot's ground contact.

Now a foot only informs the pelvis drop when it found ground the body can
actually reach (within foot_ik_max_pelvis_drop); feet over a gap (no hit) or a
deeper drop-off are excluded from the min. offset_* feed only the pelvis drop —
the per-leg solves already target the raw ground hit — so the legs still reach
toward real ground; only the spurious whole-body sink is removed.

New regression test (test_foot_ik.gd): split ground, solid under one foot and a
~1 m drop-off under the other, asserts the planted foot still plants and the
pelvis stays near neutral instead of being yanked to -0.35.

Solver per-frame perf (no behavior change):
- The override dictionary handed to the SpringResolver is now a reused buffer
  instead of a fresh {} each solve. Safe: the controller's solve and the
  spring's read never interleave within a physics frame.
- Animation bone globals are memoized per solve (_anim_global / _anim_cache),
  so the hip/leg reads and the full-body shift no longer re-walk shared parent
  chains.

Validated: headless import clean; GUT 113/113 (3x stable, no orphans);
scene-smoke clean on foot_ik_demo.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@blugart-dev blugart-dev merged commit f706e2e into main Jun 19, 2026
1 check passed
@blugart-dev blugart-dev deleted the fix/foot-ik-pelvis-drop branch June 19, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant