A physics simulation is used in games and applications where objects are exposed to physical forces: Think of games like pool billiard and car racing simulators. Massive objects are pulled by gravity, forces cause objects to gain momentum, friction slows them down, solid objects collide and bounce off one another, etc. Action and Adventure games also make use of physics to implement solid obstacles, falling, and jumping.
The jMonkeyEngine3 has built-in support for jBullet Physics (based on Bullet Physics) via the com.jme3.bullet package. This article focuses mostly on the RigidBodyControl, but also introduces you to others.
If you are looking for info on how to respond to physics events such as collisions, 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. You create a Bullet PhysicsSpace in jME3 with a com.jme3.bullet.BulletAppState.
Internally, the updating and syncing of the actual physics objects happens in the following way:
BulletAppState.update())simpleUpdate in main loop, update() in Controls and AppStates)updateLogicalState())Application.render())When you use this physics simulation, values correspond to the following units:
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);
BulletAppState via the ApplicationStateManager:
BulletAppState bas = app.getStateManager().getState(BulletAppState.class);
For each Spatial that you want to be physical:
PhysicsCollisionListener interface to respond to PhysicsCollisionEvents.Let's look at the details:
A CollisionShape 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 CollisionShape from the com.jme3.bullet.collision.shapes package. (Read the tip under "PhysicsControls Code Samples" how to use default CollisionShapes for Boxes and Spheres.)
| Non-Mesh CollisionShape | Usage | Examples |
|---|---|---|
| BoxCollisionShape() | Box-shaped behaviour, does not roll. | Oblong or cubic objects like bricks, crates, furniture. |
| SphereCollisionShape() | Spherical behaviour, can roll. | Compact objects like apples, soccer balls, cannon balls, compact spaceships. |
| CylinderCollisionShape() | Tube-shaped and disc-shaped behaviour, can roll on one side. | Oblong objects like pillars. Disc-shaped objects like wheels, plates. |
| CompoundCollisionShape() | A CompoundCollisionShape allows custom combinations of shapes. Use the addChildShape() method on the compound object to add other shapes to it and position them relative to one another. | A car with wheels (1 box + 4 cylinders), etc. |
| CapsuleCollisionShape() | A built-in compound shape of a vertical cylinder with one sphere at the top and one sphere at the bottom. Typically used with 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. | Persons, animals. |
| SimplexCollisionShape() | A physical point, line, triangle, or rectangle Shape, defined by one to four points. | Guardrails |
| PlaneCollisionShape() | A 2D plane. Very fast. | Flat solid floor or wall. |
All non-mesh CollisionShapes can be used for dynamic, kinematic, as well as static Spatials. (Code samples see below)
| Mesh CollisionShapes | Usage | Examples |
|---|---|---|
| MeshCollisionShape | A mesh-accurate shape for static or kinematic Spatials. Can have complex shapes with openings and appendages. Limitations: Collisions between two mesh-accurate shapes cannot be detected, only non-mesh shapes can collide with this shape. This Shape does not work with dynamic Spatials. | A whole static game level model. |
| HullCollisionShape | A less accurate shape for dynamic Spatials that cannot easily be represented by a CompoundShape. Limitations: The shape is convex (behaves as if you gift-wrapped the object), i.e. openings, appendages, etc, are not individually represented. | A dynamic 3D model. |
| GImpactCollisionShape | A mesh-accurate shape for dynamic Spatials. It uses http://gimpact.sourceforge.net/. Limitations: CPU intensive, use sparingly! We recommend using HullCollisionShape (or CompoundShape) instead to improve performance. Collisions between two mesh-accurate shapes cannot be detected, only non-mesh shapes can collide with this shape. | Complex dynamic objects (like spiders) in Virtual Reality or scientific simulations. |
| HeightFieldCollisionShape | A mesh-accurate shape optimized for static terrains. This shape is much faster than other mesh-accurate shapes. Limitations: Requires heightmap data. Collisions between two mesh-accurate shapes cannot be detected, only non-mesh shapes can collide with this shape. | Static terrains. |
The mesh-accurate shapes can use a CollisionShapeFactory as constructor (code samples see below).
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
MeshCollisionShape level_shape = new MeshCollisionShape(level_geo.getMesh());
HullCollisionShape shape = new HullCollisionShape(katamari_geo.getMesh());
CompoundCollisionShape myComplexShape = CollisionShapeFactory.createMeshShape((Node) myComplexGeometry );
CollisionShape shape = CollisionShapeFactory.createDynamicMeshShape(spaceCraft);
CompoundCollisionShape boxShape = CollisionShapeFactory.createBoxCompoundShape((Node) crate_geo);
SphereCollisionShape sphereShape = new SphereCollisionShape(1.0f);
BulletPhysics are available in jME3 through PhysicsControls classes from the com.jme3.bullet.control package. jME3's PhysicsControl classes directly extend BulletPhysics objects and 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.
| Standard PhysicsControls | Usage | Examples |
|---|---|---|
| RigidBodyControl | The most commonly used PhysicsControl. You can use it for dynamic objects (solid objects that freely affected by collisions, forces, or gravity), for static objects (solid but not affected by any forces), or kinematic objects (remote-controlled solid objects). | Impacting projectiles, moving obstacles like crates, rolling and bouncing balls, elevators, flying aircaft or space ships. Solid immobile floors, walls, static obstacles. |
| GhostControl | Use for collision and intersection detection between physical objects. A GhostControl itself is non-solid and invisible. GhostControl moves with the Spatial it is attached to. Use GhostControls to implement custom game interactions by adding it to a visible Geometry. | A monster's "aggro radius", CharacterControl collisions, motion detectors, photo-electric alarm sensors, poisonous or radioactive perimeters, life-draining ghosts, etc. |
| Special PhysicsControls | Usage | Examples |
|---|---|---|
| VehicleControl PhysicsVehicleWheel | Special Control used for "terrestrial" vehicles with suspension and wheels. | Cars, tanks, hover crafts, ships, motorcycles… |
| CharacterControl | Special Control used for Walking Characters. | Upright walking persons, animals, robots… |
| RagDollControl | Special Control used for collapsing, flailing, or falling characters | Falling persons, animals, robots, "Rag dolls" |
Click the links for details on the special PhysicsControls. This article is about RigidBodyControl.
The PhysicsControl constructors expect a Collision Shape and a mass (a float in kilogram). The most commonly used PhysicsControl is the RigidBodyControl:
RigidBodyControl myThing_phys = new RigidBodyControl( myThing_shape , 123.0f ); // dynamic
RigidBodyControl myDungeon_phys = new RigidBodyControl( myDungeon_shape , 0.0f ); // static
The following creates a box Geometry with the correct default BoxCollisionShape:
Box b = new Box(1,1,1); Geometry box_geo = new Geometry("Box", b); box_geo.addControl(new RigidBodyControl( 1.0f )); // explicit non-zero mass, implicit BoxCollisionShape
The following creates a MeshCollisionShape for a whole loaded (static) scene:
... gameLevel.addControl(new RigidBodyControl(0.0f)); // explicit zero mass, implicit MeshCollisionShape
For each physical Spatial in the scene:
myThing_geo.addControl(myThing_phys);
The PhysicsSpace is an object in BulletAppState that is like a rootNode for Physics Controls.
bulletAppState.getPhysicsSpace().add(myThing_phys); rootNode.attachChild(myThing_geo);
bulletAppState.getPhysicsSpace().remove(myThing_phys); myThing_geo.removeFromParent();
The PhysicsSpace also manages global physics settings. Typically, you can leave the defaults, and you don't need to change the following settings:
bulletAppState.getPhysicsSpace().setAccuracy(1f/60f;);
bulletAppState.getPhysicsSpace().setGravity(new Vector3f(0, -9.81f, 0));
bulletAppState.getPhysicsSpace().setWorldMax(new Vector3f(10000f, 10000f, 10000f)); bulletAppState.getPhysicsSpace().setWorldMin(new Vector3f(-10000f, -10000f, -10000f));
After you have registered, attached, and added everything, you can adjust physical properties or apply forces.
On a RigidBodyControl, you can set the following physical properties.
| RigidBodyControl Method | Property | Examples |
|---|---|---|
| setGravity(new Vector3f(0f,-9.81f,0f)) | You can change the gravity of individual physics objects after they were added to the PhysicsSpace. Gravity is a vector pointing from this Spatial towards the source of gravity. The longer the vector, the stronger is gravity. If gravity is the same absolute direction for all objects (e.g. on a planet surface), set this vector globally on the PhysicsSpace object and not individually. If the center of gravity is relative (e.g. towards a black hole) then setGravity() on each Spatial to constantly adjust the gravity vectors at each tick of their update() loops. | For planet earth: new Vector3f(0f,-9.81f,0f) |
| setMass(1f) | Sets the mass in kilogram. Dynamic objects have masses > 0.0f. Heavy dynamic objects need more force to be moved and light ones move with small amounts of force. Static immobile objects (walls, floors, including buildings and terrains) must have a mass of zero! | Person: 60f, ball: 1.0f Floor: 0.0f (!) |
| setFriction(1f) | Friction. Slippery objects have low friction. The ground has high friction. | Ice, slides: 0.0f Soil, concrete, rock: 1.0f |
| setRestitution(0.0f) | Bounciness. By default objects are not bouncy (0.0f). For a bouncy rubber object set this > 0.0f. This setting has an impact on performance, so use it sparingly. | Brick: 0.0f Rubber ball: 1.0f |
On a RigidBodyControl, you can apply the following physical forces:
| RigidBodyControl Method | Motion |
|---|---|
| setPhysicsLocation() | Positions the objects. Do not use setLocalTranslation() for physical objects. Important: Make certain not to make CollisionShapes overlap when positioning them. |
| setPhysicsRotation() | Rotates the object. Do not use setLocalRotate() for physical objects. |
| setCcdMotionThreshold(0.1f) | The amount of motion in 1 physics tick to trigger the continuous motion detection. Rarely used, but necessary if you need to fiddle with details. |
| setKinematic(true) | By default, RigidBodyControls are dynamic (kinematic=false) and are affected by forces. If you set kinematic=true, the object is no longer affected by forces, but it still affects others. A kinematic is solid, and must have a mass. (See detailed explanation below.) |
All physical objects…
| Property | Static | Kinematic | Dynamic |
|---|---|---|---|
| Examples | Immobile obstacles: Floors, walls, buildings, … | Remote-controlled solid objects: Airships, meteorites, elevators, doors; networked or remote-controlled NPCs; invisible "airhooks" for hinges and joints. | Interactive objects: Rolling balls, movable crates, falling pillars, zero-g space ship… |
| Does it have a mass? | no, 0.0f | yes1), >0.0f | yes, >0.0f |
| How does it move? | never | setLocalTranslation(); | setLinearVelocity(); applyForce(); setWalkDirection(); for CharacterControl |
| How to place in scene? | setPhysicsLocation(); setPhysicsRotation() | setLocalTranslation(); setLocalRotation(); | setPhysicsLocation(); setPhysicsRotation() |
| 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 | no | yes |
| How to activate this behaviour? | setMass(0f); setKinematic(false); | setMass(1f); setKinematic(true); | setMass(1f); setKinematic(false); |
setLocalTranslation() or move(), or by using a MotionPath.
Use the following methods to move dynamic physical 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. |
| applyTorque(…) | Rotate (twist) the object once around its axes, expressed as a Vector3f. |
| 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. |
PhysicsControls also support the following advanced features:
| PhysicsControl Method | Property |
|---|---|
| setCollisionShape(collisionShape) | Changes the collision shape after creation. |
| setCollideWithGroups() setCollisionGroup() addCollideWithGroup(COLLISION_GROUP_01) removeCollideWithGroup(COLLISION_GROUP_01) | Collision Groups are integer bit masks – enums are available in the 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. Use this to improve performance by grouping objects that will never collide in different groups (the the engine saves times because it does not need to check on them). |
| setDamping(float, float) | The first value is the linear threshold and the second the angular. This simulates dampening of forces, for example for underwater scenes. |
| setAngularFactor(1f) | Set the amount of rotation that will be applied. A value of zero will cancel all rotational force outcome. (?) |
| setSleepingThreshold(float,float) | Sets the sleeping thresholds which define when the object gets deactivated to save resources. The first value is the linear threshold and the second the angular. Low values keep the object active when it barely moves (slow precise performance), high values put the object to sleep immediately (imprecise fast performance). (?) |
| setCcdMotionThreshold(0f) | Sets the amount of motion that has to happen in one physics tick to trigger the continuous motion detection. This avoids the problem of fast objects moving through other objects. Set to zero to disable (default). |
| setCcdSweptSphereRadius(.5f) | Bullet does not use the full collision shape for continuous collision detection, insteadit uses a "swept sphere" shape to approximate a motion. Only relevant for fast moving dynamic bodies. (?) |
setApplyPhysicsLocal(true) for an object to make it move relatively to its local physics space. You would do that if you need a physics space that moves with a node (e.g. a spaceship with artificial gravity surrounded by zero-g space). By default, it's set to false, and all movement is relative to the world.
PlaneCollisionShape for flat streets, floors and the outside edge of the scene, if you keep these pieces separate.bulletAppState.getPhysicsSpace().enableDebug(assetManager);