MP: replicant gait animation -- peer mechs walk instead of sliding (task #50)
A peer's replicant mech slid around as a frozen statue: DeadReckon moved localOrigin but nothing animated the legs. Reconstruct the replicated-motion display loop: after DeadReckon, derive a SIGNED speed demand from the replicated velocity (the master publishes worldLinearVelocity in every update record, Mover::WriteUpdateRecord MOVER.cpp:759; the replicant stores it in updateVelocity.linearMotion :712; sign = forward dot against the -Z facing), write it to controlsMapper->speedDemand (the LEG channel's live demand source), and advance the LEG state machine for its JOINT writes only -- travel stays with DeadReckon, so position always follows the master and the clip cadence matches the replicated speed. Two replicant-only initialization holes found live (the master's equivalents are primed per-frame in the PLAYER-only drive block): - globalTimeScale/idleStrideScale read 0 -> clip advance time dt*0, the leg machine engaged (state 11) but the clip froze (legCycle stuck 0); - reverseSpeedMax2 (the run-cycle rise-clamp @0x7a0; LoadLocomotionClips does NOT set it) read debug-heap garbage -> legCycleSpeed clamped to -4.3e8 on entering run state 13. Primed both; legCycleSpeed sane-banded once. VERIFIED 2-node (BT_GOTO driver + BT_REPL_LOG observer): the replicant runs the full gait lifecycle -- stand -> walk(5,7) -> run(10,12), legCycle 7.5->34 tracking the master's speed, wind-down(8), Standing(0) when the master stops. Solo un-regressed (goto->aim->kill chain intact, no crash). projectedVelocity source-swap (the binary publishes the BODY channel's smoothed projection instead of the actual travel velocity) evaluated and DEFERRED: it risks a verified working replication feed for a smoothing nuance; recorded in multiplayer.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
47aede3094
commit
d09cda6c36
@@ -1194,6 +1194,51 @@ void
|
||||
{
|
||||
DeadReckon(dt); // engine: reckoner + lerp
|
||||
localToWorld = localOrigin;
|
||||
|
||||
// REPLICANT GAIT (task #50): animate the peer mech's legs at the
|
||||
// REPLICATED speed -- without this the replicant slides around as a
|
||||
// frozen statue. The master publishes worldLinearVelocity in every
|
||||
// update record (Mover::WriteUpdateRecord, MOVER.cpp:759); the
|
||||
// replicant stores it in updateVelocity.linearMotion (:712). Derive
|
||||
// the signed speed demand from it (forward = local -Z; a negative
|
||||
// forward dot = the master is reversing), feed the LEG channel's live
|
||||
// demand source, and advance the leg state machine for its JOINT
|
||||
// writes only -- TRAVEL stays with DeadReckon (the returned distance
|
||||
// is discarded), so position always follows the master and the clip
|
||||
// cadence matches the replicated speed (residual foot-slip is the
|
||||
// inherent dead-reckoning artifact). The state machine self-arms
|
||||
// stand->walk / winds down from the demand exactly as on the master.
|
||||
if (!IsMechDestroyed() && controlsMapper != 0)
|
||||
{
|
||||
const Vector3D &wv = updateVelocity.linearMotion;
|
||||
float spd = sqrtf((float)(wv.x * wv.x + wv.z * wv.z));
|
||||
UnitVector zAxR;
|
||||
localToWorld.GetFromAxis(Z_Axis, &zAxR);
|
||||
const float fdot = -((float)wv.x * (float)zAxR.x
|
||||
+ (float)wv.z * (float)zAxR.z); // mech faces -Z
|
||||
controlsMapper->speedDemand = (fdot < 0.0f) ? -spd : spd;
|
||||
// Prime the same clip-advance scalars the master's gait block sets
|
||||
// each frame -- uninitialized on a replicant they read 0, freezing
|
||||
// the clip at advance-time dt*0 (observed: legState engaged at 11,
|
||||
// legCycle stuck 0). Same forwardCycleRate floor as the master
|
||||
// (the model-record decode reads ~1 u/s^2 -- a 55s ramp; floor 25).
|
||||
globalTimeScale = 1.0f;
|
||||
idleStrideScale = 1.0f;
|
||||
if (forwardCycleRate < 25.0f) forwardCycleRate = 25.0f;
|
||||
// reverseSpeedMax2@0x7a0: the run-cycle rise-CLAMP (leg case 12/13);
|
||||
// LoadLocomotionClips doesn't set it, so a replicant reads 0xCDCD
|
||||
// (-4.3e8) and the clamp CLOBBERS legCycleSpeed the moment the run
|
||||
// cycle engages (observed live: legCycle=-4.31602e+08, state 13
|
||||
// stuck). Same heal as the master's drive block.
|
||||
reverseSpeedMax2 = reverseStrideLength;
|
||||
// legCycleSpeed itself is also debug-heap garbage until a walk
|
||||
// state first writes it (Standing never touches it); sane-band it
|
||||
// once so a direct run-state entry can't slew from -4.3e8.
|
||||
if (legCycleSpeed < -100.0f || legCycleSpeed > 200.0f)
|
||||
legCycleSpeed = 0.0f;
|
||||
(void)AdvanceLegAnimation(dt); // joints only; travel = DeadReckon
|
||||
}
|
||||
|
||||
if (getenv("BT_REPL_LOG"))
|
||||
{
|
||||
static float s_replLog = 0.0f; s_replLog += dt;
|
||||
@@ -1203,7 +1248,11 @@ void
|
||||
DEBUG_STREAM << "[repl] mech " << (long)GetEntityID()
|
||||
<< " pos=(" << localOrigin.linearPosition.x << ", "
|
||||
<< localOrigin.linearPosition.y << ", "
|
||||
<< localOrigin.linearPosition.z << ")\n" << std::flush;
|
||||
<< localOrigin.linearPosition.z << ")"
|
||||
<< " vel=(" << updateVelocity.linearMotion.x << ","
|
||||
<< updateVelocity.linearMotion.z << ")"
|
||||
<< " legState=" << (int)legStateAlarm.GetLevel()
|
||||
<< " legCycle=" << legCycleSpeed << "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user