Is 2D Physics in Unity deterministic?
Generally speaking, 2D physics in Unity can be deterministic on the same machine, but not across different machines.
Unity uses the Box2D physics engine to simulate 2D physics. Box2D does not use any random numbers, and does not base any computation on random events (such as timers). For the same input and the same binary, Box2D reproduces any simulation, which could have different results each time depending on the platform.
However, for your application, you might want strict determinism. As such, you would need to know whether Box2D can produce identical results on different binaries and different platforms.
Generally, Box2D cannot do this. The reason is that different compilers and different processors implement floating point math differently, which affects the results of the simulation. To learn more about this topic, see the Proper Fixation blog post Consistency: how to defeat the purpose of IEEE floating point.
The only true way to produce a deterministic simulation is by reloading the same Scene on the same machine. It is possible to have a deterministic result on the same machine with 2D Physics if you structure your application appropriately.
When Unity creates the physics "world", it starts empty. From this point onwards, on the same machine, given the same events and using no random input, the physics system produces a deterministic simulation for the GameObjects that have physics components. A cold restart occurs when you destroy the world and the physics components contained in it, then create a new world and add the same physics components in the same order. In Unity, this is the same as reloading the same Scene, with the same initialization routines, because Unity adds the GameObjects in the same order and simulates them in the same way. Unity should produce an identical simulation.
This information only applies when using a fixed-step simulation update and not a manual variable time-step.
If reloading the Scene is not possible, you can also perform a soft restart on the world. You can achieve deterministic physics with a soft restart on the same machine, but not on different machines, because of the difference in compilers and CPUs as indicated at the start of this document.
To perform a soft restart, you must ensure that each simulation starts with no physics components enabled, so that Unity doesn't automatically add them to the physics world. You then need to manually enable or instantiate each physics component, in the same order each time. In theory, this results in Unity adding physics behavior to the physics world in the same order each time. As long as you enable the physics components in the same order, Unity should maintain the correct internal ordering.
The physics engine stores all of its states in various structures, and the order of these structures usually drives the order in which the physics engine calculates GameObjects with physics components and their interactions. This means you can’t just reposition GameObjects to their starting configuration to perform a soft restart. Even a tiny change of processing order can result in a larger simulation change over time. In many cases this change isn’t noticeable, but it’s not a good scenario for applications where you need to produce an identical simulation every time.
Follow these steps to perform a soft restart:
- Save your Scene with all 2D physics components disabled (or alternatively, set all GameObjects using 2D physics components to Inactive).
- Prepare the Scene for the simulation:
- Enable the 2D physics components in the same order, each time. If the GameObjects containing the components are not in the Scene, then Instantiate them in the same order each time.
- Set the all the 2D Rigidbody components to Kinematic.
- Set the 2D Rigidbody components to the state in which they should be at the beginning of the simulation (position, velocity, angular velocity, inertia and rotation).
- Set the 2D Rigidbody components to actual desired Kinematic value.
- Set the GameObjects with the 2D Physics components to Inactive.
- Run at least one fixed step of the simulation to set up the physics Scene.
- Run the simulation
- Set each GameObject to Active, and call rigidbody.WakeUp
- Run the physics simulation using a fixed time step.
- At the end of the simulation:
- Reset the process. To do this, disable the GameObjects in reverse order, or use DestroyImmediate to destroy the GameObjects with the 2D physics components in reverse order.
- Wait one fixed update frame to enable the physics world to update its state.
While a soft restart can help improve determinism, it is not guaranteed to be a perfect workaround. There are various states in the physics engine that don’t reset when you add or remove physics components, so make sure you test thoroughly with the whole breadth of simulation scenarios in your application.
The only accurate way to guarantee determinism in 2D Physics using Box2D is to reload the Scene. This performs a cold restart, which destroys and recreates the Box2D physics world.