Jump: hold right A to charge (legs bend), release to jump
Tap hops ~0.16 m, full charge ~0.68 m, a real crouch charges too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
537e72c207
commit
0bddcf4517
3 files changed
+52
-49
No files matched your search
+32
-27
@@ -64,15 +64,16 @@ public partial class PlayerRagdoll : Node3D
|
|||||||
// ~0.3 m from 1 m, legs fully folded from a big fall (Boneworks) — then the normal rise.
|
// ~0.3 m from 1 m, legs fully folded from a big fall (Boneworks) — then the normal rise.
|
||||||
public const float LandingReach = 0.05f; // m past the pose's hip height
|
public const float LandingReach = 0.05f; // m past the pose's hip height
|
||||||
public const float AbsorbTime = 0.4f; // s after a hard touchdown the head guide stays off (it would hold the dip up)
|
public const float AbsorbTime = 0.4f; // s after a hard touchdown the head guide stays off (it would hold the dip up)
|
||||||
// Jump (right A): a short countermovement dip, then the legs push JumpForce over the stroke up to
|
// Jump (right A, Boneworks-style hold to charge, release to jump): holding bends the legs — the
|
||||||
// full extension. Takeoff speed is physics — v² = 2·(F/m − g)·stroke — so a crouch before
|
// support goal sinks at ChargeSpeed down to JumpMaxStroke below full extension — and releasing
|
||||||
// the press (real or stick) jumps higher: ~0.45 m standing, ~0.7 m from a deep crouch.
|
// pushes JumpForce over that stroke up to extension. Takeoff speed is physics,
|
||||||
public const float JumpForce = 2.4f; // × body weight
|
// v² = 2·(F/m − g)·stroke: a tap hops, a full charge (or a real/stick crouch) jumps ~0.7 m.
|
||||||
public const float JumpDip = 0.2f; // m the hips drop first when pressed from standing
|
public const float JumpForce = 2.2f; // × body weight
|
||||||
|
public const float ChargeSpeed = 1.2f; // m/s the hips sink while A is held
|
||||||
public const float JumpExtension = 0.06f; // m above the standing hips at toe-off
|
public const float JumpExtension = 0.06f; // m above the standing hips at toe-off
|
||||||
public const float JumpMaxStroke = 0.5f;
|
public const float JumpMaxStroke = 0.5f;
|
||||||
public const float JumpBuffer = 0.15f; // s a press is remembered before touchdown
|
public const float JumpMinStroke = 0.08f; // a tap still hops
|
||||||
public const float CoyoteTime = 0.12f; // s after leaving the ground a jump still works
|
public const float CoyoteTime = 0.12f; // s after leaving the ground a release still jumps
|
||||||
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 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 BracedAirControl = 150f; // N of horizontal hip carry while airborne but braced on the hands (mantling)
|
||||||
public const float CrawlCarry = 300f;
|
public const float CrawlCarry = 300f;
|
||||||
@@ -197,12 +198,13 @@ public partial class PlayerRagdoll : Node3D
|
|||||||
return GetWorld3D().DirectSpaceState.IntersectShape(_legsQuery, 1).Count == 0;
|
return GetWorld3D().DirectSpaceState.IntersectShape(_legsQuery, 1).Count == 0;
|
||||||
}
|
}
|
||||||
private Vector3 _goalVelocity;
|
private Vector3 _goalVelocity;
|
||||||
private enum JumpPhase { None, Dip, Push, Flight }
|
private enum JumpPhase { None, Charge, Push, Flight }
|
||||||
private JumpPhase _jump;
|
private JumpPhase _jump;
|
||||||
private float _jumpT, _jumpRequest, _airT, _jumpFromY, _sinceLanding = float.MaxValue;
|
private float _jumpT, _airT, _jumpFromY, _sinceLanding = float.MaxValue;
|
||||||
|
private bool _jumpHeld;
|
||||||
|
|
||||||
// Right A. Buffered, so a press just before touchdown still jumps.
|
// Right A, every tick. Holding while landing starts the charge on touchdown.
|
||||||
public void Jump() => _jumpRequest = JumpBuffer;
|
public bool JumpHeld { set => _jumpHeld = value; }
|
||||||
|
|
||||||
private float _standingHip; // hips of the standing pose above the feet = the legs' full extension
|
private float _standingHip; // hips of the standing pose above the feet = the legs' full extension
|
||||||
|
|
||||||
@@ -349,7 +351,7 @@ public partial class PlayerRagdoll : Node3D
|
|||||||
_arms[i] = new Arm();
|
_arms[i] = new Arm();
|
||||||
Built = false;
|
Built = false;
|
||||||
_jump = JumpPhase.None;
|
_jump = JumpPhase.None;
|
||||||
_jumpRequest = 0f;
|
_jumpHeld = false;
|
||||||
_sinceLanding = float.MaxValue;
|
_sinceLanding = float.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -931,7 +933,7 @@ public partial class PlayerRagdoll : Node3D
|
|||||||
// tiptoes, a reach up — would otherwise float the body off the ground).
|
// tiptoes, a reach up — would otherwise float the body off the ground).
|
||||||
goal.Y = Mathf.Min(goal.Y, _footY + _standingHip + JumpExtension);
|
goal.Y = Mathf.Min(goal.Y, _footY + _standingHip + JumpExtension);
|
||||||
_supportGoalY = goal.Y;
|
_supportGoalY = goal.Y;
|
||||||
if (_jump == JumpPhase.Dip)
|
if (_jump == JumpPhase.Charge)
|
||||||
goal.Y = Mathf.Min(goal.Y, _jumpFromY);
|
goal.Y = Mathf.Min(goal.Y, _jumpFromY);
|
||||||
float w = Mathf.Tau * GroundFrequency;
|
float w = Mathf.Tau * GroundFrequency;
|
||||||
Vector3 accel = (goal - pos) * (w * w) + (targetVelocity - Pelvis.LinearVelocity) * (2f * GroundDamping * w);
|
Vector3 accel = (goal - pos) * (w * w) + (targetVelocity - Pelvis.LinearVelocity) * (2f * GroundDamping * w);
|
||||||
@@ -984,35 +986,38 @@ public partial class PlayerRagdoll : Node3D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jump state: request → Dip (countermovement, the support goal drops) → Push (full leg force up to
|
// Jump state: held → Charge (the support goal sinks: the legs bend) → released → Push (full leg
|
||||||
// extension) → Flight (no ground until falling again) → None on touchdown.
|
// force up to extension) → Flight (no ground until falling again) → None on touchdown.
|
||||||
private void UpdateJump(float dt, bool wasGrounded)
|
private void UpdateJump(float dt, bool wasGrounded)
|
||||||
{
|
{
|
||||||
_airT = Grounded ? 0f : _airT + dt;
|
_airT = Grounded ? 0f : _airT + dt;
|
||||||
_jumpRequest -= dt;
|
|
||||||
_jumpT += dt;
|
_jumpT += dt;
|
||||||
float pos = Pelvis.GlobalPosition.Y;
|
float pos = Pelvis.GlobalPosition.Y;
|
||||||
float top = _footY + _standingHip + JumpExtension;
|
float top = _footY + _standingHip + JumpExtension;
|
||||||
switch (_jump)
|
switch (_jump)
|
||||||
{
|
{
|
||||||
case JumpPhase.None:
|
case JumpPhase.None:
|
||||||
if (_jumpRequest > 0f && (Grounded || _airT < CoyoteTime) && !Climbing)
|
if (_jumpHeld && Grounded && !Climbing)
|
||||||
{
|
{
|
||||||
_jumpRequest = 0f;
|
_jump = JumpPhase.Charge;
|
||||||
_jumpT = 0f;
|
_jumpT = 0f;
|
||||||
// Stroke at least JumpDip + JumpExtension: from standing, dip first.
|
_jumpFromY = Mathf.Min(pos, top);
|
||||||
_jumpFromY = Mathf.Max(top - JumpMaxStroke, Mathf.Min(pos, top - JumpDip - JumpExtension));
|
|
||||||
_jump = pos > _jumpFromY + 0.02f && Grounded ? JumpPhase.Dip : JumpPhase.Push;
|
|
||||||
_jumpFromY = Mathf.Min(_jumpFromY, pos);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JumpPhase.Dip:
|
case JumpPhase.Charge:
|
||||||
if (pos <= _jumpFromY + 0.02f || _jumpT > 0.2f || !Grounded)
|
if (!Grounded && _airT > CoyoteTime || Climbing)
|
||||||
{
|
{
|
||||||
_jump = JumpPhase.Push;
|
_jump = JumpPhase.None; // walked off or grabbed something while charging
|
||||||
_jumpFromY = pos;
|
break;
|
||||||
_jumpT = 0f;
|
|
||||||
}
|
}
|
||||||
|
if (_jumpHeld)
|
||||||
|
{
|
||||||
|
_jumpFromY = Mathf.Max(top - JumpMaxStroke, _jumpFromY - ChargeSpeed * dt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_jump = JumpPhase.Push;
|
||||||
|
_jumpFromY = Mathf.Min(pos, top - JumpMinStroke);
|
||||||
|
_jumpT = 0f;
|
||||||
break;
|
break;
|
||||||
case JumpPhase.Push:
|
case JumpPhase.Push:
|
||||||
if (pos >= top || pos - _jumpFromY >= JumpMaxStroke || _jumpT > 0.35f)
|
if (pos >= top || pos - _jumpFromY >= JumpMaxStroke || _jumpT > 0.35f)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using Godot;
|
|||||||
// physics tick, in this order:
|
// physics tick, in this order:
|
||||||
// 1. discrete origin jumps made by other code (spawn, teleport markers) carry the body along;
|
// 1. discrete origin jumps made by other code (spawn, teleport markers) carry the body along;
|
||||||
// 2. stick: left = move (head-relative), right X = turn (snap by default, SmoothTurn for smooth),
|
// 2. stick: left = move (head-relative), right X = turn (snap by default, SmoothTurn for smooth),
|
||||||
// right Y = crouch (down lowers, up raises — RagdollDriver.StickCrouch); right A = jump;
|
// right Y = crouch (down lowers, up raises — RagdollDriver.StickCrouch); right A = hold to charge, release to jump;
|
||||||
// 3. the ragdoll's OriginCorrection (push-back at walls, floor follows the ground, falls);
|
// 3. the ragdoll's OriginCorrection (push-back at walls, floor follows the ground, falls);
|
||||||
// 4. True Body solve from the corrected camera/controllers, then the ragdoll's muscles.
|
// 4. True Body solve from the corrected camera/controllers, then the ragdoll's muscles.
|
||||||
// Waits for the calibrated BodyProfile like everything else; rebuilds on recalibration.
|
// Waits for the calibrated BodyProfile like everything else; rebuilds on recalibration.
|
||||||
@@ -36,7 +36,6 @@ public partial class PlayerRig : Node
|
|||||||
private bool _haveLastOrigin;
|
private bool _haveLastOrigin;
|
||||||
private Vector3 _moveVelocity;
|
private Vector3 _moveVelocity;
|
||||||
private bool _snapArmed = true;
|
private bool _snapArmed = true;
|
||||||
private bool _jumpPrev;
|
|
||||||
private readonly MeshInstance3D[] _ghosts = new MeshInstance3D[2];
|
private readonly MeshInstance3D[] _ghosts = new MeshInstance3D[2];
|
||||||
private bool _rebuild = true;
|
private bool _rebuild = true;
|
||||||
private RagdollDriver _driver;
|
private RagdollDriver _driver;
|
||||||
@@ -231,11 +230,8 @@ public partial class PlayerRig : Node
|
|||||||
if (_moveVelocity != Vector3.Zero)
|
if (_moveVelocity != Vector3.Zero)
|
||||||
_origin.GlobalPosition += _moveVelocity * dt;
|
_origin.GlobalPosition += _moveVelocity * dt;
|
||||||
|
|
||||||
// Jump: right A, on the press.
|
// Jump: hold right A to charge (the legs bend), release to jump.
|
||||||
bool jump = _right.IsButtonPressed("ax_button");
|
_ragdoll.JumpHeld = _right.IsButtonPressed("ax_button");
|
||||||
if (jump && !_jumpPrev)
|
|
||||||
_ragdoll.Jump();
|
|
||||||
_jumpPrev = jump;
|
|
||||||
|
|
||||||
// Crouch: right stick held down lowers the body, up raises it back (the stick's dominant
|
// Crouch: right stick held down lowers the body, up raises it back (the stick's dominant
|
||||||
// axis decides between crouch and turn).
|
// axis decides between crouch and turn).
|
||||||
|
|||||||
+17
-15
@@ -114,8 +114,9 @@ public partial class RagdollSpike : Node3D
|
|||||||
_phases.Add(("climbs a wall hand over hand (grab, pull, regrab higher)", SetupClimbWall, TickClimbWall, 8f, FinishClimbWall));
|
_phases.Add(("climbs a wall hand over hand (grab, pull, regrab higher)", SetupClimbWall, TickClimbWall, 8f, FinishClimbWall));
|
||||||
_phases.Add(("walks off a 1 m ledge and lands: a dip the view follows, no bounce", () => SetupLedgeTop(1f), TickDrop, 4f, () => FinishDrop(1f)));
|
_phases.Add(("walks off a 1 m ledge and lands: a dip the view follows, no bounce", () => SetupLedgeTop(1f), TickDrop, 4f, () => FinishDrop(1f)));
|
||||||
_phases.Add(("big fall (5 m): legs fold fully, then it recovers", () => SetupLedgeTop(5f), TickDrop, 5f, () => FinishDrop(5f)));
|
_phases.Add(("big fall (5 m): legs fold fully, then it recovers", () => SetupLedgeTop(5f), TickDrop, 5f, () => FinishDrop(5f)));
|
||||||
_phases.Add(("standing jump", Rest, t => TickJump(0f), 4f, () => FinishJump(crouched: false)));
|
_phases.Add(("jump: a tap of A hops", Rest, t => TickJump(0f, 0.05f), 4f, () => FinishJump(0)));
|
||||||
_phases.Add(("jump from a crouch goes higher", Rest, t => TickJump(0.45f), 4.5f, () => FinishJump(crouched: true)));
|
_phases.Add(("jump: holding A to charge jumps much higher", Rest, t => TickJump(0f, 0.6f), 4.5f, () => FinishJump(1)));
|
||||||
|
_phases.Add(("jump: a tap from a real crouch beats a standing tap", Rest, t => TickJump(0.45f, 0.05f), 4.5f, () => FinishJump(2)));
|
||||||
_phases.Add(("looks down past vertical while crouched (body doesn't flip around)", Rest, TickLookDown, 5f, FinishLookDown));
|
_phases.Add(("looks down past vertical while crouched (body doesn't flip around)", Rest, TickLookDown, 5f, FinishLookDown));
|
||||||
_phases.Add(("stick crouch lowers the body and the view, and stands back up", Rest, TickStickCrouch, 6f, FinishStickCrouch));
|
_phases.Add(("stick crouch lowers the body and the view, and stands back up", Rest, TickStickCrouch, 6f, FinishStickCrouch));
|
||||||
_phases.Add(("standing on a seesaw tips it (body weight reaches movable ground)", SetupSeesaw, _ => { }, 3f, FinishSeesaw));
|
_phases.Add(("standing on a seesaw tips it (body weight reaches movable ground)", SetupSeesaw, _ => { }, 3f, FinishSeesaw));
|
||||||
@@ -783,11 +784,11 @@ public partial class RagdollSpike : Node3D
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- jump
|
// ---------------------------------------------------------------- jump
|
||||||
|
|
||||||
private float _jumpStand, _jumpApex, _jumpEyeErr, _standJumpApex;
|
private float _jumpStand, _jumpApex, _jumpEyeErr, _tapApex;
|
||||||
private bool _jumpPressed;
|
private bool _jumpPressed;
|
||||||
|
|
||||||
// Optionally squat (eyes `crouch` lower) first, then press jump at 2 s; the player keeps the pose.
|
// Optionally squat (eyes `crouch` lower) first, then hold A from 2 s for `hold` s; the player keeps the pose.
|
||||||
private void TickJump(float crouch)
|
private void TickJump(float crouch, float hold)
|
||||||
{
|
{
|
||||||
float k = Mathf.SmoothStep(0f, 1f, Mathf.Clamp((_t - 0.8f) / 0.8f, 0f, 1f)) * (crouch > 0f ? 1f : 0f);
|
float k = Mathf.SmoothStep(0f, 1f, Mathf.Clamp((_t - 0.8f) / 0.8f, 0f, 1f)) * (crouch > 0f ? 1f : 0f);
|
||||||
_headLocal = new Transform3D(new Basis(Vector3.Right, -0.2f * k), new Vector3(0f, EyeHeight - crouch * k, -0.05f * k));
|
_headLocal = new Transform3D(new Basis(Vector3.Right, -0.2f * k), new Vector3(0f, EyeHeight - crouch * k, -0.05f * k));
|
||||||
@@ -804,11 +805,9 @@ public partial class RagdollSpike : Node3D
|
|||||||
_jumpEyeErr = 0f;
|
_jumpEyeErr = 0f;
|
||||||
_jumpPressed = false;
|
_jumpPressed = false;
|
||||||
}
|
}
|
||||||
if (_t >= 2f && !_jumpPressed)
|
_ragdoll.JumpHeld = _t >= 2f && _t < 2f + hold;
|
||||||
{
|
if (_t >= 2f)
|
||||||
_jumpPressed = true;
|
_jumpPressed = true;
|
||||||
_ragdoll.Jump();
|
|
||||||
}
|
|
||||||
if (_jumpPressed)
|
if (_jumpPressed)
|
||||||
{
|
{
|
||||||
_jumpApex = Mathf.Max(_jumpApex, pelvis - _jumpStand);
|
_jumpApex = Mathf.Max(_jumpApex, pelvis - _jumpStand);
|
||||||
@@ -816,16 +815,19 @@ public partial class RagdollSpike : Node3D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool FinishJump(bool crouched)
|
private bool FinishJump(int kind)
|
||||||
{
|
{
|
||||||
GD.Print($"RAGDOLL: {(crouched ? "crouched" : "standing")} jump: hips rose {_jumpApex:F3} m above the standing stance, eye vs camera max {_jumpEyeErr:F3}, grounded {_ragdoll.Grounded}, jumping {_ragdoll.Jumping}, snaps {NewSnaps}");
|
string what = kind == 0 ? "standing tap" : kind == 1 ? "full charge" : "tap from a crouch";
|
||||||
|
GD.Print($"RAGDOLL: {what}: hips rose {_jumpApex:F3} m above the standing stance, eye vs camera max {_jumpEyeErr:F3}, grounded {_ragdoll.Grounded}, jumping {_ragdoll.Jumping}, snaps {NewSnaps}");
|
||||||
bool ok = _finite && NewSnaps == 0 && _ragdoll.Grounded && !_ragdoll.Jumping && _jumpEyeErr < 0.12f && _origin.Origin.Length() < 0.15f;
|
bool ok = _finite && NewSnaps == 0 && _ragdoll.Grounded && !_ragdoll.Jumping && _jumpEyeErr < 0.12f && _origin.Origin.Length() < 0.15f;
|
||||||
if (!crouched)
|
if (kind == 0)
|
||||||
{
|
{
|
||||||
_standJumpApex = _jumpApex;
|
_tapApex = _jumpApex;
|
||||||
return ok && _jumpApex > 0.3f && _jumpApex < 0.65f;
|
return ok && _jumpApex > 0.03f && _jumpApex < 0.35f;
|
||||||
}
|
}
|
||||||
return ok && _jumpApex > _standJumpApex + 0.1f;
|
if (kind == 1)
|
||||||
|
return ok && _jumpApex > _tapApex + 0.25f && _jumpApex < 0.9f;
|
||||||
|
return ok && _jumpApex > _tapApex + 0.1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------- looking down past vertical
|
// ---------------------------------------------------------------- looking down past vertical
|
||||||
|
|||||||
Reference in new issue
Block a user