Quick answer: With Async Physics enabled, the physics thread runs ahead of the game thread. Read results in the async callback or via the marshalled game-thread snapshot, not by direct query mid-frame.
A vehicle game enables Async Physics for stability. Game-thread code reading wheel forces gets stale or jittery values — the physics thread is at a different sim step.
Why Desync Happens
Async Physics runs Chaos on its own thread at a fixed step, decoupled from the variable game-thread frame rate. A direct GetComponentVelocity() mid-frame reads whatever the physics thread last marshalled — possibly an interpolated or stale value.
Use AsyncPhysicsTickComponent
void UMyVehicleComponent::AsyncPhysicsTickComponent(float DeltaTime, float SimTime)
{
// runs ON the physics thread, in sync with the sim
FVector Velocity = GetPhysicsLinearVelocity();
ApplyControlInput(Velocity);
}
Logic that must be in lockstep with physics goes here. It executes on the physics thread at the sim step.
Marshal Results to Game Thread
For game-thread code (UI, gameplay), read the marshalled snapshot. Chaos provides interpolated game-thread transforms; use those for rendering/UI, not for physics decisions.
Input Marshalling
Player input is game-thread. Marshal it into the physics sim via FAsyncPhysicsTimestamp-tagged input structs so the physics thread applies it at the right step. Don’t poke physics bodies directly from Tick.
Verifying
Vehicle handling is smooth and deterministic. Wheel force reads in AsyncPhysicsTickComponent are consistent. No jitter from cross-thread stale reads.
“Async physics is a separate timeline. Read and write through the async callback, not directly from Tick.”
For networked physics games, async physics + input marshalling is also the foundation of deterministic rollback — worth the architecture investment.