dimforge / bevy_rapier

Official Rapier plugin for the Bevy game engine.
https://rapier.rs
Apache License 2.0
1.27k stars 259 forks source link

Problem with deterministic simulation #79

Closed Looooong closed 3 years ago

Looooong commented 3 years ago

I'm experimenting with deterministic physics simulation using Rapier with Bevy. The initial state includes a main character and a set of balls inside a room. The main character can be controlled to move around the room and push the balls. After simulating for awhile, a replay button can be pressed to replay the physics simulation along with the recorded character movements.

The final position of the main character at the end of the replay is identical between multiple replays. This confirms that moving the kinematic character using velocity is deterministic.

However, the final positions of the balls are not the same between replays. At first, I thought maybe deleting and recreating all the physics entities causes the order of the physics bodies on the memory changed, thus changes the processing order of physics contacts. I modified the code to retain the physics entities between replays. Nevertheless, the balls' final positions still change between replays.

The enhanced-determinism feature is already enabled, and the timestep mode is set to TimestepMode::FixedTimestep. Is there anything else to make the simulation deterministic?

The app repo: https://github.com/Looooong/doce/tree/67a32acbd8cfbf31c88b253bf5991a17da5d06bc

sebcrozet commented 3 years ago

Hi! There are a few things that don't look deterministic in your code:

In fact, I'm not sure what guarantees Bevy gives in terms of determinism of Query iteration unless you restart the whole app.

Looooong commented 3 years ago
  • Second, you are despawning every entity and spawning new ones. I'm not actually sure that the new entity/components will be ouptut in the same order by Bevy's query.iter(). Because I am guessing that the entitties handles are recycled by Bevy, and this recycling may give entity handles that depend on the order of the previously removed entities.

I actually retain all the entities in the world when switching from AppState::InGame to AppState::Replay. The things that get reset are RigidBodyPosition (set to inital RigidBodyPosition) and RigidBodyVelocity(set toRigidBodyVelocity::zero()`).

  • First, if you really want to reset the simulation to its initial state, you should destroy every single component and resource originating from Rapier, and start fresh. Otherwise, the state before the replay may affect the state during the replay (because it can change the order some things can be inserted).

Thank's for the reminder. I totally forgot about the resources inserted by Bevy Rapier. I will look into those closer.

In fact, I'm not sure what guarantees Bevy gives in terms of determinism of Query iteration unless you restart the whole app.

I had some success building a deterministic physics simulation app using Bevy Rapier before this one. I implemented a 2D double pendulum system. The final simulation states are identical after each run (restart the whole app as you say). Now, my goal is to take things further by generating complex 3D physics state and having a replay system so that I don't have to restart the app.

Looooong commented 3 years ago

By resetting Rapier resources when cleaning up physics entities, I managed to produce somewhat deterministic results. Instead of generating a different outcome after each replay with the same set of inputs, my app is able to come to an identical deterministic result after each replay within the same app session (not restarting the whole app). The only problem left is that it seems to generate 2 different outcomes for the same set of inputs between app restarting.

Anyway, my initial state is complex and unstable, unlike the boxes example within Rapier. So this is a good result for me. To ensure determinism, I can just make the game simpler and more stable than this one.

Moreover, Bevy and Rapier still have a long way to go. I'm looking forward to more development from you guys. I will close this issue now.