- Step rule + lower-body collider: no walking onto tables, steps <= 0.35 m - Structural hand strength: two-hand mantle/pull-up works, one hand can't - Arms aim from the physical shoulder horizontally, keep True Body pose vertically - Crawl pose, crawl carry, 35 deg slope limit, ground feed-forward ignores play-space corrections (fixes a runaway feedback loop) - Grab static world (climbing), procedural visual legs - Playground sandbox; menu strength slider, spectator (old drag) and turn toggles - Physics 144 Hz with interpolation; legacy chase-body spikes pin 90 Hz - RagdollSpike: 27 phases incl. mantle, pull-up, crawl, ledge drop, ramps Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
108 lines
3.7 KiB
C#
108 lines
3.7 KiB
C#
using Godot;
|
|
|
|
// The old grab-the-air movement. Since the ragdoll player (body step 2) it is only the SPECTATOR
|
|
// mode (menu → Player → Spectator), where there is no body; off by default. Known issues kept as-is
|
|
// (owner, 2026-10-02): two-hand turning isn't 2-anchor based, simultaneous two-hand moves act weird.
|
|
public partial class AnchorMoveMode : Node
|
|
{
|
|
public static bool Enabled;
|
|
|
|
private const float GripThreshold = 0.6f;
|
|
|
|
private XROrigin3D _origin;
|
|
private XRCamera3D _camera;
|
|
private XRController3D _left;
|
|
private XRController3D _right;
|
|
|
|
private bool _wasTwoHand;
|
|
private Vector3 _leftPrev;
|
|
private Vector3 _rightPrev;
|
|
private Vector3 _twoHandMidPrev;
|
|
private float _twoHandYawPrev;
|
|
|
|
public override void _Ready()
|
|
{
|
|
_origin = GetNode<XROrigin3D>("../XROrigin3D");
|
|
_camera = GetNode<XRCamera3D>("../XROrigin3D/XRCamera3D");
|
|
_left = GetNode<XRController3D>("../XROrigin3D/LeftHand");
|
|
_right = GetNode<XRController3D>("../XROrigin3D/RightHand");
|
|
}
|
|
|
|
public override void _Process(double delta)
|
|
{
|
|
if (!Enabled)
|
|
{
|
|
_wasTwoHand = false;
|
|
_leftPrev = _left.Position;
|
|
_rightPrev = _right.Position;
|
|
return;
|
|
}
|
|
bool leftGrip = _left.GetFloat("grip") > GripThreshold && !PropGrabber.IsGrabbing(_left);
|
|
bool rightGrip = _right.GetFloat("grip") > GripThreshold && !PropGrabber.IsGrabbing(_right);
|
|
|
|
if (leftGrip && rightGrip && !WorldPanel.IsBlocking(_left) && !WorldPanel.IsBlocking(_right))
|
|
TwoHandGrab();
|
|
else
|
|
{
|
|
_wasTwoHand = false;
|
|
if (leftGrip && !WorldPanel.IsBlocking(_left))
|
|
GrabMove(_left, ref _leftPrev);
|
|
else
|
|
_leftPrev = _left.Position;
|
|
if (rightGrip && !WorldPanel.IsBlocking(_right))
|
|
GrabMove(_right, ref _rightPrev);
|
|
else
|
|
_rightPrev = _right.Position;
|
|
}
|
|
}
|
|
|
|
private void TwoHandGrab()
|
|
{
|
|
Vector3 mid = Midpoint();
|
|
float yaw = HandsYaw();
|
|
|
|
if (!_wasTwoHand)
|
|
{
|
|
_wasTwoHand = true;
|
|
_twoHandMidPrev = mid;
|
|
_twoHandYawPrev = yaw;
|
|
return;
|
|
}
|
|
|
|
Vector3 deltaWorld = _origin.GlobalTransform.Basis * (mid - _twoHandMidPrev);
|
|
float yawDelta = _twoHandYawPrev - yaw;
|
|
if (deltaWorld.LengthSquared() > 0.000001f || Mathf.Abs(yawDelta) > 0.000001f)
|
|
{
|
|
Vector3 hxz = new Vector3(_camera.GlobalPosition.X, 0f, _camera.GlobalPosition.Z);
|
|
Transform3D turn = new Transform3D(Basis.Identity, hxz)
|
|
* new Transform3D(Basis.FromEuler(new Vector3(0, yawDelta, 0)), Vector3.Zero)
|
|
* new Transform3D(Basis.Identity, -hxz);
|
|
_origin.GlobalTransform = new Transform3D(Basis.Identity, -deltaWorld) * turn * _origin.GlobalTransform;
|
|
}
|
|
_twoHandMidPrev = mid;
|
|
_twoHandYawPrev = yaw;
|
|
}
|
|
|
|
private void GrabMove(XRController3D controller, ref Vector3 prev)
|
|
{
|
|
Vector3 pos = controller.Position;
|
|
Vector3 delta = pos - prev;
|
|
prev = pos;
|
|
if (delta.LengthSquared() == 0f)
|
|
return;
|
|
Vector3 deltaWorld = _origin.GlobalTransform.Basis * delta;
|
|
_origin.GlobalTransform = new Transform3D(Basis.Identity, -deltaWorld) * _origin.GlobalTransform;
|
|
}
|
|
|
|
private Vector3 Midpoint() => (_left.Position + _right.Position) * 0.5f;
|
|
|
|
private float HandsYaw()
|
|
{
|
|
Vector3 v = _right.Position - _left.Position;
|
|
v.Y = 0f;
|
|
if (v.LengthSquared() < 0.0001f)
|
|
return _twoHandYawPrev;
|
|
return Mathf.Atan2(v.X, v.Z);
|
|
}
|
|
}
|