Physics with Bullet

7 min read

In this article by Rickard Eden author of jMonkeyEngine 3.0 CookBook we will learn how to use physics in games using different physics engine. This article contains the following recipes:

  • Creating a pushable door
  • Building a rocket engine
  • Ballistic projectiles and arrows
  • Handling multiple gravity sources
  • Self-balancing using RotationalLimitMotors

(For more resources related to this topic, see here.)

Using physics in games has become very common and accessible, thanks to open source physics engines, such as Bullet. jMonkeyEngine supports both the Java-based jBullet and native Bullet in a seamless manner.

jBullet is a Java-based library with JNI bindings to the original Bullet based on C++. jMonkeyEngine is supplied with both of these, and they can be used interchangeably by replacing the libraries in the classpath. No coding change is required. Use jme3-libraries-physics for the implementation of jBullet and jme3-libraries-physics-native for Bullet. In general, Bullet is considered to be faster and is full featured.

Physics can be used for almost anything in games, from tin cans that can be kicked around to character animation systems. In this article, we’ll try to reflect the diversity of these implementations.

Creating a pushable door

Doors are useful in games. Visually, it is more appealing to not have holes in the walls but doors for the players to pass through. Doors can be used to obscure the view and hide what’s behind them for a surprise later. In extension, they can also be used to dynamically hide geometries and increase the performance. There is also a gameplay aspect where doors are used to open new areas to the player and give a sense of progression.

In this recipe, we will create a door that can be opened by pushing it, using a HingeJoint class.

This door consists of the following three elements:

  • Door object: This is a visible object
  • Attachment: This is the fixed end of the joint around which the hinge swings
  • Hinge: This defines how the door should move

Getting ready

Simply following the steps in this recipe won’t give us anything testable. Since the camera has no physics, the door will just sit there and we will have no way to push it. If you have made any of the recipes that use the BetterCharacterControl class, we will already have a suitable test bed for the door. If not, jMonkeyEngine’s TestBetterCharacter example can also be used.

How to do it…

This recipe consists of two sections. The first will deal with the actual creation of the door and the functionality to open it. This will be made in the following six steps:

  1. Create a new RigidBodyControl object called attachment with a small BoxCollisionShape. The CollisionShape should normally be placed inside the wall where the player can’t run into it. It should have a mass of 0, to prevent it from being affected by gravity.
  2. We move it some distance away and add it to the physicsSpace instance, as shown in the following code snippet:

    attachment.setPhysicsLocation(new Vector3f(-5f, 1.52f, 0f)); bulletAppState.getPhysicsSpace().add(attachment);

  3. Now, create a Geometry class called doorGeometry with a Box shape with dimensions that are suitable for a door, as follows:

    Geometry doorGeometry = new Geometry(“Door”, new Box(0.6f, 1.5f, 0.1f));

  4. Similarly, create a RigidBodyControl instance with the same dimensions, that is, 1 in mass; add it as a control to the doorGeometry class first and then add it to physicsSpace of bulletAppState. The following code snippet shows you how to do this:

    RigidBodyControl doorPhysicsBody = new RigidBodyControl(new BoxCollisionShape(new Vector3f(.6f, 1.5f, .1f)), 1);
    bulletAppState.getPhysicsSpace().add(doorPhysicsBody); doorGeometry.addControl(doorPhysicsBody);

  5. Now, we’re going to connect the two with HingeJoint. Create a new HingeJoint instance called joint, as follows:

    new HingeJoint(attachment, doorPhysicsBody, new Vector3f(0f, 0f, 0f),
    new Vector3f(-1f, 0f, 0f), Vector3f.UNIT_Y, Vector3f.UNIT_Y);

  6. Then, we set the limit for the rotation of the door and add it to physicsSpace as follows:

    joint.setLimit(-FastMath.HALF_PI – 0.1f, FastMath.HALF_PI + 0.1f);

Now, we have a door that can be opened by walking into it. It is primitive but effective. Normally, you want doors in games to close after a while. However, here, once it is opened, it remains opened. In order to implement an automatic closing mechanism, perform the following steps:

  1. Create a new class called DoorCloseControl extending AbstractControl.
  2. Add a HingeJoint field called joint along with a setter for it and a float variable called timeOpen.
  3. In the controlUpdate method, we get hingeAngle from HingeJoint and store it in a float variable called angle, as follows:

    float angle = joint.getHingeAngle();

  4. If the angle deviates a bit more from zero, we should increase timeOpen using tpf. Otherwise, timeOpen should be reset to 0, as shown in the following code snippet:

    if(angle > 0.1f || angle < -0.1f) timeOpen += tpf; else timeOpen = 0f;

  5. If timeOpen is more than 5, we begin by checking whether the door is still open. If it is, we define a speed to be the inverse of the angle and enable the door’s motor to make it move in the opposite direction of its angle, as follows:

    if(timeOpen > 5) { float speed = angle > 0 ? -0.9f : 0.9f; joint.enableMotor(true, speed, 0.1f);
    spatial.getControl(RigidBodyControl.class).activate(); }

  6. If timeOpen is less than 5, we should set the speed of the motor to 0:

    joint.enableMotor(true, 0, 1);

  7. Now, we can create a new DoorCloseControl instance in the main class, attach it to the doorGeometry class, and give it the same joint we used previously in the recipe, as follows:

    DoorCloseControl doorControl = new DoorCloseControl(); doorControl.setHingeJoint(joint);

How it works…

The attachment RigidBodyControl has no mass and will thus not be affected by external forces such as gravity. This means it will stick to its place in the world. The door, however, has mass and would fall to the ground if the attachment didn’t keep it up with it.

The HingeJoint class connects the two and defines how they should move in relation to each other. Using Vector3f.UNIT_Y means the rotation will be around the y axis. We set the limit of the joint to be a little more than half PI in each direction. This means it will open almost 100 degrees to either side, allowing the player to step through.

When we try this out, there may be some flickering as the camera passes through the door. To get around this, there are some tweaks that can be applied. We can change the collision shape of the player. Making the collision shape bigger will result in the player hitting the wall before the camera gets close enough to clip through. This has to be done considering other constraints in the physics world.

You can consider changing the near clip distance of the camera. Decreasing it will allow things to get closer to the camera before they are clipped through. This might have implications on the camera’s projection.

One thing that will not work is making the door thicker, since the triangles on the side closest to the player are the ones that are clipped through. Making the door thicker will move them even closer to the player.

In DoorCloseControl, we consider the door to be open if hingeAngle deviates a bit more from 0. We don’t use 0 because we can’t control the exact rotation of the joint. Instead we use a rotational force to move it. This is what we do with joint.enableMotor. Once the door is open for more than five seconds, we tell it to move in the opposite direction. When it’s close to 0, we set the desired movement speed to 0. Simply turning off the motor, in this case, will cause the door to keep moving until it is stopped by an external force.

Once we enable the motor, we also need to call activate() on RigidBodyControl or it will not move.


Please enter your comment!
Please enter your name here