Soft grab (Nimso): ease the object into the hand (or the hand to the hold) before the weld

Light props (<= 6 kg) fly to the palm, static world / heavy things pull the hand; position only,
~0.1 s, never a snap. Welds no longer freeze a hover gap. Spike: 1 kg cube from 6 cm -> 1.5 mm,
bar from 5 cm -> 1.2 cm (contact slop on a curved hold).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
FoxyzandClaude Opus 5.5 committed 2026-10-02 20:28:40 +03:00
1 parent 83186f9676
commit 22159aa902
3 files changed
+198 -12

No files matched your search

+31 -12
View File
@@ -26,6 +26,7 @@ public partial class PropGrabber : Node
private readonly uint[] _grabSeq = new uint[2];
private readonly float[] _breakTimer = new float[2];
private readonly bool[] _gripPrev = new bool[2];
private readonly SoftGrab[] _easing = new SoftGrab[2]; // grabbed, being eased into the hand (no weld yet)
private uint _grabCount;
public override void _Ready()
@@ -50,32 +51,50 @@ public partial class PropGrabber : Node
public override void _PhysicsProcess(double delta)
{
float dt = (float)delta;
for (int hand = 0; hand < 2; hand++)
{
SoftGrab ease = _easing[hand];
if (ease == null)
continue;
if (!IsInstanceValid(ease.Target) || !IsInstanceValid(ease.Hand))
_easing[hand] = null;
else if (ease.Step(dt))
{
_easing[hand] = null;
Weld(hand == 0 ? _left : _right, hand, ease.Hand, ease.Target);
}
}
HoldUpdate(_left, 0, dt);
HoldUpdate(_right, 1, dt);
}
private void Weld(XRController3D controller, int hand, RigidBody3D handBody, PhysicsBody3D prop)
{
_held[hand] = prop;
_grabSeq[hand] = _grabCount++;
Vector3 pivot = PalmPoint(handBody, hand);
_joints[hand] = MakeWeld(handBody, prop, pivot, RigidWeldBias);
_localAnchor[hand] = prop.GlobalTransform.AffineInverse() * pivot;
_arms?.SetHeld(hand, prop as RigidBody3D);
PlayerRig.SetHeld(controller, prop);
string what = prop is RigidBody3D rb ? $"{rb.Mass:0.###} kg" : "static world";
GD.Print($"PropGrabber: grabbed '{prop.Name}' ({what}, {(hand == 0 ? "left" : "right")}{(_held[1 - hand] == prop ? ", two-handed" : "")})");
}
private void UpdateHand(XRController3D controller, int hand)
{
bool grip = controller.GetFloat("grip") > GripThreshold;
int other = 1 - hand;
if (grip && !_gripPrev[hand] && _held[hand] == null)
if (grip && !_gripPrev[hand] && _held[hand] == null && _easing[hand] == null)
{
PhysicsBody3D prop = FindProp(controller, hand);
RigidBody3D handBody = PhysicsHands.GetBody(controller);
if (prop != null && handBody != null)
{
_held[hand] = prop;
_grabSeq[hand] = _grabCount++;
Vector3 pivot = PalmPoint(handBody, hand);
_joints[hand] = MakeWeld(handBody, prop, pivot, RigidWeldBias);
_localAnchor[hand] = prop.GlobalTransform.AffineInverse() * pivot;
_arms?.SetHeld(hand, prop as RigidBody3D);
PlayerRig.SetHeld(controller, prop);
string what = prop is RigidBody3D rb ? $"{rb.Mass:0.###} kg" : "static world";
GD.Print($"PropGrabber: grabbed '{prop.Name}' ({what}, {(hand == 0 ? "left" : "right")}{(_held[other] == prop ? ", two-handed" : "")})");
}
_easing[hand] = SoftGrab.Begin(handBody, hand, prop); // welded once it's in the hand (_PhysicsProcess)
}
if (!grip)
_easing[hand] = null;
if (!grip && _held[hand] != null)
{