Tiling the Floor


Managing the Maze

MazeManager reads in a maze plan and generates two maze representations: a 3D subgraph added to the scene by WrapMaze3D and a 2D image employed by BirdsEye as the background for its moving arrow. The primary aim of MazeManager is to hide the processing of the maze plan from the rest of the application. In addition to serving up 3D and 2D mazes, MazeManager contains the coordinates of the user's starting point and deals with collision detection between the player and the maze walls. MazeManager's readFile( ) initializes maze[][] by reading in the ASCII maze plan, and buildMazeReps( ) creates the on-screen mazes.

When buildMazeReps( ) creates the 3D maze, it has to translate the indices in maze[][] into (x, y, z) coordinates for the blocks and cylinders. The first assumption is that every block and cylinder is standing on the floor, so their y-coordinates are 0. Then, the row's indices of maze[][] are treated as z-axis values and the columns as x-axis values. For example, if maze[3][5] contains a b, then a block will be placed at (5, 0, 3) in the scene.

This approach means that the top-left corner of the maze is located at (0, 0, 0) in the scene, and the rest of the maze extends over the positive XZ quadrant.

When buildMazeReps( ) generates the 2D image, it continues to treat the columns of maze[][] as x-axis numbers, but the rows are viewed as y-axis values. For example, if maze[3][5] contains a b, then a blue square will be drawn at the coordinate (5, 3) in the image.

The 3D scene is a single BranchGroup with transformGroups hanging off it. There's one TRansformGroup for each block (Box node) and cylinder (Cylinder node) to place the shape at its given coordinate. A transformGroup is created with a call to makeObs( ):

     private TransformGroup makeObs(char ch,int x,int z,Appearance app)     // place an obstacle (block/cylinder) at (x,z) with appearance app     {       Primitive obs;       if (ch == 'b')  // blue textured block         obs = new Box(RADIUS, HEIGHT/2, RADIUS,                             Primitive.GENERATE_TEXTURE_COORDS |                             Primitive.GENERATE_NORMALS, app );       else   // green textured cylinder         obs = new Cylinder(RADIUS, HEIGHT,                             Primitive.GENERATE_TEXTURE_COORDS |                             Primitive.GENERATE_NORMALS, app );           // position obstacle so its base is resting on the floor at (x,z)       TransformGroup posnTG = new TransformGroup(  );       Transform3D trans = new Transform3D(  );       trans.setTranslation( new Vector3d(x, HEIGHT/2, z) );  // move up       posnTG.setTransform(trans);       posnTG.addChild(obs);       return posnTG;     } 

The overhead of using textured surfaces is reduced by reusing two precalculated Appearance nodesone for the blocks, one for the cylinderscreated with calls to makeApp( ):

     private Appearance makeApp(Color3f colObs, String texFnm)     {       Appearance app = new Appearance(  );           // mix the texture and the material color       TextureAttributes ta = new TextureAttributes(  );       ta.setTextureMode(TextureAttributes.MODULATE);       app.setTextureAttributes(ta);           // load and set the texture       System.out.println("Loading obstacle texture from " + texFnm);       TextureLoader loader = new TextureLoader(texFnm, null);       Texture2D texture = (Texture2D) loader.getTexture(  );       app.setTexture(texture);      // set the texture           // add a colored material       Material mat = new Material(colObs,black,colObs,specular,20.f);       mat.setLightingEnable(true);       app.setMaterial(mat);       return app;     } 

The Appearance modulates the texture and material and switches on lighting effects.


The intention is that the scene will be poorly lit except for a spotlight "held" by the user. The efficacy of the spot depends on its own parameters, such as its attenuation and concentration, and on the shininess and specular color of the objects, which are set in the Material node component.

Specular color is the color of a shape when it reflects light from shiny areas. Usually the specular color is white, as in this example.


The 2D image is initialized as a BufferedImage:

     BufferedImage mazeImg =           new BufferedImage(IMAGE_LEN, IMAGE_LEN, BufferedImage.TYPE_INT_ARGB); 

The drawing operations are applied via a graphics context:

     Graphics g = (Graphics) mazeImg.createGraphics(  );             drawBlock(g, x, z);      // for a block at (x,y)     // other drawing operations...         drawCylinder(g, x, z);   // for a cylinder at (x,y)             g.dispose(  );   // when drawing is completed 

drawBlock( ) and drawCylinder( ) are quite simple:

     private void drawBlock(Graphics g, int i, int j)     // draw a blue box in the 2D image     { g.setColor(Color.blue);       g.fillRect(i*IMAGE_STEP, j*IMAGE_STEP, IMAGE_STEP, IMAGE_STEP);     }         private void drawCylinder(Graphics g, int i, int j)     // draw a green circle in the 2D image     { g.setColor(Color.green);       g.fillOval(i*IMAGE_STEP, j*IMAGE_STEP, IMAGE_STEP, IMAGE_STEP);     } 

IMAGE_STEP is the number of pixels in the 2D image corresponding to a maze cell's dimensions.


Collision Detection

Collision detection is a matter of testing a supplied (x, z) pair against the maze[z][x] cell to see if it contains a b or c. Coordinates beyond the maze's extent must be dealt with:

     public boolean canMoveTo(double xWorld, double zWorld)     // is (xWorld, zWorld) free of obstacles?     // Called by the KeyBehavior object to test a possible move.     {       int x = (int) Math.round(xWorld);       int z = (int) Math.round(zWorld);           if ((x < 0) || (x >= LEN) || (z < 0) || (z >= LEN))         return true;    // since outside the possible maze dimensions           if ((maze[z][x] == 'b') || (maze[z][x] == 'c'))         return false;    // since loc occupied by block or cylinder           return true;     }  // end of canMoveTo(  ) 

The supplied coordinates should be integer values since the user only moves in one-unit steps and rotates by 90 degrees. However, Java 3D transforms utilize floats or doubles, so the coordinates will never be whole, which means they must be rounded before use.



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