Body step 2.4: landing, movable ground, playground fixes

- legs brake landings at 4.5 g; vertical support is a rate-limited servo
  (no launch when stepping onto a mantled ledge)
- ground control pushes back on movable ground (seesaw tips under the player)
- playground hinges use absolute paths (joint paths resolve from the joint)
- big props grabbable via surface query; Reset props keeps playground pieces
- spike: landing, seesaw, playground joints/grab, person-like wall climb

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
FoxyzandClaude Opus 5.5 committed 2026-10-02 10:10:38 +03:00
1 parent b5f598aae9
commit 0b8f2131c1
6 files changed
+208 -42

No files matched your search

+17 -2
View File
@@ -52,10 +52,11 @@ public partial class PlayerRagdoll : Node3D
// Ground control (world-space support on the pelvis, only while grounded).
public const float GroundFrequency = 4f; // Hz
public const float GroundDamping = 1f;
public const float LegStrength = 2f; // max support, × body weight
public const float LegStrength = 4.5f; // max support, × body weight (real legs brake a landing at 3–5 g; 2× sank ~0.4 m)
public const float GroundFriction = 1f; // horizontal force ≤ this × support
public const float GroundProbeExtra = 0.3f; // ground counts while within hip height + this
public const float MaxStepHeight = 0.35f;
public const float MaxRiseSpeed = 1.2f; // m/s the legs lift the body toward its stance
public const float MaxSlopeDegrees = 35f; // steeper ground gives no support (Nimso: "slopes beyond an angle can't be run up") // higher ground under the hips isn't stood on unless the body got up there
public const float BracedAirControl = 150f; // N of horizontal hip carry while airborne but braced on the hands (mantling)
public const float CrawlCarry = 300f;
@@ -166,6 +167,8 @@ public partial class PlayerRagdoll : Node3D
private readonly Vector3[] _girdle = new Vector3[2]; // True Body shoulder offset, chest frame
private float _footY;
private float _supportGoalY;
private RigidBody3D _groundBody;
private Vector3 _groundPoint;
private float _tuckFree, _dt;
private PhysicsShapeQueryParameters3D _legsQuery;
@@ -752,10 +755,13 @@ public partial class PlayerRagdoll : Node3D
Godot.Collections.Dictionary hit = GetWorld3D().DirectSpaceState.IntersectRay(query);
bool wasGrounded = Grounded;
Grounded = false;
_groundBody = null;
if (hit.Count > 0)
{
Vector3 point = (Vector3)hit["position"];
Vector3 normal = (Vector3)hit["normal"];
_groundBody = GodotObject.InstanceFromId((ulong)hit["collider_id"]) as RigidBody3D; // Box3D: "collider" is null
_groundPoint = point;
bool inReach = pos.Y - point.Y < hipAboveFloor + GroundProbeExtra;
bool steep = normal.Y <= Mathf.Cos(Mathf.DegToRad(MaxSlopeDegrees));
if (inReach && steep && wasGrounded && point.Y <= _footY + MaxStepHeight)
@@ -806,7 +812,12 @@ public partial class PlayerRagdoll : Node3D
float w = Mathf.Tau * GroundFrequency;
Vector3 accel = (goal - pos) * (w * w) + (targetVelocity - Pelvis.LinearVelocity) * (2f * GroundDamping * w);
Vector3 force = accel * BodyMass;
float support = Mathf.Clamp(force.Y + weight, 0f, LegStrength * weight);
// Vertical as a rate-limited velocity servo: full leg strength brakes a landing, but
// the legs raise the body at most MaxRiseSpeed — they can push, not pull, so a hard push
// toward a higher stance (onto a ledge) would otherwise overshoot into a jump.
float riseVelocity = Mathf.Clamp((goal.Y - pos.Y) * w, -3f, MaxRiseSpeed) + targetVelocity.Y;
float verticalAccel = (riseVelocity - Pelvis.LinearVelocity.Y) * (2f * GroundDamping * w);
float support = Mathf.Clamp(verticalAccel * BodyMass + weight, 0f, LegStrength * weight);
float carry = Mathf.Max(GroundFriction * support, _legsSolid ? 0f : CrawlCarry);
// Hips held back by an obstacle: lean on it, don't bulldoze — full leg force tipped the
// body over knee-high boxes and steep slopes (and the riding view tipped with it).
@@ -814,6 +825,10 @@ public partial class PlayerRagdoll : Node3D
carry = Mathf.Min(carry, BlockedPush);
Vector3 horizontal = new Vector3(force.X, 0f, force.Z).LimitLength(carry);
Pelvis.ApplyCentralForce(horizontal + Vector3.Up * support);
// Newton's third law: standing on something movable (seesaw, plank, crate) pushes it
// down — the feet aren't physical, so the reaction is applied where the ground ray hit.
if (_groundBody != null && IsInstanceValid(_groundBody))
_groundBody.ApplyForce(-(horizontal + Vector3.Up * support), _groundPoint - _groundBody.GlobalPosition);
}
else if (Braced)
{