- jMonkeyEngine 3.0 Beginner’s Guide
- Ruth Kusterer
- 780字
- 2025-04-04 22:38:53
Time for action – pick a brick (crosshairs with ray casting)
You want the player to aim and click one of the cubes. You want to identify the selected cube, and make it rotate.
- Start by implementing the
AnalogListener
object on theAnalog()
method to test for our left-click action,MAPPING_ROTATE
. Remember, we chose theAnalogListener
object because rotation is a continuous motion.private AnalogListener analogListener = new AnalogListener() { public void onAnalog(String name, float intensity, float tpf) { if (name.equals(MAPPING_ROTATE)) { // implement action here } } };
- To identify what was clicked, we use methods from the
com.jme3.collision
package. Replace the implement action here comment with the following code; first we create an empty results list.CollisionResults results = new CollisionResults();
- We aim at an invisible line (a so called
com.jme3.math.ray
) straight from the camera location forward in the camera direction.Ray ray = new Ray(cam.getLocation(), cam.getDirection());
- We calculate intersections between this line-of-sight ray and all geometries attached to the
rootNode
object, and collect them in the results list.rootNode.collideWith(ray, results);
- Let's print the intermediate result, so that we can see what is going on. For each hit, we know the distance from the camera, the impact point, and the geometry.
for (int i = 0; i < results.size(); i++) { 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."); }
- If the user has clicked anything, the results list is not empty. In this case we identify the selected geometry; the closest item must be the target that the player picked! If the results list is empty, we just print some feedback to the console.
if (results.size() > 0) { Geometry target = results.getClosestCollision().getGeometry(); // implement action here } else { System.out.println("Selection: Nothing" ); }
- Replace the implement action here comment with the actual code that rotates the cubes around their y axes. You can use the
getName()
action in a conditional statement to identify geometries, and respond differently to each, for example:if (target.getName().equals("Red Cube")) { target.rotate(0, -intensity, 0); // rotate left } else if (target.getName().equals("Blue Cube")) { target.rotate(0, intensity, 0); // rotate right }
Build and run the sample. When you point with the center mark and click either the red or the blue cube, it rotates. You notice that the red cube rotates to the left and the blue cube to the right.
What just happened?
Impressive: your application can now not only feel key presses and clicks coming from the user, but it can even see through the eyes of the user and know what he or she looked at in the scene! In our example, you can now tell the cubes apart, and respond with different actions accordingly: the red one rotates to the left because you used -intensity
(negative), and the blue one to the right because you used intensity
(positive) as rotation factors.
What does seeing through the eyes of the user mean? Mathematically, you aim an invisible line from the camera location forward, in its view direction. A line that has a fixed beginning and direction is called a ray. In the jMonkeyEngine, this corresponds to a com.jme3.math.Ray
object. The ray starts at the 3D coordinates of the camera, which coincides with the center of the screen, the typical location of crosshairs. The ray travels in the view direction through the scene and intersects (collides) with scene objects. The com.jme3.collision
package provides you with specialized methods that detect collisions between various mathematical objects, including between rays and the scene graph. This neat little trick for identifying click targets is called ray casting.

The code sample prints a bit of information so you can see what this picking algorithm is capable of. The CollisionResults
object contains a list of all collisions between two scene elements, and accessors that let you pick the closest (or farthest) intersected object. In the jMonkeyEngine SDK, choose Window | Output | Output to open the console. When you run the application and click on the cubes, the output should look similar to the following:
Selection #0: Red Cube at (0.0, 1.1, 1.0), 9.048468 WU away. Selection #1: Red Cube at (0.0, 1.4, -0.9), 11.090528 WU away. Selection #0: Blue Cube at (0.1, -0.8, 1.0), 9.044685 WU away. Selection #1: Blue Cube at (0.1, -1.0, -1.0), 11.054616 WU away.
Note that each cube is detected twice: the first impact point is on the front side, where the ray enters the geometry, and the second is the exit point on the backside of the geometry. In general, the clicked item is the closest geometry in the results list. Therefore, we use results.getClosestCollision().getGeometry()
to identify the target.
In an actual game, you can use this crosshair-style picking method to implement an attack on an enemy; instead of simply rotating the picked geometry, you could play a gun sound and subtract health points from the identified target. If your conditional identifies the clicked geometry as a door, you could play a creaking sound, and trigger a closing or opening animation on the target, and so on. Your imagination is the limit!