Chapter 26. Fractal Land


The Bird's-Eye View

The BirdsEye object displays a static image representing the maze as seen from above, and it draws an arrow on top of it to show the user's current position. As the user moves and turns, so does the arrow. If the user hits a wall, then the message "BANG!" appears (see Figure 25-12).

Figure 25-12. The bird's-eye view pane


The arrow switches between four different images, shown in Figure 25-13.

Figure 25-13. Arrows as a clock


Figure 25-13 shows the arrows laid out in a circle, labeled with their filenames and numbers (e.g., the down arrow is in arrowFwd.gif and is assigned number 0). The numbering scheme is used to quickly switch between the different arrows when the viewpoint is rotated.

These images are loaded into arrowIms[] at start time, indexed by the numbers shown in Figure 25-13. For instance, arrowIms[0] contains the image from arrowFwd.gif:

     private static final int NUM_DIRS = 4;         private static final int FORWARD = 0;     private static final int LEFT = 1;     private static final int BACK = 2;     private static final int RIGHT = 3;         Image[] arrowIms = new Image[NUM_DIRS];         arrowIms[FORWARD] = new ImageIcon("images/arrowFwd.gif").getImage(  );     arrowIms[LEFT] = new ImageIcon("images/arrowLeft.gif").getImage(  );     arrowIms[BACK] = new ImageIcon("images/arrowBack.gif").getImage(  );     arrowIms[RIGHT] = new ImageIcon("images/arrowRight.gif").getImage(  ); 

The images are ordered in the array so moves and rotations can be calculated using clock arithmetic, as explained below.

moves[] is another important array. It contains the distance offset when the user moves forward, left, backward, or right:

     private Point moves[];  // global             private void initMoves(  )     { moves = new Point[NUM_DIRS];       step = mm.getImageStep(  );       moves[FORWARD] = new Point(0, step);  // move downwards on-screen       moves[LEFT] = new Point(step, 0);     // right on-screen       moves[BACK] = new Point(0, -step);    // up on-screen       moves[RIGHT] = new Point(-step, 0);   // left on-screen     } 

moves[] stores the offsets in the same order as arrowIms[].

The user's arrow starts at the maze's starting point. The starting direction is along the positive z-axis, which is down the screen when viewed from above in BirdsEye. This information is stored in initPosition( ):

     private void initPosition(  )     { currPosn = mm.getImageStartPosn(  );       compass = FORWARD;       userIm = arrowIms[FORWARD];           // the user's arrow starts by facing down the screen       showBang = false;     } 

currPosn is a Point object holding the current (x, y) position of the arrow. compass is the current heading for the user and an index into the arrowIms[] to get the current arrow image.

Moving the Arrow

KeyBehavior calls setMove( ) in BirdsEye to move the arrow and supplies a direction that matches the FORWARD, LEFT, BACK or RIGHT constants defined above. Before the move is made, the actual heading is calculated as the current compass value plus the direction, modulo 4:

     public void setMove(int dir)     {       int actualHd = (compass + dir) % NUM_DIRS;       Point move = moves[actualHd];       currPosn.x += move.x;  // update user position       currPosn.y += move.y;       repaint(  );     } 

For example, if the compass value is LEFT (1) and the direction is BACK (2), then the actual heading will be RIGHT (1 + 2 = 3). In other words, if the users move backward when they're pointing left, then they will move to the right.

After the current position (currPosn) is updated, a repaint is requested, which causes paintComponent( ) to be called. It draws the maze image first, the arrow at the current position, and finally the "BANG!" text if necessary.

Rotating the Arrow

KeyBehavior calls setRotation( ) in BirdsEye to rotate the arrow and supplies a LEFT or RIGHT value. As with a move, the heading must be calculated, and this becomes the new compass value and changes the user's arrow:

     public void setRotation(int dir)     {       compass = (compass + dir) % NUM_DIRS;       userIm = arrowIms[compass];  // update user's arrow       repaint(  );     } 

For example, if the compass value is LEFT (1) and the rotation direction is RIGHT (3), then the compass will change to FORWARD (1 + 3 = 4, which becomes 0 when divided modulo 4). This means that when the arrow is pointing left and the user turns right, then the new heading is forward.

The movement and rotation code in BirdsEye is tricky but would be worse if the user could rotate through a wider range of angles. The additional complexity would arise from the need to calculate more distance offsets, which would translate into a larger moves[] array and more images in arrowIms[].

Why Not Use a Bird's-Eye Camera?

An alternative to the schematic view offered by BirdsEye is to create a third camera in a similar way to SecondViewPanel but looking down on the maze from above. This was my original approach, but it proved unsatisfactory. It was almost impossible to see the user's avatar (the cone) due to the darkness of the scene, the texturing on the floor, and the camera's height above the scene. If the camera was moved nearer the ground, then less of the maze would have been visible.

It's useful to present an abstracted picture of the entire scene, which leaves out unnecessary detail such as textures and parts of the scenery. This helps the players maintain a general idea of the game without overloading them with information. Invariably, this abstraction requires a different kind of modeling than just another Java 3D pane.

Having detailed views and overviews in a game suggests that essential scene information (e.g., the maze plan) should be managed by an object that can supply alternative representations for these views. This is the role played by MazeManager.



Killer Game Programming in Java
Killer Game Programming in Java
ISBN: 0596007302
EAN: 2147483647
Year: 2006
Pages: 340

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net