# JME 3 Tutorial (9) - Hello Collision

Previous: Hello Picking, Next: Hello Audio

This tutorial demonstrates how you load a scene model and give it solid walls and floors for a character to walk around.

We will use a `PhysicsNode` for the static collidable scene, and a `PhysicsCharacterNode` for the mobile first-person character. We will also adapt the default first-person camera to work with physics-controlled navigation.

The solution shown here can be used for first-person shooters, mazes, and similar games.

## Sample Code

If you don't have it yet,
sample scene.

    jMonkeyProjects$ ls -1 BasicGame
    assets/
    build.xml
    town.zip
    src/

Place town.zip in the root directory of your JME3 project.

    package jme3test.helloworld;

    import com.jme3.app.SimpleApplication;
    import com.jme3.asset.plugins.ZipLocator;
    import com.jme3.bullet.BulletAppState;
    import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
    import com.jme3.bullet.collision.shapes.CollisionShape;
    import com.jme3.bullet.control.CharacterControl;
    import com.jme3.bullet.control.RigidBodyControl;
    import com.jme3.bullet.util.CollisionShapeFactory;
    import com.jme3.input.KeyInput;
    import com.jme3.input.controls.ActionListener;
    import com.jme3.input.controls.KeyTrigger;
    import com.jme3.light.AmbientLight;
    import com.jme3.light.DirectionalLight;
    import com.jme3.math.ColorRGBA;
    import com.jme3.math.Vector3f;
    import com.jme3.scene.Node;
    import com.jme3.scene.Spatial;

    /**
     * Example 9 - How to make walls and floors solid.
     * This version uses Physics and a custom Action Listener.
     * @author normen, with edits by Zathras
     */
    public class HelloCollision extends SimpleApplication
      implements ActionListener {

      private Spatial sceneModel;
      private BulletAppState bulletAppState;
      private RigidBodyControl landscape;
      private CharacterControl player;
      private Vector3f walkDirection = new Vector3f();
      private boolean left = false, right = false, up = false, down = false;

      public static void main(String[] args) {
        HelloCollision app = new HelloCollision();
        app.start();
      }

      public void simpleInitApp() {
        /** Set up Physics */
        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);

        // We re-use the flyby camera for rotation, while positioning is handled by physics
        viewPort.setBackgroundColor(new ColorRGBA(0.7f,0.8f,1f,1f));
        flyCam.setMoveSpeed(100);
        setUpKeys();
        setUpLight();

        // We load the scene from the zip file and adjust its size.
        assetManager.registerLocator("town.zip", ZipLocator.class.getName());
        sceneModel = assetManager.loadModel("main.scene");
        sceneModel.setLocalScale(2f);

        // We set up collision detection for the scene by creating a
        // compound collision shape and a static physics node with mass zero.
        CollisionShape sceneShape =
          CollisionShapeFactory.createMeshShape((Node) sceneModel);
        landscape = new RigidBodyControl(sceneShape, 0);
        sceneModel.addControl(landscape);

        // We set up collision detection for the player by creating
        // a capsule collision shape and a physics character node.
        // The physics character node offers extra settings for
        // size, stepheight, jumping, falling, and gravity.
        // We also put the player in its starting position.
        CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
        player = new CharacterControl(capsuleShape, 0.05f);
        player.setJumpSpeed(20);
        player.setFallSpeed(30);
        player.setGravity(30);
        player.setPhysicsLocation(new Vector3f(0, 10, 0));

        // We attach the scene and the player to the rootnode and the physics space,
        // to make them appear in the game world.
        rootNode.attachChild(sceneModel);
        bulletAppState.getPhysicsSpace().add(landscape);
        bulletAppState.getPhysicsSpace().add(player);
      }

        private void setUpLight() {
            // We add light so we see the scene
            AmbientLight al = new AmbientLight();
            al.setColor(ColorRGBA.White.mult(1.3f));
            rootNode.addLight(al);

            DirectionalLight dl = new DirectionalLight();
            dl.setColor(ColorRGBA.White);
            dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());
            rootNode.addLight(dl);
        }

      /** We over-write some navigational key mappings here, so we can
       * add physics-controlled walking and jumping: */
      private void setUpKeys() {
        inputManager.addMapping("Lefts",  new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("Ups",    new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("Downs",  new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("Jumps",  new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addListener(this, "Lefts");
        inputManager.addListener(this, "Rights");
        inputManager.addListener(this, "Ups");
        inputManager.addListener(this, "Downs");
        inputManager.addListener(this, "Jumps");
      }

      /** These are our custom actions triggered by key presses.
       * We do not walk yet, we just keep track of the direction the user pressed. */
      public void onAction(String binding, boolean value, float tpf) {
        if (binding.equals("Lefts")) {
          left = value;
        } else if (binding.equals("Rights")) {
          right = value;
        } else if (binding.equals("Ups")) {
          up = value;
        } else if (binding.equals("Downs")) {
          down = value;
        } else if (binding.equals("Jumps")) {
          player.jump();
        }
      }

      /**
       * This is the main event loop--walking happens here.
       * We check in which direction the player is walking by interpreting
       * the camera direction forward (camDir) and to the side (camLeft).
       * The setWalkDirection() command is what lets a physics-controlled player walk.
       * We also make sure here that the camera moves with player.
       */
      @Override
      public void simpleUpdate(float tpf) {
        Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
        Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
        walkDirection.set(0, 0, 0);
        if (left)  { walkDirection.addLocal(camLeft); }
        if (right) { walkDirection.addLocal(camLeft.negate()); }
        if (up)    { walkDirection.addLocal(camDir); }
        if (down)  { walkDirection.addLocal(camDir.negate()); }
        player.setWalkDirection(walkDirection);
        cam.setLocation(player.getPhysicsLocation());
      }
    }

Run the sample. You should see a town square with houses and a monument. Use the WASD keys and the mouse to navigate around in first person view. Run forward and jump by pressing W and Space. Note how you step over the sidewalk, and up the steps to the monument. You can walk in the alleys between the houses, but the walls are solid. Don't walk over the edge of the world!

## Understanding the Code

Let's start with the class declaration:

    extends SimpleApplication implements ActionListener

SimpleApplication is the base class for all jME3 games. We also have this class implement the `ActionListener` interface because we want to customize the navigational inputs later.

      private Spatial sceneModel;
      private BulletAppState bulletAppState;
      private RigidBodyControl landscape;
      private CharacterControl player;
      private Vector3f walkDirection = new Vector3f();
      private boolean left = false, right = false, up = false, down = false;

We initialize a few private fields:

- The BulletAppState gives this SimpleApplication access to physics features (such as collision detection) supplied by jME3's jBullet integration
- The Spatial sceneModel is a normal OgreXML model loaded as a Spatial.
- You use the model as the game's landscape by adding a RigidBodyControl to it.
- The (invisible) first-person player is represented by a CharacterControl object.
- The fields `walkDirection` and the four Booleans are used for physics-controlled navigation.

Let's have a look at all the details:

## Initializing the Game

As usual, you initialize the game in the `simpleInitApp()` method.

        viewPort.setBackgroundColor(new ColorRGBA(0.7f,0.8f,1f,1f));
        flyCam.setMoveSpeed(100);
        setUpKeys();
        setUpLight();

We switch the background color from balck to light blue, since this is a scene with a sky. You repurpose the default flyCam camera as first-person camera and set its speed. The auxiliary method `setUpLights()` adds light sources. The auxiliary method `setUpKeys()` configures input mappings–we will look at it later.

### The Physics-Controlled Scene

The first thing you do in every physics game is create a BulletAppState object. It gives you access to jME3's jBullet integration which handles physical forces and collisions.

        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);

For the scene, you load the `sceneModel` from a zip file, and adjust the size.

        assetManager.registerLocator("town.zip", ZipLocator.class.getName());
        sceneModel = assetManager.loadModel("main.scene");
        sceneModel.setLocalScale(2f);

The file `town.zip` is included as a sample model in the JME3 sources – you can
. (Optionally, use any OgreXML scene of your own.) For this sample, place the zip file in the application's top level directory (that is, next to src/, assets/, build.xml).

        CollisionShape sceneShape =
          CollisionShapeFactory.createMeshShape((Node) sceneModel);
        landscape = new RigidBodyControl(sceneShape, 0);
        sceneModel.addControl(landscape);

To use collision detection, you want to add a RigidBodyControl to the `sceneModel` Spatial. The RigidBodyControl for a complex model takes two arguments: A Collision Shape, and the object's mass.

- JME3 offers a `CollisionShapeFactory` that precalculates a mesh-accurate collision shape for a Spatial. We choose to generate a `CompoundCollisionShape`, which has MeshCollisionShapes as its children. Note: This type of collision shape is optimal for immobile objects, such as terrain, houses, and whole shooter levels.
- You set the mass to zero since a scene is static and its mass irrevelant.

Add the control to the Spatial to make give it physical properties. Attach the sceneModel to the rootNode to make it visible.

        rootNode.attachChild(sceneModel);

Tip: Remember to always add a light source so you can see the scene.

### The Physics-Controlled Player

Next you set up collision detection for the first-person player.

        CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);

Again, you create a CollisionShape: This time we choose a CapsuleCollisionShape, a cylinder with a rounded top and bottom. Note: This shape is optimal for a person: It's tall and the roundness helps to get stuck less often on obstacles.

- Supply the CapsuleCollisionShape constructor with the desired size of the bounding capsule to fit the shape of your character. In this example the character is 2*1.5f units wide, and 6f units tall.
- The final integer argument specifies the orientation of the cylinder: 1 is the Y-axis, which fits an upright person. For many animals and vehicles you would use 0 or 2.

        player = new CharacterControl(capsuleShape, 0.05f);

Now you use the CollisionShape to create a `CharacterControl` that represents the player. The last argument of the CharacterControl constructor (here `.05f`) is the size of a step that the character should be able to surmount.

        player.setJumpSpeed(20);
        player.setFallSpeed(30);
        player.setGravity(30);

Apart from step height and character size, the `CharacterControl` lets you configure jumping, falling, and gravity speeds. Adjust the values to fit your game situation.

        player.setPhysicsLocation(new Vector3f(0, 10, 0));

Finally we put the player in its starting position and update its state – remember to use setPhysicsLocation() instead of setLocalTranslation(). since you are dealing with a physical object.

### Activating the PhysicsSpace

As in every JME3 application, you must attach the scene and the player to the `rootNode` to make them appear in the game world.

        rootNode.attachChild(landscape);
        rootNode.attachChild(player);

Remember that for physical games, you must also add all solid objects (usually the characters and the scene) to the `PhysicsSpace`!

        bulletAppState.getPhysicsSpace().add(landscape);
        bulletAppState.getPhysicsSpace().add(player);

## Navigation

The default camera controller `cam` is a third-person camera. JME3 also offers a first-person controller, `flyCam`, which we use here to handle camera rotation.

However we must redefine how walking is handled for physics-controlled objects: When you navigate a physical node, you do not specify a target location, but a walk direction. The physics space calculates how far the character can actually go in the desired direction – or whether it will be stoped by an obstacle.

This is why we must re-define the flyCam's navigational key mappings to use `setWalkDirection()` instead of `setLocalTranslation()`. Here are the steps:

### 1. inputManager

Configure the familiar WASD inputs for walking, and Space for jumping.

        inputManager.addMapping("Lefts",  new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("Ups",    new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("Downs",  new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("Jumps",  new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addListener(this, "Lefts");
        inputManager.addListener(this, "Rights");
        inputManager.addListener(this, "Ups");
        inputManager.addListener(this, "Downs");
        inputManager.addListener(this, "Jumps");

In the code sample above, this block of code was moved into an auxiliary method `setupKeys()` that is called from `simpleInitApp()`–this is just to keep the code more readable.

### 2. onAction()

Remember that we declared this class an `ActionListener` so we could customize the flyCam. The `ActionListener` interface requires us to implement the `onAction()` method: You re-define the actions triggered by navigation key presses to work with physics.

      public void onAction(String binding, boolean value, float tpf) {
        if (binding.equals("Lefts")) {
          left = value;
        } else if (binding.equals("Rights")) {
          right = value;
        } else if (binding.equals("Ups")) {
          up = value;
        } else if (binding.equals("Downs")) {
          down = value;
        } else if (binding.equals("Jumps")) {
          player.jump();
        }
      }

Every time the user presses one of the WASD keys, you keep track of the direction the user wants to go – by storing this info in four directional Booleans. We will use them soon.

Note that no actual walking happens here – not yet!

The only movement that you do not have to implement yourself is the jumping action. The call `player.jump()` is a special method that handles a correct jumping motion for your `PhysicsCharacterNode`.

### 3. setWalkDirection()

In `onAction()` you have determined in which direction the user wants to go in terms of “forward” or “left”. Now you need poll the current rotation of the camera to find to which vectors “forward” and “left” correspond in the coordinate system. This last and most important code snippet goes into the main event loop, `simpleUpdate()`.

        Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
        Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
        walkDirection.set(0, 0, 0);
        if (left)  { walkDirection.addLocal(camLeft); }
        if (right) { walkDirection.addLocal(camLeft.negate()); }
        if (up)    { walkDirection.addLocal(camDir); }
        if (down)  { walkDirection.addLocal(camDir.negate()); }

Reset the variable `walkDirection` to zero. Then add to it all latest motion vectors that you polled from the camera. It is posible for a character to move forward and to the left simultaneously.

    player.setWalkDirection(walkDirection);

This one line does the “walking” magic: Always use `setWalkDirection()` to make a physics-controlled object move continuously, and the physics engine automatically handles collision detection for you!

Important: Do not use `setLocalTranslation()` to walk the player around. You may get it stuck by overlapping with another physical object. You can put the player in a start position with `setPhysicalLocation()` if you make sure to place it a bit above the floor and away from obstacles.

Lastly, do not forget to make the first-person camera object move along with the physics-controlled player node:

        cam.setLocation(player.getPhysicsLocation());

That's it!

## Conclusion

You have learned how to load a “solid” physical scene model and walk around in it with a first-person perspective. You had JME3 calculate the CollisionShapes, and you represented collidables as PhysicsNodes that you registered to the Physics Space. You also made certain to use `player.setWalkDirection(walkDirection)` to move physical characters around.

To learn more about different ways of loading models and scene have a look at Hello Asset,Scene Explorer and Scene Composer

There are also other possible solutions for this task that do not require physics. Have a look at
(and SphereMotionAllowedListener.java). To learn more about complex physics scenes where several mobile physical objects bump into each other, read Hello Physics.

Do you want to hear your player say “ouch!” when he bumps into a wall? Continue with learning how to add sound to your game.

beginner, beginner,, collision, controller, controllers, intro, documentation, model, physics

view online version