Using applyForce with Bullet on Android

I ran into something interesting using native bullet on Android that I thought might be useful to others. Sorry in advance for the long post, but some explanation is required :slight_smile:

If you don’t already know, the SDK automatically replaces jBullet libraries with native bullet libraries. This is mostly due to the performance difference between jBullet and native bullet when running on an Android device.

Bullet runs at a rate that is independent of the rendering framerate, which is generally a good thing. However, it does introduce some weirdness that you should be aware of.

By default, bullet is configured in BulletAppState to run once every 1/60 seconds. This is true if the rendering framerate is at 15fps or 200fps.

Each rendering frame, BulletAppState calls an update method in bullet called stepSimulation and passes a timestep value which equals the amount of time that has passed since the last call to stepSimulation (ie. basically the rendering tpf time).

If the amount of time is less that 1/60 seconds, bullet will update the physics objects location by interpolation without running a real physics update. When the accumulated time passed to bullet becomes greater than 1/60 seconds, bullet runs a physics update and interpolates the left over amount of time. The benefit of this is that bullet runs a real physics update at a rate that is independent of the rendering framerate so that the physics simulations appear to have the same physics reactions independent of the rendering framerate and target hardware capabilities.

On the other side of things, if the rendering tpf is greater than 1/60 seconds (which is typical on Android), bullet may run multiple physics updates within 1 rendering frame. For example, if the rendering update loop is running at a tpf of 30fps (1/30 sec), then bullet will most likely run 2 physics updates within 1 update loop. If the rendering update loop is running at 15fps (1/15 sec), then bullet will run 4 times within 1 rendering frame. etc…

Users are notified each time bullet runs a real physics update via the prePhysicsTick callback. It is recommended that users use this method to apply forces to the physics objects. If you apply forces in the normal render update method in your control or appState, it is not guarenteed to be apply to physics because bullet may not be running a real update during that frame or it may be running multiple times during that frame. The prePhysicsTick is called once every time bullet runs a real physics update. This could be 0 times during a rendering frame or multiple times during a rendering frame depending on the current tpf for that frame.

This is generally a nice feature to keep physics looking consistent across different hardware targets, but you need to understand the sequence of events to make it work properly.

The issue comes in when the rendering framerate is running at < 60fps (normal for Android). In this case, bullet will sometimes run at least 2 times within 1 rendering frame. The sequence bullet follows in this case is the following:

Bullet Sequence for each Rendering Frame

  1. apply gravity to the object
  2. if a real physics update is needed (ie. accumulated time > physics update time):
        2a. for each physics update needed:
            call prePhysicsTick in jME
            apply forces and impulses to object velocity and move the object
            call physicsTick in jME
  3. interpolate object locations and velocities for any leftover time
  4. clear forces

As you can see, forces are not cleared in between each call to prePhysicsTick. This means that if you are using applyForces inside prePhysicsTick and bullet runs multiple physics updates within 1 rendering frame, forces applied in prePhysicsTick will be accumulated. In my applications, objects in Android appeared to move much faster than objects on Desktop because the forces I applied were accumulated over multiple physics updates within 1 rendering frame.

One solution is to call getPhysicsSpace.clearForces() at the beginning of the prePhysicsTick method to avoid accumulating forces. However, this also removes the force due to gravity since bullet does not re-apply gravity forces in between each call to prePhysicsTick. I then had to also add object.applyForce(getGravity().mult(mass)) to re-apply gravity. Not really a clean solution, but it seemed to work.

Another solution is to use applyImpulse instead of applyForce. When you look at the bullet math that is being done between applyForce and applyImpulse, the only real mathematical difference is that applyForce is internally multiplied by the physics update time before being added to the object velocity whereas applyImpulse is directly applied to the velocity. The other difference is that forces are accumulated during the multiple calls to prePhysicsTick within 1 rendering frame whereas impulses are not.

Since the physics update time is passed to prePhysicsTick in jME, you can simply multiply your force amount by the physics update time and then use applyImpulse. This removes the accumulation problem and still allows you to keep the same math you have to apply a force on an object in a framerate independent way.

7 Likes

thanks for the detailed explanation!! I always found it weird why the results between ApplyForce and ApplyImpulse were so different when essentially, Impulse = Force * time (this is just in general, not just for Android). I always found ApplyImpulse to give more realism, so used that, good to know its required for android :slight_smile:

This is what I got for optionA. Seems to work in a sense that my objects still react physically.

[java]@Override
public void prePhysicsTick(PhysicsSpace space, float f){
space.clearForces();
super.prePhysicsTick(space, f);
for(PhysicsRigidBody body : getPhysicsSpace().getRigidBodyList()){
space.getGravity(gravity);
body.applyCentralForce(gravity.mult(body.getMass()));
}
}[/java]

@iwgeric what if you just set physycs to 30fps?

[java]
bulletAppState.getPhysicsSpace().setAccuracy(1f/30f);
[/java]

This also happened to me and I also had to do the same fix.
I will try to use the apply impulse and see what the results are.
What I also noticed was that with the applyforce is that it reacts differently depending on your android device hard ware.
It wasn’t running the same. Reason is the above.
Thanks for the nice explanation.

@funbox
You might also want to try to only clearForces on the rigidbody you are applying the forces to. This way, you wouldn’t need to reset gravity on everything, just the rigidbody you are affecting with other forces. I’m not sure how many objects are in your physicsSpace, but it might be more efficient to only affect 1 object instead of call Java->Native JNI->Java for each object mutilple times in a frame.

@mifth
That will definitely affect how many times prePhysicsTick is called within one frame. However, the downside is that you are running bullet at a lower rate. The affect of this might be nothing and might be significant. If you run bullet slower, each moving object will jump a little further each tick. This could possibly cause a larger penetration or even cause objects to go through (or get stuck inside) other objects. You just need to be careful, that’s all.

The other issue is that on Android you are not running at a fixed timeframe on the render side. You probably won’t be able to pick a new number and guarantee that the device is running the rendering update loop faster than the physics accuracy rate.

That will definitely affect how many times prePhysicsTick is called within one frame. However, the downside is that you are running bullet at a lower rate. The affect of this might be nothing and might be significant. If you run bullet slower, each moving object will jump a little further each tick. This could possibly cause a larger penetration or even cause objects to go through (or get stuck inside) other objects. You just need to be careful, that's all.

The other issue is that on Android you are not running at a fixed timeframe on the render side. You probably won’t be able to pick a new number and guarantee that the device is running the rendering update loop faster than the physics accuracy rate.

@iwgeric , physics and rendering is not synced anyway. Physics works in a different thread. I oftenly set physycs to 40fps for desktop apps when rendering is 60fps. So, i would prefer to change accuracy in my case for android too.

Another way is to set the accuracy to TPF:
[java]bulletAppState.getPhysicsSpace().setAccuracy(Tpf);[/java]

Possibly, i tell something wrong. @normen is the master of physics.