Mouse Picking

Mouse picking means that the user clicks an object in the scene to select it, or to interact with it otherwise. Games use picking to implement aiming and shooting, casting spells, picking up objects, selecting targets, dragging and moving objects, etc. Mouse picking can be done using fixed crosshairs, or using the mouse pointer.

mouse-picking.png

See Input Handling for details on how to define the necessary input triggers, input mappings, and input listeners.

Pick a Target Using Fixed Crosshairs

The following pick target input mapping implements an action that determines what a user clicked. It assumes that the mouse pointer is invisible and there are crosshairs painted in the center of the screen. It assumes that the user aims the crosshairs at an object in the scene and clicks. You use Ray Casting to identify the geometry that was picked by the user. Use this method together with a first-person flyCam.

  1. Activate the first-person camera: flyCam.setEnabled(true);

  2. Keep mouse pointer invisible using inputManager.setCursorVisible(false).

  3. Map the pick target action to a MouseButtonTrigger.

  4. Implement the action in the Listener.

The following example rotates Spatials named “Red Box” or “Blue Box” when they are clicked. Modify this code to do whatever your game needs to do with the identified target (shoot it, take it, move it, etc).

  private AnalogListener analogListener = new AnalogListener() {
    public void onAnalog(String name, float intensity, float tpf) {
        if (name.equals("pick target")) {
         // Reset results list.
         CollisionResults results = new CollisionResults();
         // Aim the ray from camera location in camera direction
         // (assuming crosshairs in center of screen).
         Ray ray = new Ray(cam.getLocation(), cam.getDirection());
         // Collect intersections between ray and all nodes in results list.
         rootNode.collideWith(ray, results);
         // Print the results so we see what is going on
         for (int i = 0; i < results.size(); i++) {
           // For each "hit", we know distance, impact point, geometry.
           float dist = results.getCollision(i).getDistance();
           Vector3f pt = results.getCollision(i).getContactPoint();
           String target = results.getCollision(i).getGeometry().getName();
           System.out.println("Selection #" + i + ": " + target + " at " + pt + ", " + dist + " WU away.");
         }
         // 5. Use the results -- we rotate the selected geometry.
         if (results.size() > 0) {
           // The closest result is the target that the player picked:
           Geometry target = results.getClosestCollision().getGeometry();
           // Here comes the action:
           if(target.getName().equals("Red Box"))
             target.rotate(0, - intensity, 0);
           else if(target.getName().equals("Blue Box"))
             target.rotate(0, intensity, 0);
         }
        } // else if ...
    }
  };

Pick a Target Using the Mouse Pointer

The following pick target input mapping implements an action that determines what a user clicked. It assumes that the mouse pointer is visible, and the user aims the cursor at an object in the scene. You use ray casting to determine the geometry that was picked by the user.

Note: Picking with a visible mouse pointer implies that your application can no longer use the default flyCam where the MouseAxisTrigger rotates the camera. You have to deactivate the flyCam mappings and provide custom mappings. Either different inputs rotate the camera, or the camera is fixed.

  1. Map the pick target action to a MouseButtonTrigger.

  2. Make the mouse pointer visible using inputManager.setCursorVisible(true).

  3. Remap the inputs for camera rotation, or deactivate camera rotation.

  4. Implement the action in the Listener.

The following example rotates Spatials named “Red Box” or “Blue Box” when they are clicked. Modify this code to do whatever your game needs to do with the identified target (shoot it, take it, move it, etc).

private AnalogListener analogListener = new AnalogListener() {
    public void onAnalog(String name, float intensity, float tpf) {
      if (name.equals("pick target")) {
        // Reset results list.
        CollisionResults results = new CollisionResults();
        // Convert screen click to 3d position
        Vector2f click2d = inputManager.getCursorPosition();
        Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone();
        Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();
        // Aim the ray from the clicked spot forwards.
        Ray ray = new Ray(click3d, dir);
        // Collect intersections between ray and all nodes in results list.
        rootNode.collideWith(ray, results);
        // (Print the results so we see what is going on:)
        for (int i = 0; i < results.size(); i++) {
          // (For each "hit", we know distance, impact point, geometry.)
          float dist = results.getCollision(i).getDistance();
          Vector3f pt = results.getCollision(i).getContactPoint();
          String target = results.getCollision(i).getGeometry().getName();
          System.out.println("Selection #" + i + ": " + target + " at " + pt + ", " + dist + " WU away.");
        }
        // Use the results -- we rotate the selected geometry.
        if (results.size() > 0) {
          // The closest result is the target that the player picked:
          Geometry target = results.getClosestCollision().getGeometry();
          // Here comes the action:
          if (target.getName().equals("Red Box")) {
            target.rotate(0, -intensity, 0);
          } else if (target.getName().equals("Blue Box")) {
            target.rotate(0, intensity, 0);
          }
        }
      } // else if ...
    }
  };