Does Box2D Physics rely on the framerate?

2019-05-23 14:35发布

I am making a 2D sidescroller game for Android and am thinking of using Box2D for the physics. I would like to know if when using Box2D, if the framerate of the device drops from 60 fps (Android's cap) to, for example, 30 fps will all the physics slow down with it?

To elaborate, I'm going to be using real-time online multiplayer and so the framerate cannot be relied upon as a constant. So if one of the devices in the multiplayer game is running at 60 fps and another is at 30 fps and an object should be moving at 10 metres/second will it move at 5 metres/second on the slower device?

I would like to use Box2D, so if this problem is the case, is there a way around it?

3条回答
混吃等死
2楼-- · 2019-05-23 15:30

There are two things you need to Consider.

Frame rate does not really affect the speed of the moving objects. even if your framerate is @ 10fps your body will still move at 10 metres / second.

What does really change is the number of position and velocity iterations that you can effectively do at that frame rate and the precision of collision detection.

At slower framerates we have frequently seen bodies tunnel through others and collisions failing one way or another.

On way to fix this would be to use a fixed timestep even in cases where there is incoherent framerate. The code we use for this is as follows

float maximumStep = 1.0f/60.0f;
float progress = 0.0;

// TODO: Tune it further to prevent DEATH!
while (progress < dt)
{
    float step = min((dt-progress), maximumStep);
    // Since we do not have any post collision dynamics therefore we can get away with a one step process for velocity / position iterations.
    GameState::sharedInstance()->box2dWorld->Step(step, 8, 8);
    progress += step;
}

Note the TODO here to prevent DEATH. Although this approach guarantees that the processing for the physics works at a fixed frame rate and there are absolutely NO collision failures, BUT you have to make sure that you do not end up stuck in the timestep loop mentioned above.

We tune things by setting the maximum step value to the LARGEST value that results in consistent physics. This is done by trials to see at what values collisions start to fail and pivot the maximum step at that point.

You also have to tune the number of position and velocity iterations (the numbers mentioned in the Step Function to the proper values based on your game. Every game has to tune this according to it's own needs. Keep this to a minimum that works for you.

查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-05-23 15:33

Your question is, I think, really two questions.

1) Should you vary the rate of the physics timestep during simulation.

NO, you should not.. You can iterate the engine multiple times during the a time step of the game loop (call the Step(...) function multiple times with the same step values).

From section 2.4 of the 2.3.0 Box2D manual:

A variable time step produces variable results, which makes it difficult to debug. So don't tie the time step to your frame rate (unless you really, really have to).

2) How can I connect two real-time physics simulations and slave their physics update cycles to each other.

Once upon a time there was a genre-altering game called Age of Empires. It boasted thousands of AI units fighting each other in near-real-time on a 28.8 network. It worked so well, somebody wrote an article about how they did it:

  1. The article.
  2. The pdf version of the article.

I adapted the technique and the code below for my update loop so that I could control the frame rate of two games running against each other on two different iPads.

void GameManager::UpdateGame()
{
   const uint32 MAXIMUM_FRAME_RATE = Constants::DEFAULT_OBJECT_CYCLES_PER_SECOND();
   const uint32 MINIMUM_FRAME_RATE = 10;
   const uint32 MAXIMUM_CYCLES_PER_FRAME = (MAXIMUM_FRAME_RATE/MINIMUM_FRAME_RATE);
   const double UPDATE_INTERVAL = (1.0/MAXIMUM_FRAME_RATE);

   static double lastFrameTime = 0.0;
   static double cyclesLeftOver = 0.0;

   double currentTime;
   double updateIterations;

   currentTime = CACurrentMediaTime();
   updateIterations = ((currentTime - lastFrameTime) + cyclesLeftOver);

   if(updateIterations > (MAXIMUM_CYCLES_PER_FRAME*UPDATE_INTERVAL))
   {
      updateIterations = MAXIMUM_CYCLES_PER_FRAME*UPDATE_INTERVAL;
   }

   while (updateIterations >= UPDATE_INTERVAL)
   {
      //      DebugLogCPP("Frame Running");
      updateIterations -= UPDATE_INTERVAL;
      // Set the random seed for this cycle.
      RanNumGen::SetSeed(_cycleManager->GetObjectCycle());
      // Dispatch messages.
      _messageManager->SendMessages();
      // Update all entities.
      _entityManager->Update();
      // Update the physics
      _gameWorldManager->Update(Constants::DEFAULT_OBJECT_CYCLE_SECONDS());
      // Advance the cycle clock.
      _cycleManager->Update();
   }

   cyclesLeftOver = updateIterations;
   lastFrameTime = currentTime;
}

This piece of code keeps the number of iterations executed balanced between an upper and lower maximum. One key piece to note is that the actual call to this function doesn't happen if the message from the other player has not been received in a timely fashion. This effectively slaves the two physics systems together.

3) THE PART THAT YOU (MAYBE) REALLY SHOULD KNOW

If you plan on using Box2D on both devices to independently run the physics, you are going to almost certainly going to see them diverge after a short time. I ran my game on an iPad 2 and iPad 3 and noticed after a few seconds they diverged (collisions occur in one, but not the other). This is because rounding behavior in floating point numbers is different based on multiple factors. For a few quick calculations, no problems. But small discrepancies creep into the lower order bits and accumulate when the values are continually cycled through integrators (as you would see, for example, in a physics simulation). Double precision helps a little, but not in the end.

A few simple tests of looking at a specific bouncing ball in a field of bouncing balls on multiple CPUs (e.g. iPad 2 vs. iPad 3 for the exact same code) will show this. The errors creep in after a couple seconds of motion and suddenly your velocities/positions are off enough to make a difference.

Fixed point math is a solution to this, but this way also leads to its own kind of madness. At one point, Box2D had a fixed point version, but this time has passed.

I even toyed with cooking up a fixed point version of Box2D, but got distracted by another project (Space Spiders Must Die!). Some day...

This may not be a problem for your particular situation (i.e. you are not doing independent simulation or doing it in a way that it works as expected), and if it is not, no worries.

You can see lots of other Box2D stuff (but not this game...it is not up there yet) here on my blog.

Was this helpful?

查看更多
Explosion°爆炸
4楼-- · 2019-05-23 15:37

Found the answer in the Box2D manual.

"2.4 Simulating the World (of Box2D)

.....We also don't like the time step to change much. A variable time step produces variable results, which makes it difficult to debug. So don't tie the time step to your frame rate (unless you really, really have to)......"

So basically, the physics do not change with framerate, but they can if you want them to do so.

查看更多
登录 后发表回答