I’m trying to create a client-side prediction and reconciliation system for networked physics (I don’t want to use Netcode for Entities, for various reasons), this involves overriding the states of all my physics entities on the client to whatever I’m receiving from the server (which will be x cycles behind, where x is the latency), and then manually invoking the physics simulation x times on the client, to catch back up with the correct time. Client-side prediction and state overriding is all working fine, but I’m struggling with the reconciliation, purely because I can’t figure out how to actually get physics to simulate multiple times in one frame.
With regular Monobehaviour and PhysX, this is as simple as Physics.Simulate(deltaTime);, but I can’t for the life of me figure this out with ECS and UnityPhysics.
I understand that all the physics systems execute within FixedStepSimulationSystemGroup, so I’ve tried
var physicsSystem = Unity.Entities.World.DefaultGameObjectInjectionWorld.GetExistingSystem<FixedStepSimulationSystemGroup>();
for (int i = 0; i < 2000; i++)
{
Debug.Log("Sim phys " + i);
physicsSystem.Update(Unity.Entities.World.DefaultGameObjectInjectionWorld.Unmanaged);
}
This certainly hitches the framerate, but I would expect to see all my physics object “jump forward”, and this doesn’t happen, the game pauses for a fraction of a second, and then continues as if no additional physics simulations occurred.
What’s the correct way to do this? Manually invoking the physics simulation doesn’t feel like that obscure a use case, and yet (as with much of ECS) I’m struggling to find docs on this)
For context, I’m using ECS because I want the determinism offered by UnityPhysics, performance is nice, but that’s less so why I’m here
FixedStepSimulationSystemGroup by default uses a rate manager that determines whether or not to actually run the systems it holds, based on the time known to the world. The rate manager in turn sets some state that is only visible to the systems that run in that group, such as updated double rewindable allocators (SystemState.WorldUpdateAllocator) and time that has been ticked from the previous execution of the group, so times inside the group for a time step of 0.05 would be 0, 0.05, 0.10, … The specific default rate manager used by FSSSG uses catch-up behaviour, so if you start from world time 0 and have a delta time of 0.13 for a final elapsed time of 0.13, your fixed step group would run 2 times: once from 0.0 to 0.05, and once more for 0.05 to 0.10. Next frame, if you have a delta time of 0.04 for a final elapsed time of 0.17, your fixed step group would run once for 0.10 to 0.15.
Basically, running FSSSG in a loop would not do anything after the first call, since the world time outside it did not change.
Physics should solely care about the delta time and not consider the actual time for anything, so you might be able to just run the PhysicsSystemGroup by itself instead. Be sure to push / pop the correct fixed delta time value so the simulation steps correctly, otherwise you would be using the actual game’s dynamic frame delta time.
I find that surprising, when using UnityPhysics across different clients, the physics simulation isn’t diverging across clients like it does with PhysX… perhaps it’s just less non-determinstic than PhysX?
E.G, let’s say I have a pile of basketballs…
PhysX
The pile will be stable on the server, but collapse on the client
UnityPhysics
The pile will be stable on both, or collapse on both
For running Unity Physics multiple times per frame, have a look at the predictive physics simulation done in the Pool demo in the PhysicsSamples project.