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
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
- apply gravity to the object
- 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 - interpolate object locations and velocities for any leftover time
- 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.