The jMonkeyEngine3 has built-in support for jBullet physics via the com.jme3.bullet
package.
Game Physics are used in applications that simulate mass/gravity, collisions, and friction. Think of pool billiard or car racing simulations.
If you are looking for info on how to respond to physics events, read about Physics Listeners.
Bullet physics runs internally at 60fps by default. This rate is not dependent on the actual framerate and it does not lock the framerate at 60fps. Instead, when the actual fps is higher than the physics framerate the system will display interpolated positions for the physics objects. When the framerate is lower than the physics framerate the physics space will be stepped multiple times per frame to make up for the missing calculations. A bullet physics space can be created with a BulletAppState. The updating and syncing of the actual physics objects happens in the following way: A "normal" update loop with physics looks like this:
When you use physics, 1 unit (1.0f) equals 1 meter, weight is expressed in kilograms, most torque and rotation values are expressed in radians.
Full code samples are here:
A short overview of how to write a jME application with Physics capabilities:
Do the following once per application to gain access to the physicsSpace
object:
com.jme3.app.SimpleApplication
.private BulletAppState bulletAppState;
public void simpleInitApp() { bulletAppState = new BulletAppState(); stateManager.attach(bulletAppState); ...
You can also access the BulletAppState via the state manager:
stateManager.getState(BulletAppState.class)
For each Spatial that you want to be physical:
com.jme3.bullet.control.RigidBodyControl
PhysicsCollisionListener
interface to respond to PhysicsCollisionEvent
s if desired. Before you can create a Physics Control, you must create a Collision Shape from the com.jme3.bullet.collision.shapes
package.
The Collision Shape is a simplified shape for which physics are easier to calculate than for the real shape of the model. This approach speeds up the simulation greatly.
Shape | Purpose |
---|---|
BoxCollisionShape | Box shaped objects such as bricks, crates, simple obstacles. Does not roll. |
SphereCollisionShape | Spherical objects such as balls. Can roll. |
CylinderCollisionShape | Tube-shaped pillars, disc-shaped wheels. Can roll on one side. |
CapsuleCollisionShape | A compound of a cylinder plus two spheres at the top and bottom. Rotated upright, this shape is optimal for character nodes: A cylinder-shaped body does not get stuck at corners and vertical obstacles; the rounded top and bottom do not get stuck on stair steps and ground obstacles. Is locked to stay upright, does not roll. |
CompoundCollisionShape | A CompoundCollisionShape allows custom combinations of box/sphere/cylinder shapes to form another more complex shape. |
MeshCollisionShape | A free-form mesh-accurate shape that wraps itself around a mesh. Limitations: Only non-mesh collision shapes (sphere, box, cylinder, compound) can collide with mesh-accurate collision shapes. The Mesh Collision Shape only works for static obstacles, e.g. for a game level model. |
GImpactCollisionShape | This free-form Mesh Collision Shape can be used for moving objects. Uses http://gimpact.sourceforge.net/. Limitations: CPU intensive, use sparingly! We recommend using HullCollisionShapes or CompoundShapes made of simple shapes if you need improved performance. |
HeightFieldCollisionShape | Optimized Mesh Collision Shape for static terrains. This shape is much faster than a other Free-Form Mesh Shapes. Requires heightmap data. |
HullCollisionShape | A collision shape that is based on a mesh but is a simplified convex version. |
SimplexCollisionShape | A physical point, line, triangle, or quad Collision Shape, defined by one to four points. |
PlaneCollisionShape | A 2D plane that can be used as flat solid floor or wall. |
Pick the right shape for the mesh for what you want to do: If you give a box a sphere collision shape, it will roll; if you give a ball a box collision shape, it will sit on a slope. Let's look at the constructor:
MeshCompoundShape and MeshCollisionShape are both mesh-accurate and are intended for immobile scene objects, such as terrains, buildings, or whole shooter levels. Limitation: Only collisions of non-mesh-accurate shapes (sphere, box, etc) shapes can be detected against mesh-accurate shapes.
CompoundCollisionShape myComplexShape = CollisionShapeFactory.createMeshShape((Node) myComplexGeometry );
An angular, non-mesh-accurate compound shape:
CompoundCollisionShape boxShape = CollisionShapeFactory.createBoxCompoundShape((Node) someBox);
SphereCollisionShape, BoxCollisionShape, CapsuleCollisionShape are also not mesh-accurate, but have better performance. The can be added to anything, and collisions between them and any other shape can be detected.
SphereCollisionShape sphereShape = new SphereCollisionShape(1.0f);
Available PhysicsControls in the com.jme3.bullet.control package are:
Physics Control | Purpose |
---|---|
RigidBodyControl | Use for physical objects in the scene, e.g. projectiles and obstacles – things that are freely affected by physical forces, be it by collision or falling. |
CharacterControl | Use for characters (persons, animals) that stand upright, orthogonally to the X/Z plane. When directional forces are applied to a CharacterControl'ed Spatial, it does not tip over (as a RigidBodyControl'ed Spatial would), but it moves upright (as a walking character would). |
GhostControl | A GhostControl is a PhysicsControl that detects overlaps with other physical objects. A GhostControl is non-solid and moves with the Spatial it is attached to. Use this for game elements that do not have a visible solid Geometry: Aggro radius, motion detectors, photoelectric sensors, radioactive areas, life-draining ghosts, etc. |
VehicleControl PhysicsVehicleWheel | Implements terrestric vehicle behaviour. |
RagDollControl | Implements Ragdoll behaviour. |
Bullet Physics are available in jME3 through a several classes. You will use PhysicsControls in 99% of the time.
PhysicsControls are the recommended way to use physics in a jME3 application. PhysicsControls are flexible and can be added to any Spatial to make it act according to physical properties. These Control classes directly extend Bullet Physics Objects. Package: com.jme3.bullet.control
Physics Objects are mostly standard Bullet classes like RigidBody, GhostObject etc., that jME3's other classes are built upon. Advanced users can use these classes to create custom physics functions. Package: com.jme3.bullet.objects
Physics Nodes have Bullet Controllers attached and wrap their methods to “simulate” the old physics nodes that were available before alpha-4. The setLocalTranslation/Rotation() info is transferred to the Bullet Objects for simplicity. Do not use Physics Nodes, use PhysicsControls instead. Package: com.jme3.bullet.nodes
The various Physics Control constructors expect a Collision Shape (here thingShape) and a mass (a float).
RigidBodyControl myControl=new RigidBodyControl( thingShape , 1.0f );
To make the Physics Control visible in the scene, you must attach the Control to a Geometry (e.g. a model named myGeometry):
myGeometry.addControl(myControl);
This code sample creates a physical Character:
// Load a normal model Node model = (Node) assetManager.loadModel("Models/myCharacterModel.mesh.xml"); rootNode.attachChild(model); // Create a appropriate physical shape for it CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1); CharacterControl character_phys = new CharacterControl(capsuleShape, 0.01f); // Attach physical properties to model and PhysicsSpace model.addControl(character_phys); bulletAppState.getPhysicsSpace().add(character_phys);
Tip: Spheres and Boxes will fall back to their correct default Collision Shape if you don't specify a shape in the RigidBodyControl constructor. The following creates a box with Box Collision Shape:
Box(1,1,1); myBox.addControl(new RigidBodyControl( 1.0f )); bulletAppState.getPhysicsSpace().add(myBox);
The Physics Space is an object in BulletAppState that is like a rootNode for Physics Controls.
bulletAppState.getPhysicsSpace().setGravity(new Vector3f(0f,-1f,0f)); bulletAppState.getPhysicsSpace().setAccuracy(0.005f);
bulletAppState.getPhysicsSpace().add(myPhysicsControl); ...
myModel.addControl(myPhysicsControl); ...
rootNode.attachChild(myModel); ...
bulletAppState.getPhysicsSpace().remove(myPhysicsControl); myModel.removeFromParent();
On a PhysicsControl, you can set the following physical properties.
RigidBodyControl Method | Property |
---|---|
setFriction(1f) | Friction. |
setMass(1f) | Sets the mass. Dynamic objects have masses > 0.0f. Static immobile obstacles (including buildings and terrains) have mass 0.0f. |
setPhysicsLocation() | Positions the object. Do not use setLocalTranslation(). |
setPhysicsRotation() | Rotates the object. Do not use setLocalRotate(). |
setRestitution(0.0f) | How bouncy the object is. For a rubber object set this > 0.0f. This setting has an impact on performance. |
setKinematic(true) | A kinematic node is not affected by gravity, but it is solid and affects other physics objects. It has a mass its position is updated from the spatials translation. You can attach joints to it. |
setGravity(new Vector3f(0f,-1f,0f)) | You can change the gravity of a physics object after it was added to the physics space. |
setCcdMotionThreshold(0.1f) | The amount of motion in 1 physics tick to trigger the continuous motion detection. |
CharacterControl Method | Property |
setFallSpeed(1f) | Fall speed (down) |
setJumpSpeed(1f) | Jump speed (up) |
setMaxSlope(1.5f) | How steep the slopes are that the character can still climb. |
setUpAxis(1) | 0 = X axis , 1 = Y axis , 2 = Z axis. E.g. for characters and vehicle, up is usually along the the Y axis. |
setGravity(1f) | You can change the Gravity of a physics object after it was added to the physics space |
Physical objects…
A dynamic physics object is one that falls when in mid-air, it bounces off obstacles, and is pushed around when it collides with another physical object. It has a mass.
You can create static physical objects without mass. They are still treated as solid objects, but they cannot be dynamically pushed around. They act as static, immobile attached physical obstacles such as terrains and building models.
Kinematic RigidBodys have a mass, but they are not affected by gravity. When non-kinematic objects collide with a kinematic object, only the non-kinematic ones are pushed away by the collision. The intesity of the kinematic's effect against other objects depends on their speed and mass: Ekin = mass * speed^2
(well, approximately, bullet doesn't use Einsteins formula ;)) Tip: Spatials with a kinematic RigidBodyControl can be moved programmatically, e.g. using setLocalTranslation() in the update() loop, or by an Animation Path. You can also "hang them up in mid-air" and attach other PhysicsNodes to them using hinges and joints.
airhook.setKinematic(true);
Use the following methods to move physics objects.
Method | Motion |
---|---|
setAngularVelocity(new Vector3f(0f,0f,1f)) | Set the current rotational speed of the object; the x, y and z component are the speed of rotation around that axis. |
setLinearVelocity(new Vector3f(0f,0f,1f)) | Set the current linear speed of this object |
setWalkDirection(new Vector3f(0f,0f,0.1f)) | Make a physical character walk (characters are locked to prevent falling over and use a simple physics simulation). Use setWalkDirection(Vector3f.ZERO) to stop a directional motion. |
applyCentralForce(…) | Move (push) the object once with a certain moment, expressed as a Vector3f. |
applyForce(…) | Move (push) the object once with a certain moment, expressed as a Vector3f. Optionally, you can specify where on the object the pushing force hits. |
applyContinuousForce(…) | Keep moving (pushing) the object with continuous force in one direction, expressed as a Vector3f. Optionally, you can specifiy where on the object the pushing force hits. You can applyContinuousForce(false) to stop the force. |
applyTorque(…) | Rotate (twist) the object once around its axes, expressed as a Vector3f. |
applyContinuousTorque(…) | Keep rotating (twisting) the object continuously around its axes, expressed as a Vector3f. You can applyContinuousTorque(false) to stop the rotation. |
applyImpulse(…) | An idealised change of momentum. This is the kind of push that you would use on a pool billiard ball. |
applyTorqueImpulse(…) | An idealised change of momentum. This is the kind of push that you would use on a pool billiard ball. |
clearForces() | Cancels out all forces (force, torque) etc and stops the motion. |
Note: It is possible to position physics nodes using setLocalTranslation(), e.g. to place them in their start position in the scene. However you must be very careful not to cause an "impossible state" where one physical object overlaps with another! Within the game, you typically use the setters shown here exclusively. Physics also supports the following features:
Method | Property |
---|---|
setCollisionShape(collisionShape) | Changes the collision shape. |
setCollideWithGroups() setCollisionGroup() addCollideWithGroup(COLLISION_GROUP_01) removeCollideWithGroup(COLLISION_GROUP_01) | Collision Groups are integer bit masks – enums are available in CollisionObject. All physics objects are by default in COLLISION_GROUP_01. Two objects collide when the collideWithGroups set of one contains the collisionGroup of the other. |
setDamping(float, float) | The first value is the linear threshold and the second the angular. |
setAngularFactor(1f) | Set the amount of rotation that will be applied. A value of zero will cancel all rotational force outcome. |
setCcdSweptSphereRadius() | ? |
setSleepingThreshold(float,float) | Sets the sleeping thresholds wich define when the object gets deactivated to save ressources. Low values keep the object active when it barely moves. The first value is the linear threshold and the second the angular. |
You can control the game by triggering forces; or may want to respond to collisions, e.g. by substracting health points, or by playing a sound. To specify how the game responds to physics events, you use Physics Listeners. Do not overuse physics nodes. Although the physics nodes are put to “sleep” when they are not moved, creating a world solely out of dynamic physics nodes will quickly bring you to the limits of your computer's capabilities. You can use normal non-physical Nodes in the same scene next to physical ones. Use the non-physical ones for non-solid things for which you do not want to detect collisions (ghost, foliage, plants, effects, …). This improves performance. If you get weird behaviour, such as physical nodes jittering wildy and being ejected for no apparent reason, it usually means you have created an impossible state. Verify that none of the collision shapes overlap. This can happen when you create physical nodes in positions that are too close to other nodes; or if you position a physical node using setLocalTranslation() and it touches another node's collision shape. For large static meshes like shooter levels or terrain its best to divide the mesh into multiple physics objects to allow the less cpu intense broadphase to filter out most collision items.