Body step 2.2: fix the 2.1 headset instabilities

Spike now runs real input (90 Hz sample-and-hold tracking + jitter under 144 Hz
physics), which reproduced the playtest. Root causes fixed:
- PoseFilter/RagdollDriver: timestamped extrapolation of tracking, one tick
  pipeline shared by PlayerRig and the spike
- low-pass all feed-forwards (ground control amplified jitter by body mass)
- arm muscles back to local targets (physical-frame aim fed chest rotation back)
- structural hand strength only when braced or holding
- no free-path view chasing while grounded (8 m crouch slide); stick hold-back
- explicit inertia for the Legs body (shape-less tuck tumbled the pelvis)
- world grab: Box3D shape queries return a null collider, use collider_id
- new crouch-hold phases; world-grab detection asserted

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
FoxyzandClaude Opus 5.5 committed 2026-10-02 08:37:56 +03:00
1 parent 2aff2b61da
commit 6006c5de98
8 files changed
+352 -81

No files matched your search

+19 -25
View File
@@ -33,9 +33,9 @@ public partial class PlayerRig : Node
private bool _haveLastOrigin;
private Vector3 _moveVelocity;
private bool _snapArmed = true;
private readonly Vector3[] _ctrlPrev = new Vector3[2];
private readonly MeshInstance3D[] _ghosts = new MeshInstance3D[2];
private bool _rebuild = true;
private RagdollDriver _driver;
public PlayerRagdoll Ragdoll => _ragdoll;
public static bool Spectator { get; private set; }
@@ -84,6 +84,7 @@ public partial class PlayerRig : Node
// frame); the body is simulated, so it interpolates.
_ragdoll = new PlayerRagdoll { Name = "Ragdoll", PhysicsInterpolationMode = PhysicsInterpolationModeEnum.On };
AddChild(_ragdoll);
_driver = new RagdollDriver { Solver = Solver, Ragdoll = _ragdoll };
BodyProfile.Changed += _ => _rebuild = true;
for (int i = 0; i < 2; i++)
{
@@ -121,30 +122,16 @@ public partial class PlayerRig : Node
if (_haveLastOrigin && _ragdoll.Built && IsJump(_lastOrigin, origin))
Carry(origin * _lastOrigin.AffineInverse());
if (_ragdoll.Built && !_rebuild)
{
// 2. Stick locomotion, 3. reconciliation.
// 2. Stick locomotion.
if (_ragdoll.Built && !_driver.RebuildRequested)
Locomote(dt);
Vector3 correction = _ragdoll.OriginCorrection(_camera.GlobalPosition, _origin.GlobalPosition.Y, dt);
if (correction != Vector3.Zero)
_origin.GlobalPosition += correction;
}
// 4. Pose + muscles.
Transform3D head = _camera.GlobalTransform;
Transform3D left = _left.GlobalTransform, right = _right.GlobalTransform;
Solver.Solve(profile, head, left, right, _origin.GlobalPosition.Y, dt);
if (_rebuild || !_ragdoll.Built)
{
_ragdoll.Build(profile, Solver, left, right);
_rebuild = false;
_ctrlPrev[0] = left.Origin;
_ctrlPrev[1] = right.Origin;
}
Vector3 lv = (left.Origin - _ctrlPrev[0]) / dt, rv = (right.Origin - _ctrlPrev[1]) / dt;
_ctrlPrev[0] = left.Origin;
_ctrlPrev[1] = right.Origin;
_ragdoll.Step(Solver, left, right, lv, rv, _origin.GlobalPosition.Y, dt);
// 3. Filtered tracking → play-space correction → True Body → muscles (shared with RagdollSpike).
_driver.RebuildRequested |= _rebuild;
_rebuild = false;
Transform3D o = _origin.GlobalTransform;
_driver.Tick(profile, ref o, _camera.Transform, _left.Transform, _right.Transform, _moveVelocity, dt);
_origin.GlobalTransform = o;
_lastOrigin = _origin.GlobalTransform;
_haveLastOrigin = true;
}
@@ -162,6 +149,14 @@ public partial class PlayerRig : Node
}
}
// The body is held back (hips against a table, a wall the head clears): stop the stick from
// carrying the view further that way.
public static Vector3 HoldBack(Vector3 velocity, Vector3 heldBack)
{
float into = velocity.Dot(heldBack);
return into > 0f ? velocity - heldBack * into : velocity;
}
private static bool IsJump(Transform3D a, Transform3D b) =>
a.Origin.DistanceTo(b.Origin) > JumpDistance ||
BodyMath.RotationError(b.Basis.GetRotationQuaternion(), a.Basis.GetRotationQuaternion()).Length() > JumpAngle;
@@ -170,8 +165,6 @@ public partial class PlayerRig : Node
{
_ragdoll.TeleportBy(delta);
Solver.Shift(delta);
_ctrlPrev[0] = delta * _ctrlPrev[0];
_ctrlPrev[1] = delta * _ctrlPrev[1];
}
private void Locomote(float dt)
@@ -195,6 +188,7 @@ public partial class PlayerRig : Node
if (!_ragdoll.Grounded)
desired = _moveVelocity; // no air control
_moveVelocity = _moveVelocity.MoveToward(desired, MoveAcceleration * dt);
_moveVelocity = HoldBack(_moveVelocity, _ragdoll.HeldBack);
if (_moveVelocity != Vector3.Zero)
_origin.GlobalPosition += _moveVelocity * dt;