I’m making an asynchronous multiplayer game where players plot out their moves before the round is “simulated”. I want to use physics in this game to control player movement and enemies. After each round of plotting moves, the round is simulated from the start, showing everything that happend on previous round(s) and including everything from the current round.
Since this game is async multiplayer, my intention is to store the plotted moves and game state in a stringified JSON object that can be passed into the game with a WWW request. This will then be converted to an object within the game, and used to rebuild the level each round.
My question is with regards the simulation that plays out based on the plotted moves. If the level is rebuilt exactly the same, will the physics engine produce the exact same results each and every time? Does chaos theory exist within the engine? Will different devices produce different physics calculations and results?
It’s critical that the physics results are the same each time, as the result of each round can have a massive impact on future rounds.
Mat, depending on what you’re doing you might be ok if you just need the results to be pretty close but not perfectly identical every time. Only you can make that call though of course. In a turn based game I’d think that if the initial states are set and it’s just a few seconds of gameplay that’s played back this way you might be alright. The longer the sim runs the more likely it’s going to be to deviate, especially if you’re doing anything in Update() where the frame rate might matter to the end results.
Unity physics is not deterministic, not even in the same machine. The same exact actions, even triggered from FixedUpdate, have small deviations that accumulate over time. You can reproduce short tests where results are roughly the same, but longer runs will surely result in chaos due to the accumulated deviations.
I’ve only just seen these replies. Thanks for posting.
From what you’ve all hinted at, maybe I should avoid Physics in this game. Out of interest, how would you define “short runs” or “longer runs”. By the end of the game, I would expect the full playback to last 30 - 60 seconds.
I suppose I could log the final states of each physics object and make sure that after playback, each object was positioned at x,y,z with their velocities set manually. This may result in a stutter if things go out of sync with the original outcomes, but I could live with that - maybe.
I’m leaning towards no physics but will run some tests.
It doesn’t really matter if it’s short or long, there are times where it will give inconsistent results, depending on how busy the cpu is for example (perhaps more simulation steps were fired) so funny results can occur sooner than expected.
As for my experience, the non-determinism is strongly influenced for the number of collisions during the evaluated time. If your object doesn’t collide with anything (i.e. a starship) then you might get a reasonable determinism in long runs ( > 1 minute). If your object experiences frequent collisions then each collision adds a bit of “entropy” to the result. For instance, in vehicles with frequent collisions results can begin diverging after 10-20 seconds.
@matbrummitt As hippocoder mentioned, Unity’s physics engine isn’t deterministic for several unfixable reasons (I’ve tried :c). Lockstep Framework actually works like what you described. Players plot out moves and the simulation simulates the game while executing those moves at the proper times. This can be done as a replay with all the moves known in advance or in real time, with the moves being generated fractions of a second before they’re played.
Anyways, one way you could use to have players generate moves (AKA commands) before-hand is to create a Frame[ ] and fill it up with new frames. Each frame accounts for approximately 1/16 seconds of the game, starting from the frame at index 0. If the player wants Egg to blow up at 10 seconds, you would find the frame at 10 seconds into the game (10 * 16 = 160) and do frames[320].AddCommand (command). Inside the command, you would store the necessary information needed to cause Egg to blow up.
Finally use FrameManager.AddFrame on every frame in your array then let the simulation run. You’ll have to override NetworkHelper’s Send functions to not send frame or player inputs while playing the plotted commands (although if a frame is already registered, it will ignore any subsequent frames trying to get its spot so this is really just to reduce overhead).
Do you mean “unfixable externally” (with scripting), “unfixable externally but could be fixed by Unity devs”, or “unfixable because of the PhysX intrinsic design/nature not allowing any fix”?
Floats. Different operating systems round floats differently after calculations.
Unity stuff. On high performing machines, fixed timestep frames run optimally but when the standard framerate drops below the fixed timestep framerate, several FixedUpdate frames will run on the same standard frame to catch up.
More Unity stuff. Elapsed time and order of spawning objects affects simulation. You can’t reset the simulation so elapsed time is a biggie.
No control over running frames. You could toggle Time.fixedDeltaTime to 0 to stop simulation. I haven’t tried this.
I encourage you to try getting consistent numbers if you don’t want to take my word.
@jpthek9 Thank you very much for the details! I was very interested on knowing your thoughts on this because you seem to have dedicated a lot of time to this issue. I’ve done some experiments as well, but wanted to know if there are alternative ways of approaching the issue.