Body physics Stage 2: physical arm segments (chase-driven upper arm + forearm, hierarchical budgets, layer 5)

- PhysicsArms: per side 2.0 kg upper arm + 1.2 kg forearm RigidBody3D PD-chasing their
  analytic 2-bone IK pose, solved per physics tick from the physical chest shoulders to the
  physical hands; no joints (Box3D has no usable shoulder/wrist joint; AGENTS forbids joints
  between tracked bodies). Budgets from a 60 kg mass stat: hand 90 < forearm 180 < upper
  arm 360 < chest 2400 N. Owns calibration + reach budget (moved from BodyRig); 1 m snap,
  non-finite reset, BodyMath.RotationError servo.
- Layer 5 = player arms (mask 1); props/table/floor masks include it; no self-collision.
- BodyRig: visuals only, arms drawn shoulder -> physical elbow -> wrist.
- ArmSpike: track gap 0, swing lag <= 6 cm, arm pushes prop with hand clear, forearm blocked
  by bar without tunneling and quiet when pinned, teleport snap, NaN reset — PASS x3.
- CollapseSpike now runs the full body (49/49 x3); all six spikes PASS x3.
- HANDOFF/AGENTS: Stage 2 entry (item 10), layers, tuning table, Stage 2b next steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
FoxyzandClaude Opus 5.5 committed 2026-10-02 00:04:07 +03:00
1 parent cb518999e5
commit 7589b13aba
11 files changed
+708 -174

No files matched your search

+15 -1
View File
@@ -1,7 +1,8 @@
using System.Collections.Generic;
using Godot;
// Headless regression for the Stage-1 playtest collapse (2026-10-01). Two families:
// Headless regression for the Stage-1 playtest collapse (2026-10-01), now with the full Stage-2
// body (chest + PhysicsArms + hands). Two families:
// - Stacking: seated chest overlapping the static table, 50 kg anvil rigid-welded to two
// chase hands, the logged grab sequence (L, R two-handed, L release, L re-grab), the real
// PropShop with the chest plowing through the props. Did NOT reproduce the NaN — kept as
@@ -39,6 +40,7 @@ public partial class CollapseSpike : Node3D
private Node3D _world;
private Node3D _head;
private PhysicsTorso _torso;
private PhysicsArms _arms;
private readonly RigidBody3D[] _hands = new RigidBody3D[2];
private readonly Node3D[] _targets = new Node3D[2];
private readonly ConeTwistJoint3D[] _welds = new ConeTwistJoint3D[2];
@@ -189,6 +191,9 @@ public partial class CollapseSpike : Node3D
_world.AddChild(_targets[h]);
_targetBase[h] = handPos;
}
_arms = new PhysicsArms { HeadSource = _head, Torso = _torso, HandLeft = _hands[0], HandRight = _hands[1] };
_world.AddChild(_arms);
}
public override void _PhysicsProcess(double delta)
@@ -258,6 +263,15 @@ public partial class CollapseSpike : Node3D
Check("handR", _hands[1]);
if (_anvil != null)
Check("anvil", _anvil);
for (int side = 0; side < 2; side++)
{
if (_arms.UpperArm(side) == null)
continue;
Check($"upper arm {side}", _arms.UpperArm(side));
Check($"forearm {side}", _arms.Forearm(side));
if (_arms.Forearm(side).LinearVelocity.Length() > RunawaySpeed || _arms.UpperArm(side).LinearVelocity.Length() > RunawaySpeed)
Fail($"arm {side} runaway");
}
foreach (Node n in GetTree().GetNodesInGroup("prop"))
{
if (n is not RigidBody3D prop || prop.IsQueuedForDeletion())