Responding to Key PressesKeyBehavior is similar to the KeyBehavior classes I developed for FPShooter3D in Chapter 24 and for Maze3D in Chapter 25. However, this class is given charge of positioning the viewpoint in initViewPosition( ), which asks the Landscape object for the origin's coordinates: private void initViewPosition(TransformGroup steerTG) // place viewpoint at (0,?,0), facing into scene { Vector3d startPosn = land.getOriginVec( ); // startPosn is (0, <height of floor>, 0) currLandHeight = startPosn.y; // store current floor height startPosn.y += USER_HEIGHT; // add user's height steerTG.getTransform(t3d); // targetTG not yet available t3d.setTranslation(startPosn); // so use steerTG steerTG.setTransform(t3d); } KeyBehaviour needs to know the current floor height to reposition the viewpoint as it moves. The operations carried out by KeyBehavior can be grouped into three categories:
Rotations don't require floor height data, so are implemented as rotations of ViewPlatform's TRansformGroup. Movements up and down are made more efficient by KeyBehavior storing a zOffset counter which records how many upward moves have been made by the user. Consequently, a move down will only be allowed if zOffset is > 0. The efficiency gain exists because there's no need to access floor height information. Movements over the terrain are implemented by a call to moveBy( ), which has three stages:
The moveBy( ) method is: private void moveBy(Vector3d theMove) { Vector3d nextLoc = tryMove(theMove); // next (x,?,z) position if (!land.inLandscape(nextLoc.x, nextLoc.z)) return; // Landscape returns floor height at (x,z) double floorHeight = land.getLandHeight(nextLoc.x, nextLoc.z, currLandHeight); // Calculate the change from the current y-position. // Reset any offset upwards back to 0. double heightChg = floorHeight - currLandHeight - (MOVE_STEP*zOffset); currLandHeight = floorHeight; // update current height zOffset = 0; // back on floor, so no offset Vector3d actualMove = new Vector3d(theMove.x, heightChg, theMove.z); doMove(actualMove); } The method is a little more complicated than the steps above for two reasons. There is a call to inLandscape( ) to check if the proposed move will take the user off the floor, in which case the move is ignored. Second, a move always cancels out any existing upward offset, returning the user to the floor. The actual move is carried out by doMove( ) which applies the translation to ViewPlatform's transformGroup. |