Serialization
The complete state of the simulation can be serialized by taking a snapshot of the
physics world with PhysicsWorld.snapshot. This results in a bytes
object that may be saved on the disk, sent through the network, etc. The snapshot can then be restored with the
PhysicsWorld.restore static method, which creates a new world in the exact same state as the serialized one:
# Serialize the whole physics world.
serialized = world.snapshot()
# Deserialize it.
deserialized = rp.PhysicsWorld.restore(serialized)
# The simulation can continue using the deserialized world.
deserialized.step()
The world also supports the pickle module of Python, which relies on these snapshots. Therefore, a world can be saved as part of any picklable structure of your application:
# The world can be pickled like any other Python object, e.g., to save it on the disk.
data = pickle.dumps(world)
unpickled = pickle.loads(data)
The snapshot contains everything the world is made of: the rigid-bodies, colliders, joints, and soft-bodies
(including their user data), the gravity, the integration parameters, the island manager, the CCD solver, the
broad-phase, and the narrow-phase (with its contacts). However, the thread pool configured by
PhysicsWorld.set_num_threads, the PhysicsWorld.event_error_policy option, the performance counters, and the objects of your application given to the world (the event handler and the physics hooks) are not part of the
snapshot: they must be configured again for the deserialized world. Each object of the deserialized world keeps the
index and the generation it had in the serialized world, so the handles of the serialized world can be used with the
deserialized world as well:
# The handles of the serialized world refer to the same objects in the deserialized world.
restored_ball = deserialized.rigid_bodies[ball_handle]
print("Ball altitude in the deserialized world:", restored_ball.translation.y)
# The event handler and the physics hooks aren't part of the snapshot: they must be given again.
deserialized.event_handler = rp.ChannelEventCollector()
For debugging purposes, PhysicsWorld.snapshot_json gives a human-readable snapshot as a JSON string instead, which
is restored with PhysicsWorld.restore_json. It is much larger and slower than the binary snapshot:
# A human-readable (but much larger and slower) JSON snapshot, e.g., for debugging.
json_snapshot = world.snapshot_json()
from_json = rp.PhysicsWorld.restore_json(json_snapshot)
Most of the other objects of the bindings can be pickled on their own too, e.g., the rigid-bodies, colliders, and joints (as well as their builders), the shapes, the handles, the sets, the integration parameters, or the mass properties. On the other hand, the pipelines and the event collectors can't be pickled:
# Most objects can be pickled on their own too, e.g., the rigid-body and collider builders.
ball_builder = rp.RigidBody.dynamic(translation=(0.0, 10.0, 0.0))
restored_builder = pickle.loads(pickle.dumps(ball_builder))
new_ball = world.add_body(restored_builder, colliders=[rp.Collider.ball(0.5)])
A snapshot is not a stable file format: it should only be restored by the same version of the bindings as the one
that serialized it. The snapshots recognized as incompatible (or corrupted) are rejected with a SerializationError.
Like any pickled data, only restore snapshots coming from a trusted source.
If the bindings are built with the determinism feature, and if your platform fulfills the required
determinism requirements, then you have the guarantee that running the exact same simulation on
two different machines will result in the exact same snapshot bytes if the world is serialized on both machines after
the same number of timesteps.