Grouping the Boids


Adding Obstacles

The Obstacles class creates a series of blue cylinders placed at random locations around the XZ plane. The cylinders have a fixed radius, but their heights can vary between 0 and MAX_HEIGHT (8.0f). A cylinder is positioned with a TRansformGroup and then added to a BranchGroup for all the cylinders; the BranchGroup is then retrieved by calling getObsBG( ).

At the same time that a cylinder is being created, a BoundingBox is calculated:

     height = (float)(Math.random(  )*MAX_HEIGHT);     lower = new Point3d( x-RADIUS, 0.0f, z-RADIUS );     upper = new Point3d( x+RADIUS, height, z+RADIUS );     bb = new BoundingBox(lower, upper); 

A boid checks an obstacle's bounding box to avoid colliding with it. The bounding boxes for all the obstacles are added to an ArrayList, which is examined by boids when they call isOverLapping( ). A boid calls isOverLapping( ) with a BoundingSphere object representing its current position:

     public boolean isOverlapping(BoundingSphere bs)     // Does bs overlap any of the BoundingBox obstacles?     { BoundingBox bb;       for (int i=0; i < obsList.size(  ); i++) {         bb = (BoundingBox)obsList.get(i);         if( bb.intersect(bs) )           return true;       }       return false;     } // end of isOverlapping(  ) 

The isOverlapping( ) method sacrifices efficiency for simplicity: the bounding sphere is checked against every obstacle's bounding box in the scene. An obvious improvement would be to order the bounding boxes in some way to reduce the number that needs to be tested. However, this would complicate the code and wouldn't produce much improvement for the small number of obstacles used in my examples.

The boid shape

A boid is represented by a spearhead shape, which is shown from three different directions in Figure 22-5. A prey boid has an orange body with a purple nose, while predators are completely yellow.

Figure 22-5. CAD sketches for a boid shape


Even with something as simple as this shape, doing a preliminary sketch before resorting to programming is useful. I usually draw a simple side/front/top CAD-style diagram, as in Figure 22-5. The points in the diagrams (p0, p1, etc.) will become the points in the resulting GeometryArray.

Different sides of the shape reuse the same points, which suggests that Java 3D's IndexedGeometry (or a subclass) should represent the shape. IndexedGeometry allows sides to be specified by indices into an array of points, which means fewer points are needed when creating the shape.

The spearhead is made up of four triangles, so my BoidShape class uses a Java 3D IndexedTriangleArray:

     IndexedTriangleArray plane =  new IndexedTriangleArray(NUM_VERTS,                                               GeometryArray.COORDINATES |                                               GeometryArray.COLOR_3, NUM_INDICES); 

The shape is made from four triangles, but the sharing of sides means there's only four different vertices (labeled as p0, p1, p2, and p3 in Figure 22-5). As a consequence, NUM_VERTS is 4 in the code above. Each vertex has an (x, y, z) coordinate, so the IndexedTriangleArray will use 12 indices (4 vertices each require 3 values). Consequently, NUM_INDICES has the value 12.

First, the points are stored in an array, and then the indices of the points array are used to define the sides in another array:

     // the shape's coordinates     Point3f[] pts = new Point3f[NUM_VERTS];     pts[0] = new Point3f(0.0f, 0.0f, 0.25f);     pts[1] = new Point3f(0.2f, 0.0f, -0.25f);     pts[2] = new Point3f(-0.2f, 0.0f, -0.25f);     pts[3] = new Point3f(0.0f, 0.25f, -0.2f);         // anti-clockwise face definition     int[] indices = {        2, 0, 3,      // left face        2, 1, 0,      // bottom face        0, 1, 3,      // right face        1, 2, 3  };   // back face         plane.setCoordinates(0, pts);     plane.setCoordinateIndices(0, indices); 

Some care must be taken to get the ordering of the indices correct. The points for a face must be listed in counterclockwise order for the front of the face to point toward the viewer. This is an example of the "righthand rule" for orienting normals (discussed back in Chapter 16).

The point colors are set in the same way with an array of Java 3D Color3f objects, and an array of indices into that array:

     Color3f[] cols = new Color3f[NUM_VERTS];     cols[0] = purple;   // a purple nose     for (int i=1; i < NUM_VERTS; i++)       cols[i] = col;   // the body color         plane.setColors(0,cols);     plane.setColorIndices(0, indices); 

The array holding the indices (i.e., indices) can be reused, which may allow some graphics cards to do further optimizations on the shape's internal representation.

My boids don't change shape or color though this is quite common in other flocking systems; perhaps the boid gets bigger as it gets older or eats and changes color to indicate a change to its age or health. From a coding perspective, this requires a mechanism for adjusting the coordinate and/or color values inside BoidShape. The safe way to do this, as discussed in the last chapter, is to use a Java 3D GeometryUpdater, maintained as an inner class of BoidShape. The GeometryArray's updateData( ) method would be called when the shape and/or its color had to be changed.



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