From 3f912367ff54a5093ef003bf34576827ea4ec2ea Mon Sep 17 00:00:00 2001 From: Foxyz Date: Fri, 2 Oct 2026 01:48:12 +0300 Subject: [PATCH] Elbow: read the hand-suggested angle nearest the current one (fixes side-switching when pointing the controller down: 45 -> 0 switches); point-down spike phase Co-Authored-By: Claude Opus 5.5 --- scripts/ArmSpike.cs | 24 ++++++++++++++++++++++-- scripts/PhysicsArms.cs | 9 ++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/scripts/ArmSpike.cs b/scripts/ArmSpike.cs index 30adb86..b36a62c 100644 --- a/scripts/ArmSpike.cs +++ b/scripts/ArmSpike.cs @@ -53,6 +53,8 @@ public partial class ArmSpike : Node3D private float _maxElbowStep; private Vector3 _lastElbow; private float _maxPinHandLag; + private float _downSide; + private int _downSideSwitches; private float _restBodyPen; private float _restDrawnPen; private float _restAnvilMove; @@ -178,13 +180,31 @@ public partial class ArmSpike : Node3D Vector3 axis = (handPos - _arms.Shoulder(1)).Normalized(); Vector3 perp = axis.Cross(Vector3.Up).Normalized(); Vector3 fwd = (axis * Mathf.Cos(1.22f) + perp.Rotated(axis, Mathf.Tau * _phaseT) * Mathf.Sin(1.22f)).Normalized(); - _handTarget[1] = new Transform3D(Basis.LookingAt(fwd, Mathf.Abs(fwd.Y) > 0.95f ? Vector3.Forward : Vector3.Up), handPos); + if (_phaseT <= 3.5f) + _handTarget[1] = new Transform3D(Basis.LookingAt(fwd, Mathf.Abs(fwd.Y) > 0.95f ? Vector3.Forward : Vector3.Up), handPos); Vector3 elbow = _arms.ElbowTarget(1); if (_phaseT > 0.5f) _maxElbowStep = Mathf.Max(_maxElbowStep, elbow.DistanceTo(_lastElbow)); _lastElbow = elbow; - if (_phaseT > 3.5f) + if (_phaseT > 3.5f && _phaseT <= 5.5f) { + // Controller pointing straight down with tracker-like wobble: the elbow + // must stay on one side. + Vector3 wobble = new Vector3(Mathf.Sin(5f * _phaseT), 0f, Mathf.Cos(3.7f * _phaseT)) * 0.5f; + Vector3 down = (Vector3.Down + wobble).Normalized(); + _handTarget[1] = new Transform3D(Basis.LookingAt(down, Vector3.Forward), handPos); + float sideNow = Mathf.Sign((_arms.ElbowTarget(1) - _arms.Shoulder(1)).Dot(_torso.Body.GlobalTransform.Basis.X)); + if (_phaseT > 4f) + { + if (_downSide == 0f) + _downSide = sideNow; + else if (sideNow != _downSide) + _downSideSwitches++; + } + } + if (_phaseT > 5.5f) + { + Record($"point-down: elbow keeps its side under wobble (switches {_downSideSwitches})", _downSideSwitches == 0); Record($"flip: elbow never jumps (max step {_maxElbowStep:F4} m/tick, limit {FlipElbowStepLimit})", _maxElbowStep < FlipElbowStepLimit); _handTarget[1] = new Transform3D(Basis.Identity, _handRest[1]); _maxHoldPenetration = 0f; diff --git a/scripts/PhysicsArms.cs b/scripts/PhysicsArms.cs index 79ed97d..063bfb8 100644 --- a/scripts/PhysicsArms.cs +++ b/scripts/PhysicsArms.cs @@ -331,7 +331,14 @@ public partial class PhysicsArms : Node handElbow -= dir * handElbow.Dot(dir); float confidence = Mathf.Clamp(handElbow.Length() / (0.5f * radius + 1e-4f), 0f, 1f); if (handElbow.LengthSquared() > 1e-8f) - targetAngle = Mathf.Atan2(handElbow.Dot(refB), handElbow.Dot(refN)) * PoleHandWeight * confidence; + { + // Read the suggestion as the representation NEAREST the current elbow angle: + // pointing the controller down puts it at the ±180° seam, where jitter would + // otherwise flip the clamped elbow to the other side. + float raw = Mathf.Atan2(handElbow.Dot(refB), handElbow.Dot(refN)); + raw = _poleAngle[index] + Mathf.Wrap(raw - _poleAngle[index], -Mathf.Pi, Mathf.Pi); + targetAngle = raw * PoleHandWeight * confidence; + } } targetAngle = Mathf.Clamp(targetAngle, -PoleMaxAngle, PoleMaxAngle); float step = (targetAngle - _poleAngle[index]) * (1f - Mathf.Exp(-delta * PoleSmoothRate));