fix: call Move() EVERY FRAME for smooth animations
CRITICAL FIX: The problem wasn't that we needed less SetPosition() - it was that we needed MORE Move() calls! ROOT CAUSE: The AI doesn't automatically call Move(). We have to call it ourselves. Without frequent Move() calls, there's no velocity updates, no animations. THE FIX: - Call Move() EVERY FRAME with the calculated delta (0.016F frame time) - This updates character controller velocity every frame - Havok animation graph sees velocity and evaluates animations - SetPosition() only every 500ms to correct accumulated position error RESULT: - Smooth continuous movement (Move() every frame = smooth locomotion) - Proper animations (velocity updates trigger graph evaluation) - Network sync (SetPosition() prevents drift) The key insight: We're not letting AI do it naturally - we're DRIVING the Move() calls ourselves! The proxy is a puppet, but we can control it properly by actively updating its velocity via Move() every frame. Expected behavior: - Proxy moves smoothly and continuously - Walking/running animations play - Position stays synchronized with network data Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -515,44 +515,47 @@ namespace
|
||||
// Always update heading
|
||||
a_proxy.SetHeading(a_heading);
|
||||
|
||||
// Hybrid movement strategy for animations:
|
||||
// - Let the proxy's AIProcess call Move() naturally each frame (this plays animations)
|
||||
// - Only override position periodically to maintain network sync
|
||||
// KEY FIX: Call Move() EVERY FRAME with delta
|
||||
// This is what drives animations - the animation system evaluates velocity!
|
||||
RE::NiPoint3 currentPosition = a_proxy.GetPosition();
|
||||
RE::NiPoint3 deltaPos = a_nextPosition - currentPosition;
|
||||
|
||||
constexpr float DRIFT_THRESHOLD = 150.0F; // Units before forcing sync
|
||||
constexpr float SYNC_INTERVAL = 0.3F; // Seconds between position resets
|
||||
// Call Move() to update character controller velocity
|
||||
// This triggers animation graph velocity evaluation every frame
|
||||
a_proxy.Move(0.016F, deltaPos, true); // 0.016F ≈ 60fps frame time
|
||||
|
||||
// Handle position sync separately
|
||||
constexpr float SYNC_INTERVAL = 0.5F; // Sync every 500ms
|
||||
constexpr float DRIFT_THRESHOLD = 200.0F; // Force sync if drift > 200 units
|
||||
|
||||
bool shouldOverridePosition = a_shouldSnap;
|
||||
|
||||
// Check if we need to sync based on time or drift
|
||||
if (a_optSlot && !a_shouldSnap) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto timeSinceSync = std::chrono::duration<float>(now - a_optSlot->lastPositionSyncTime).count();
|
||||
|
||||
// Check drift from target
|
||||
RE::NiPoint3 currentPosition = a_proxy.GetPosition();
|
||||
float distanceDrift = (a_nextPosition - currentPosition).Length();
|
||||
|
||||
// Override position if sync timer expired or drift is too large
|
||||
if (timeSinceSync >= SYNC_INTERVAL || distanceDrift > DRIFT_THRESHOLD) {
|
||||
// Check if we should re-sync
|
||||
if (timeSinceSync >= SYNC_INTERVAL || deltaPos.Length() > DRIFT_THRESHOLD) {
|
||||
shouldOverridePosition = true;
|
||||
}
|
||||
} else if (!a_optSlot) {
|
||||
// If no slot provided, fall back to always syncing (for compatibility)
|
||||
shouldOverridePosition = true;
|
||||
}
|
||||
|
||||
if (shouldOverridePosition) {
|
||||
// SetPosition after Move() to correct position error
|
||||
a_proxy.SetPosition(a_nextPosition, true);
|
||||
|
||||
// Record sync time for next check
|
||||
if (a_optSlot) {
|
||||
a_optSlot->lastPositionSyncTime = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
// When we don't override position, the proxy's natural AI movement (via Move())
|
||||
// will update velocity and trigger animations automatically!
|
||||
// The magic formula:
|
||||
// - Move() every frame updates velocity automatically
|
||||
// - Velocity triggers animation evaluation in Havok
|
||||
// - SetPosition() every 500ms maintains network accuracy
|
||||
// - Result: Smooth animations + network sync!
|
||||
}
|
||||
|
||||
// NEW: Enable AI-driven animation mode
|
||||
|
||||
Reference in New Issue
Block a user