Time for action – listeners meet actions

Look at the inner methods of the InputListener objects:

  • onAction(String name, boolean isPressed, float tpf) in actionListener
  • onAnalog(String name, float intensity, float tpf) in analogListener
  • onTouch(String name, TouchEvent event, float tpf) in touchListener (not depicted)

When you implement the actual actions, you write a series of conditionals in these inner methods. When you want to detect several triggers (the most common scenario), add several else if conditionals. You test for each mapping by name, and then execute the desired action. In our example, we want the action to affect the blue cube (geom).

  1. Make geom accessible as a class field. Remember to adjust the geom object's constructor call in simpleInitApp() method accordingly.
    private Geometry geom;
    ...
    geom = new Geometry("Box", mesh);

    Now the InputListener objects in your class have access to the cube geometry.

  2. Let's handle MAPPING_COLOR first. In the inner onAction() method of the actionListener object, test whether name equals to MAPPING_COLOR. To execute the action when the trigger is released (that is, the key or mouse button is up again), test whether the Boolean is !isPressed:
    private ActionListener actionListener = new ActionListener() {
        public void onAction(String name, boolean isPressed, float tpf) {
            if (name.equals(MAPPING_COLOR) && !isPressed) {
                // implement action here
            } 
        }
    };
  3. Implement the color toggle action for geom: get the cube's material, and set the Color property using the return value of the static randomColor() method. Replace the implement action here comment with the following line:
    geom.getMaterial().setColor("Color", ColorRGBA.randomColor());
  4. Let's handle MAPPING_ROTATE. In the inner onAnalog() method of the analogListener, test whether name equals MAPPING_ROTATE.
    private AnalogListener analogListener = new AnalogListener() {
      public void onAnalog(String name, float intensity, float tpf) {
        if (name.equals(MAPPING_ROTATE)) {
            // implement action here
        } 
      }
    };
  5. Implement the rotation action for geom. To execute the action continuously as long as the trigger is pressed, use the provided intensity value as a factor in your continuous rotation action. Replace the implement action here comment with the following line:
     geom.rotate(0, intensity, 0); // rotate around Y axis

When you run UserInput.java now, you see the blue cube. Press the Space bar and the cube changes its color. Keep the left mouse button pressed and the cube rotates around its y axis. You can even do both at the same time. When you press the C key, however, you notice that the color changes, but the application also prints camera information to the console. Strange! Didn't you just declare the C key as an alternative to the Space bar? You did, but you did not consider the existing default mappings.

SimpleApplication internally maps the C key to a diagnostic output action. You will notice a similar issue if you map anything to the M (prints memory diagnostics) or Esc keys (stops the game), which are also internally registered by SimpleApplication. If necessary, you can remove any of the three existing mappings as follows:

inputManager.deleteMapping(INPUT_MAPPING_EXIT);       // Key_ESCAPE
inputManager.deleteMapping(INPUT_MAPPING_CAMERA_POS); // Key_C
inputManager.deleteMapping(INPUT_MAPPING_MEMORY);     // Key_M
Tip

You can call the inputManager.clearMappings() method and define all mappings from scratch. But clearing also removes the preconfigured W, A, S, and D key navigation, which we would like to keep as long as we're looking at examples. So don't clear the mappings for now—until you develop your own game.

What just happened?

Congrats! You now know how to set up individual keys and mouse buttons, and so on to trigger custom game actions that change the game state.

  1. Start by deciding on a list of actions and default triggers in your game.
  2. Define triggers and give them unique names that describe the action, not the key.
  3. Register each name—trigger pair as a mapping to the inputManager object.
  4. Create InputListener instances for discrete and analog events, and register each mapping to one of them.
  5. Finally, test for each mapping in its InputListener object's onAction(), onAnalog(), or onTouch() method, and implement its action.