Arms playtest-2 fixes: flip-proof elbow (angle clamp + rate limit, controller-driven), Stage 2b hand coupling (blocked arm holds the hand back)
- Elbow stored as an angle around the arm axis vs a torso-frame reference: 85% hand-suggested, from the controller pose (not the physics palm), clamped +-110 deg, 6 rad/s rate limit (flip spike: 1.8 cm/tick max vs 13.8 unclamped) - PhysicsArms.ConstrainHand: while an arm segment is in contact the hand may lead the forearm end by 3 cm; PhysicsHands applies it and suspends stuck-snap while active - ArmSpike: forearm-rest-over-anvil (owner pose: collider fine, drawn forearm 3.9 cm -> 0), flip sweep, bar test now asserts the hand is held back; all six spikes PASS x3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
464cda2f71
commit
7bc6a866eb
5 files changed
+193
-51
No files matched your search
@@ -15,6 +15,7 @@ public partial class PhysicsHands : Node
|
||||
|
||||
private XRController3D _left;
|
||||
private XRController3D _right;
|
||||
private PhysicsArms _arms;
|
||||
private readonly RigidBody3D[] _bodies = new RigidBody3D[2];
|
||||
private readonly MeshInstance3D[] _ghosts = new MeshInstance3D[2];
|
||||
private readonly float[] _stuckTimer = new float[2];
|
||||
@@ -24,6 +25,7 @@ public partial class PhysicsHands : Node
|
||||
_instance = this;
|
||||
_left = GetNode<XRController3D>("../XROrigin3D/LeftHand");
|
||||
_right = GetNode<XRController3D>("../XROrigin3D/RightHand");
|
||||
_arms = GetNodeOrNull<PhysicsArms>("../PhysicsArms");
|
||||
_bodies[0] = MakeHand("LeftHandBody", new Color(0.9f, 0.3f, 0.35f));
|
||||
_bodies[1] = MakeHand("RightHandBody", new Color(0.3f, 0.5f, 0.9f));
|
||||
_ghosts[0] = MakeGhost();
|
||||
@@ -92,6 +94,8 @@ public partial class PhysicsHands : Node
|
||||
return;
|
||||
int hand = _left == controller ? 0 : 1;
|
||||
Vector3 target = controller.GlobalPosition;
|
||||
Transform3D driveTarget = controller.GlobalTransform;
|
||||
bool armHeld = _arms != null && _arms.ConstrainHand(hand, ref driveTarget);
|
||||
|
||||
bool lagging = body.GlobalPosition.DistanceTo(target) > GhostLagDistance;
|
||||
if (_ghosts[hand] != null)
|
||||
@@ -103,7 +107,9 @@ public partial class PhysicsHands : Node
|
||||
|
||||
if (!PropGrabber.IsGrabbing(controller))
|
||||
{
|
||||
if (body.GlobalPosition.DistanceTo(target) > StuckDistance)
|
||||
// A hand held back by its own blocked arm is not stuck — snapping it into the
|
||||
// obstacle would just loop.
|
||||
if (body.GlobalPosition.DistanceTo(target) > StuckDistance && !armHeld)
|
||||
_stuckTimer[hand] += delta;
|
||||
else
|
||||
_stuckTimer[hand] = 0f;
|
||||
@@ -123,7 +129,7 @@ public partial class PhysicsHands : Node
|
||||
return;
|
||||
}
|
||||
}
|
||||
Drive(body, controller.GlobalTransform, delta);
|
||||
Drive(body, driveTarget, delta);
|
||||
}
|
||||
|
||||
// The hand chase itself — public so headless spikes drive their stand-ins with the real code.
|
||||
|
||||
Reference in new issue
Block a user