Grab buffer (squeeze early, catch on contact), elbow can't fold inward when the controller turns out
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ca60df3d75
commit
b8365ee95f
4 files changed
+48
-5
No files matched your search
@@ -241,3 +241,9 @@ engine does all the work"; higher ground "doesn't move my body up … until I pu
|
||||
| **Render-clock tracking timestamps** (`PoseFilter.Sample(..., sampleTime)`, `PlayerRig.TrackingAge`, bible v2 recipe) | At 1 m/s the per-tick speed spread is 1.25 m/s by arrival vs 0.013 m/s render-timed — every feed-forward beat at 18 Hz. Constant ~2-tick lag instead. Toggle in the menu to compare |
|
||||
| **max_physics_steps_per_frame 14 → 6** | One hitch could queue 14 ragdoll ticks in a frame (bible v2) |
|
||||
| Tried and reverted: arm aim from the physical shoulder vertically while climbing/tilted | Free-hand error 6.4 → 6.2 cm only, and it weakened pulls. The "tracking off when tilted" screenshots need a recording to diagnose |
|
||||
|
||||
**2.7 follow-ups (owner playtest):** grab buffer — a squeeze that finds nothing keeps trying for 0.4 s while held
|
||||
(`PropGrabber.GrabBuffer`; launching up a ladder left too short a window to catch a rung; Nimso's videos only
|
||||
mention arm collision saving missed grabs). Elbow pole clamp asymmetric: 110° outward, 35° inward
|
||||
(`ArmSolver.PoleMaxInward`; controller turned out folded the elbow 5.6 cm inward → 2.6 cm; 17° broke the crawl's
|
||||
pillar arms). Known, minor: the procedural legs lag and jitter during very fast falls.
|
||||
@@ -11,7 +11,8 @@ public sealed class ArmSolver
|
||||
public const float SoftIKFactor = 0.98f;
|
||||
public const float PoleHandWeight = 0.85f;
|
||||
public const float PoleSmoothRate = 12f;
|
||||
public const float PoleMaxAngle = 1.92f; // 110 deg either way from down/back/out
|
||||
public const float PoleMaxAngle = 1.92f; // 110 deg outward from down/back/out
|
||||
public const float PoleMaxInward = 0.6f; // 35 deg inward: a controller turned outward must not fold the elbows toward each other (owner)
|
||||
public const float PoleMaxSpeed = 6f; // rad/s
|
||||
public const float ElbowRiseAllowance = 0.05f;
|
||||
|
||||
@@ -76,7 +77,10 @@ public sealed class ArmSolver
|
||||
targetAngle = raw * PoleHandWeight * confidence;
|
||||
}
|
||||
}
|
||||
targetAngle = Mathf.Clamp(targetAngle, -PoleMaxAngle, PoleMaxAngle);
|
||||
// Asymmetric: the elbow may swing far out, but only a little in toward the body's midline.
|
||||
float outward = refB.Dot(side); // > 0: a positive angle swings the elbow outward
|
||||
float inwardLimit = Mathf.Lerp(PoleMaxAngle, PoleMaxInward, Mathf.Clamp(Mathf.Abs(outward) / 0.5f, 0f, 1f));
|
||||
targetAngle = outward >= 0f ? Mathf.Clamp(targetAngle, -inwardLimit, PoleMaxAngle) : Mathf.Clamp(targetAngle, -PoleMaxAngle, inwardLimit);
|
||||
float step = (targetAngle - _poleAngle) * (1f - Mathf.Exp(-delta * PoleSmoothRate));
|
||||
_poleAngle += Mathf.Clamp(step, -PoleMaxSpeed * delta, PoleMaxSpeed * delta);
|
||||
Vector3 pole = refN.Rotated(dir, _poleAngle);
|
||||
|
||||
+13
-2
@@ -11,6 +11,10 @@ public partial class PropGrabber : Node
|
||||
private const float BreakTime = 0.25f;
|
||||
private const float BreakDistanceBoth = 0.4f;
|
||||
private const float GripRipDistance = 0.8f;
|
||||
// Grab buffer (owner: launching up a ladder leaves a short window to catch the next rung): a
|
||||
// squeeze that finds nothing keeps trying while held, so you can squeeze early and catch the
|
||||
// hold the moment the palm reaches it.
|
||||
private const float GrabBuffer = 0.4f; // s
|
||||
private const float WorldGrabRadius = 0.07f; // palm sphere for grabbing static world (ledges, bars, holds)
|
||||
public const string NoGrabGroup = "no_grab"; // static bodies that can't be grabbed (the floor)
|
||||
|
||||
@@ -26,7 +30,8 @@ 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 readonly SoftGrab[] _easing = new SoftGrab[2];
|
||||
private readonly double[] _bufferUntil = new double[2]; // grabbed, being eased into the hand (no weld yet)
|
||||
private uint _grabCount;
|
||||
|
||||
public override void _Ready()
|
||||
@@ -86,12 +91,18 @@ public partial class PropGrabber : Node
|
||||
bool grip = controller.GetFloat("grip") > GripThreshold;
|
||||
int other = 1 - hand;
|
||||
|
||||
if (grip && !_gripPrev[hand] && _held[hand] == null && _easing[hand] == null)
|
||||
double now = Time.GetTicksMsec() / 1000.0;
|
||||
if (grip && !_gripPrev[hand])
|
||||
_bufferUntil[hand] = now + GrabBuffer;
|
||||
if (grip && now <= _bufferUntil[hand] && _held[hand] == null && _easing[hand] == null)
|
||||
{
|
||||
PhysicsBody3D prop = FindProp(controller, hand);
|
||||
RigidBody3D handBody = PhysicsHands.GetBody(controller);
|
||||
if (prop != null && handBody != null)
|
||||
{
|
||||
_easing[hand] = SoftGrab.Begin(handBody, hand, prop); // welded once it's in the hand (_PhysicsProcess)
|
||||
_bufferUntil[hand] = 0.0;
|
||||
}
|
||||
}
|
||||
if (!grip)
|
||||
_easing[hand] = null;
|
||||
|
||||
+23
-1
@@ -84,6 +84,7 @@ public partial class RagdollSpike : Node3D
|
||||
|
||||
_phases.Add(("hinge sign", SetupHinge, TickHinge, 0.3f, FinishHinge));
|
||||
_phases.Add(("pose filter: render-timed tracking moves evenly (no 18 Hz beat)", Rest, _ => { }, 0.05f, FinishPoseFilter));
|
||||
_phases.Add(("elbow: a controller turned outward doesn't fold the elbow inward", Rest, _ => { }, 0.05f, FinishElbowOut));
|
||||
_phases.Add(("stands still, head on the camera, hands on the controllers", Rest, _ => { }, 4f, FinishStand));
|
||||
_phases.Add(("reaches forward, out and overhead", Rest, TickReach, 4.5f, FinishReach));
|
||||
_phases.Add(("fast arm swings stay attached", Rest, TickSwing, 3f, FinishSwing));
|
||||
@@ -234,6 +235,26 @@ public partial class RagdollSpike : Node3D
|
||||
|
||||
private int NewSnaps => _ragdoll.Snaps - _snapsAtStart;
|
||||
|
||||
// Right arm reaching forward-down, controller yawed 0..90° outward: the elbow must stay on the
|
||||
// outer side of the shoulder→wrist line (toward +X), never toward the other arm.
|
||||
private static bool FinishElbowOut()
|
||||
{
|
||||
float worst = float.MaxValue;
|
||||
for (int deg = 0; deg <= 90; deg += 15)
|
||||
{
|
||||
ArmSolver arm = new();
|
||||
Vector3 shoulder = new(0.2f, 1.45f, 0f), target = new(0.25f, 1.05f, -0.4f);
|
||||
Basis hand = new Basis(Vector3.Up, -Mathf.DegToRad(deg)) * new Basis(Vector3.Right, -0.6f);
|
||||
for (int i = 0; i < 200; i++)
|
||||
arm.Solve(shoulder, target, Basis.Identity, Vector3.Right, hand, 0.3f, 0.27f, 1f / 144f);
|
||||
Vector3 dir = (arm.Wrist - shoulder).Normalized();
|
||||
Vector3 mid = shoulder + dir * (arm.Elbow - shoulder).Dot(dir);
|
||||
worst = Mathf.Min(worst, arm.Elbow.X - mid.X); // < 0 = elbow toward the midline
|
||||
}
|
||||
GD.Print($"RAGDOLL: elbow vs the arm line with the controller turned 0..90° outward: most inward {worst:F3} m (limit -0.04)");
|
||||
return worst > -0.04f;
|
||||
}
|
||||
|
||||
// A controller moving at 1 m/s, sampled once per 90 Hz frame (predicted for that frame's render
|
||||
// time), read by 144 Hz ticks. Per-tick output steps should all be 1/144 m: timestamped by
|
||||
// arrival they alternate (1 or 2 ticks late), timestamped by render time they don't.
|
||||
@@ -1117,7 +1138,8 @@ public partial class RagdollSpike : Node3D
|
||||
{
|
||||
float t = _t;
|
||||
// Walk up to a waist-high ledge (stop before its far end); a chest-high one starts within reach.
|
||||
_stickVelocity = (t < 1f && _ledgeTop < 1f) || (t > 4.5f && t < 5.5f) ? new Vector3(0f, 0f, -0.8f) : Vector3.Zero;
|
||||
// (A 70 cm box is too small to walk on after climbing it.)
|
||||
_stickVelocity = (t < 1f && _ledgeTop < 1f) || (t > 4.5f && t < 5.5f && _ledgeBox == null) ? new Vector3(0f, 0f, -0.8f) : Vector3.Zero;
|
||||
// Crouch to load the legs for a waist-high ledge; at a chest-high one you reach up instead.
|
||||
float crouch = Mathf.SmoothStep(0f, 1f, Mathf.Clamp((t - 1f) / 0.8f, 0f, 1f)) * (_ledgeTop < 1f ? 1f : 0f);
|
||||
float rise = Mathf.SmoothStep(0f, 1f, Mathf.Clamp((t - 2.2f) / 1.6f, 0f, 1f));
|
||||
|
||||
Reference in new issue
Block a user