The jMonkeyEngine3 has built-in support for 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.A Collision Shape is a simplified shape for which physics are easier to calculate than for the true shape of the model. This simplication approach speeds up the simulation greatly.
Before you can create a Physics Control, you must create a Collision Shape from the com.jme3.bullet.collision.shapes
package. (Read the tip under "PhysicsControls Code Samples" to learn how to use default CollisionShapes for Boxes and Spheres.)
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 CharacterControls: 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 . 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. |
Tip: 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. Make collision shapes visible by adding the following line after the bulletAppState initialization:
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
Let's look at examples of how to use the CollisionShape 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);
BulletPhysics are available in jME3 through Bullet Physics Controls from the com.jme3.bullet.control package. 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.
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. |
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 any 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 can fall back to the correct default Collision Shape if you do not specify a Collision Shape in the RigidBodyControl constructor. For example, the following creates a box with the correct Box Collision Shape:
Box(1,1,1); myBox.addControl(new RigidBodyControl( 1.0f )); // implicit BoxCollisionShape 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); ...
You remove physical objects from the scene like this:
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 Spatial 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 |
All physical objects…
Property | Static | Dynamic | Kinematic |
---|---|---|---|
Does it have a mass? | no, 0f | yes, >0f | yes, >0f (Inertia is calculated for kinematic objects you need mass to do that) |
How does it move? | never | setWalkDirection(), setLinearVelocity(), applyForce(), etc | setLocalTranslation(), move() |
Can it move and push others? | no | yes | yes |
Is is affected by forces? (Falls when it mid-air? Can be pushed by others?) | no | yes | no |
Examples | Immobile obstacles: Floor, wall, buildings, … | Interactive objects: Soccer ball, movable crate, falling pillar, … | Remote-controlled objects: Airship, meteorites, networked NPCs, invisible "airhooks" for hinges and joints. |
How to activate? | setMass(0f), (By default, objects are not kinematics) | setMass(1f), setKinematic(false) | setMass(1f), setKinematic(true) |
Tip: Typically, Spatials with a kinematic RigidBodyControl are moved programmatically, e.g. using setLocalTranslation() or move() in the update() loop, or by an Animation Path. You can also "hang them up in mid-air" and attach other PhysicsControls to them using hinges and joints.
airhook.setKinematic(true);
Use the following methods to move physics objects.
PhysicsControl Method | Motion |
---|---|
setLinearVelocity(new Vector3f(0f,0f,1f)) | Set the linear speed of this object. |
setAngularVelocity(new Vector3f(0f,0f,1f)) | Set the rotational speed of the object; the x, y and z component are the speed of rotation around that axis. |
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. |
setWalkDirection(new Vector3f(0f,0f,0.1f)) | (CharacterControl only) Make a physical character walk. CharacterControls are locked upright to prevent falling over. Use setWalkDirection(Vector3f.ZERO) to stop a directional motion. |
clearForces() | Cancels out all forces (force, torque) etc and stops the motion. |
Note: It is technically possible to position PhysicsControls 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.
PhysicsControls also supports the following features:
PhysicsControl 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 Collision Group 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. |