Drive proxy cadence from measured motion
Tie proxy locomotion/cadence to actual per-frame movement and motion-feedback instead of only replaying transmitted Speed. Changes: - docs/dev-log.md: add detailed dev log describing measured-cadence fix and cadence/animationSpeed work. - plugin/include/F4TProxyPuppet.h: added NiPoint3 include and new API: SetProxyLocomotionState, SetProxyDesiredSpeed (legacy alias), and ApplyProxyLocomotionFrame. - plugin/lib/commonlibf4/include/RE/A/ActorMotionFeedbackOutput.h: new tentative struct for motion-feedback output. - plugin/lib/commonlibf4/include/RE/Fallout.h: include new ActorMotionFeedbackOutput header. - plugin/src/F4TProxyActorController.cpp: track per-frame timing/puppet state, compute frame delta, use proxy->Move() with measured frameDelta and call ApplyProxyLocomotionFrame (instead of SetPosition/Lerp-only), and pass measured graph speed/direction to proxy. - plugin/src/F4TProxyAnimationSync.cpp: use SetProxyLocomotionState to include direction when writing proxy locomotion state. - plugin/src/F4TProxyPuppet.cpp: introduce ProxyLocomotionState map, sync controller motion fields, compute/push engine motion-feedback (via Compute/Update feedback vtable calls), use UpdateNoAI + manual motion feedback to drive cadence, implement ApplyProxyLocomotionFrame which derives velocity from world delta, clamps/chooses feedback speed (uses measured horizontal speed unless provided graph speed is valid), applies an idle threshold (<8 u/s -> idle), and writes graph speed back to the animation graph. Why: This makes foot cadence reflect on-screen translation (eliminating mild foot slide/stop-overstay) by measuring applied displacement each frame, updating controller velocity/motion-feedback, and re-asserting animation graph inputs after UpdateNoAI.
This commit is contained in:
@@ -4567,4 +4567,93 @@ Circle) decides: proxy should now jog at Speed 180 with a visibly faster cadence
|
||||
|
||||
### Build Status
|
||||
|
||||
✅ **SUCCESS** - `xmake build` clean; DLL deployed to game F4SE/Plugins.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-04 - Proxy Cadence WORKS (UpdateNoAI) + Glide Fix: Drive Cadence From Measured Translation
|
||||
|
||||
### Result
|
||||
|
||||
The `UpdateNoAI` puppet works in-game: proxies now show varying cadence including a
|
||||
real run. Confirmed by the user. Remaining issue: "a little glidy" (mild foot slide).
|
||||
|
||||
### Root Cause Of Residual Glide
|
||||
|
||||
`ApplySmoothFrameMovement()` moves the proxy by `SetPosition` with exponential Lerp
|
||||
easing (`Lerp(current, target, 0.25)` per frame). The leg cadence, however, was a
|
||||
constant *transmitted* graph Speed. Because the eased translation speed varies frame
|
||||
to frame (fast right after a packet, slowing as it converges) while the cadence is
|
||||
constant, the feet slide.
|
||||
|
||||
### Fix
|
||||
|
||||
Drive the puppet cadence from the proxy's ACTUAL horizontal translation each frame
|
||||
instead of the transmitted speed:
|
||||
|
||||
- In `ApplySmoothFrameMovement`, after computing `nextPosition`, measure
|
||||
`horizontalDistance(current, next) / frameDelta` and EMA-smooth it
|
||||
(`measuredGraphSpeed`, alpha 0.35).
|
||||
- Feed that to `F4T::ProxyPuppet::SetProxyDesiredSpeed` (units/sec ≈ graph Speed,
|
||||
~1:1). Below an 8 u/s idle threshold we publish -1 so the engine's idle stands.
|
||||
- `ApplySmoothFrameMovement` runs LAST in `ProxyActorController::Update`, after the
|
||||
per-player anim sync (which also sets a transmitted desired speed), so the measured
|
||||
value authoritatively wins for the puppet re-assert.
|
||||
|
||||
This ties foot cadence to real on-screen motion regardless of interpolation easing,
|
||||
which also makes the animation decelerate to idle naturally as the proxy converges on
|
||||
a stopped target (no stop-overstay).
|
||||
|
||||
### Build Status
|
||||
|
||||
✅ **SUCCESS** - `xmake build` clean; DLL deployed to game F4SE/Plugins.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-04 - Within-State Walk Cadence: middleProcess->animationSpeed Is The FO4 Speed-Warp Lever
|
||||
|
||||
### What The User Observed
|
||||
|
||||
After the measured-cadence build: only the RUN animation is distinct. Slow walk,
|
||||
medium walk, and jog all play the SAME walk clip. So `Speed` switches the locomotion
|
||||
STATE (walk<->run via a threshold) but does NOT vary the foot cadence WITHIN the walk
|
||||
state.
|
||||
|
||||
### Evidence
|
||||
|
||||
- Sender-side `Local send diag`: the local player's own graph `Speed` saturates into
|
||||
bands (walk ~60-160, run 373, sprint 500). `Speed` is a state selector, not a
|
||||
continuous cadence driver.
|
||||
- Writing `Speed`/`SpeedSampled` on the proxy (even persisting via the UpdateNoAI
|
||||
puppet) only flips walk<->run; sub-walk paces look identical.
|
||||
- `speedSampled` (lowercase) reads back false → not a real FO4 graph variable.
|
||||
|
||||
### TiltedEvolution Comparison
|
||||
|
||||
TE (Skyrim) pins position with `ForcePosition`, disables AI (`HookActorProcess`
|
||||
returns 0), and replays a curated graph-variable snapshot incl. `Speed`,
|
||||
`SpeedSampled`, `Direction`, `bMotionDriven`, `bIsSynced`. In Skyrim the leg blend is
|
||||
driven by the `SpeedSampled` graph variable, so pure variable replay reproduces
|
||||
cadence. FO4's player graph instead WARPS the clip from the actor's real velocity, so
|
||||
variable replay alone can't reproduce sub-state cadence on a SetPosition puppet.
|
||||
|
||||
### Fix: Drive middleProcess->animationSpeed / desiredSpeed
|
||||
|
||||
`MiddleHighProcessData` (CommonLibF4, reached via `actor->currentProcess->middleHigh`)
|
||||
exposes `float desiredSpeed (0x428)` and `float animationSpeed (0x42C)`. These are the
|
||||
engine's locomotion speed-warp inputs, normally derived from controller velocity (~0
|
||||
on a SetPosition puppet → fixed cadence). In `F4TProxyPuppet::WriteProxySpeed` (called
|
||||
around the UpdateNoAI puppet for proxy FormIDs) we now also write both fields to the
|
||||
proxy's measured ground speed (units/sec, same scale as graph Speed). This is the FO4
|
||||
analog of TE poking `middleProcess->direction`, and should make the legs cycle at the
|
||||
proxy's real pace within the walk state.
|
||||
|
||||
### Open Risk / Next Test
|
||||
|
||||
If `animationSpeed` is an engine OUTPUT (graph writes it) rather than an input, this
|
||||
won't change cadence and we move to the velocity-driven movement rework. Next test
|
||||
(slow walk vs medium vs jog) decides: sub-walk cadence should now visibly differ.
|
||||
|
||||
### Build Status
|
||||
|
||||
✅ **SUCCESS** - `xmake build` clean; DLL deployed to game F4SE/Plugins.
|
||||
Reference in New Issue
Block a user