
Time for action – pick a brick (using crosshairs)
To learn about picking a target in the scene, let's add a second cube to the scene. Again we want to click to rotate a cube, but this time, we want to pick the cube that will be the target of the action.
- Make a copy of the previous exercise,
UserInput.java
. Keep the mouse click and the key press actions for inspiration. - Rename the copy of the class to
TargetPickCenter.java
. Remember to also refactor the first line of themain()
method to the following:TargetPickCenter app = new TargetPickCenter();.
- Let's write a simple cube generator so that we can generate sample content more easily: move the code block that creates the blue cube from the
simpleInitApp()
method into a custom method calledmyCube()
. Turn theBox mesh
object into a static class field so that you can reuse it. Your method should use three arguments:String name
,Vector3f loc
, andColorRGBA
color. The method should return a new colored and named cubeGeometry
at the specified location.private static Box mesh = new Box(Vector3f.ZERO, 1, 1, 1); public Geometry myBox(String name, Vector3f loc, ColorRGBA color) { Geometry geom = new Geometry(name, mesh); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.setColor("Color", color); geom.setMaterial(mat); geom.setLocalTranslation(loc); return geom; }
- Use your
myBox()
method to attach two cubes to therootNode
object, a red one and a blue one. Space them apart a little bit so that they don't overlap sitting at the origin: move the red one up 1.5f WU, and the blue one down 1.5f WU, along the y axis.rootNode.attachChild(myBox("Red Cube", new Vector3f(0, 1.5f, 0), ColorRGBA.Red)); rootNode.attachChild(myBox("Blue Cube", new Vector3f(0, -1.5f, 0), ColorRGBA.Blue));
- Reuse the class constants
MAPPING_ROTATE
andTRIGGER_ROTATE
that you created previously, and register the left-click mapping in thesimpleInitApp()
method as before.private static final String MAPPING_ROTATE = "Rotate"; private static final Trigger TRIGGER_ROTATE = new MouseButtonTrigger(MouseInput.BUTTON_LEFT); … public void simpleInitApp() { inputManager.addMapping(MAPPING_ROTATE, TRIGGER_ROTATE); inputManager.addListener(analogListener, new String[]{MAPPING_ROTATE});
- To make aiming easier, let's mark the center of the screen with a little white cube. Since a mark is 2D, we attach it to the 2D user interface (
guiNode
), and not to the 3D scene (rootNode
)! Call theattachCenterMark()
method from thesimpleInitApp()
method.private void attachCenterMark() { Geometry c = myBox("center mark", Vector3f.ZERO, ColorRGBA.White); c.scale(4); c.setLocalTranslation( settings.getWidth()/2, settings.getHeight()/2, 0 ); guiNode.attachChild(c); // attach to 2D user interface }
Run the TargetPickCenter
method to see the intermediate result, the red cube above the blue cube.
What just happened?
Whenever you need to mass produce geometries, consider writing a convenience method, such as the myBox()
method with parameters to decrease clutter in the simpleInitApp()
method.
Between the two cubes you see a tiny white cube—this is our center mark. When you look around by moving the mouse, or navigate to the sides by pressing the A or D keys, you notice that the center mark stays put. This mark is not attached to the rootNode
object—and, therefore, is not part of the projected 3D scene. It is attached to a special guiNode
object, and is therefore part of the 2D graphical user interface (GUI). Just as with the rootNode
object, you inherited the guiNode
object from the SimpleApplication
class.
We added the center mark because you may have noticed that the mouse pointer is invisible in a running game. It would be hard to click and select a target without some visual feedback. For a sample application, our white mark is enough—in a real game, you would attach a crosshairs graphic of higher artistic value.