Chapter 25. A 3D Maze


Managing a Laser Beam

Each LaserShot object creates and manipulates a subgraph holding a beam (a thin red cylinder) and explosion (an animation laid over a quad). The subgraph shown in Figure 24-6 is created by makeBeam( ).

Figure 24-6. Scenegraph branch for LaserShot


The top-level transformGroup, beamTG, moves the beam (and explosion), and the Switch allows the beam, explosion, or nothing to be displayed. The beamDir TransformGroup initially positions and orients the cylinder so it's pointing into the scene and appears close to the nozzle of the gun image.

The cylinder has a radius of 0.05 units and a height of 0.5; it's rotated by 90 degrees, moved down by 0.3, and into the scene by 0.25 units. This means that the center of its tail is located at (0, -0.3, 0) relative to the coordinate space of beamTG.

The ImagesCsSeries class is almost identical to the ImagesSeries class in Chapter 23. The two differences are:

  • The object doesn't load its own GIFs. Instead, an array of ImageComponent2D objects is passed to it in the constructor.

  • The shape's position is specified with a center point.

The call to ImageCsSeries's constructor is carried out in makeBeam( ):

     // create explosion, centered at (0,0,0), size 2.0f     explShape = new ImageCsSeries( new Point3f(  ), 2.0f, exploIms); 

The explosion is a child of the same transformGroup as the beam (beamTG), so placing its center at (0, 0, 0) will make it appear roughly at the same place in beamTG's coordinate space as the tail of the beam.

The positioning of the beam is something of an art since it depends on creating the illusion that it's coming from the gun, which in turn depends on the way that the gun-in-hand is drawn and placed on screen. Similarly, the positioning of the explosion is governed by where the center of the explosion is drawn in the GIF and by where that center should be relative to the beam at explosion time.


Firing a Beam

AmmoManager calls LaserShot's requestFiring( ) method to utilize the beam. The request will only be accepted if the beam is not in use, which is recorded by setting the inUse Boolean.

If the LaserShot is not in use (inUse == false), then LaserShot will start an AnimBeam thread, and inUse is set to true:

     public boolean requestFiring(  )     { if (inUse)         return false;       else {         inUse = true;         new AnimBeam(this).start(  ); // calls moveBeam(  ) inside a thread         return true;       }     } 

The AnimBeam thread is simple: its sole purpose is to call the moveBeam( ) method back in the LaserShot object. By being called in a thread, the method will be executed without causing the rest of the application (e.g., KeyBehavior, AmmoManager) to wait.

moveBeam( ) incrementally moves the beam (and explosion) forward, starting from the current viewer position (steerTG). If the beam gets close to the target, then the explosion is shown; otherwise, the beam disappears after reaching a certain MAX_RANGE distance from the gun.

inUse is set to false again at the end of the method, allowing the LaserShot object to be used again by AmmoManager:

     public void moveBeam(  )     {       // position the beam at the current viewer position       steerTG.getTransform( tempT3d );       beamTG.setTransform( tempT3d);       showBeam(true);           double currDist = 0.0;       boolean hitTarget = closeToTarget(  );       while ((currDist < MAX_RANGE) && (!hitTarget)) {         doMove(INCR_VEC);         hitTarget = closeToTarget(  );         currDist += STEP;         try {           Thread.sleep(SLEEP_TIME);            }         catch (Exception ex) {}       }           showBeam(false);     // make beam invisible       if (hitTarget)         showExplosion(  );   // if a hit, show explosion       inUse = false;       // shot is finished     } 

The INCR_VEC vector (0, 0, -1) is repeatedly applied to the beam's TRansformGroup, beamTG, by doMove( ) to move the beam away from the viewer's position. This works because the top-level transform for the beam (and explosion), beamTG, is set equal to steerTG before the loop begins, giving it the same starting position and orientation as the viewer.

This means that any movements will be relative to the local coordinate space of the viewer. In particular, the INCR_VEC vector always represents a unit step directly away from the viewpoint, farther into the scene. This is true irrespective of which way the viewpoint is facing in the global coordinate space of the scene. The code for doMove( ) is:

     private void doMove(Vector3d mv)     { beamTG.getTransform( tempT3d );       toMove.setTranslation( mv );       tempT3d.mul(toMove);       beamTG.setTransform( tempT3d );     } 

The doMove and tempT3d references are global to avoid the creation of temporary objects.

closeToTarget( ) does a comparison between the current position of the beam and the position of the target. This is complicated because the beam's location in the scene is affected by beamTG and by its initial transformation with beamDir.

A general-purpose solution is to use getLocalToVworld( ) on the beam shape to retrieve its overall transformation in terms of the global scene coordinates. This requires a capability bit to be set when the shape is created:

     beam.setCapability(Node.ALLOW_LOCAL_TO_VWORLD_READ);  

The closeToTarget( ) method becomes:

     private boolean closeToTarget(  )     /* The beam is close if its current position (currVec)        is a short distance from the target position (targetVec).     */     { beam.getLocalToVworld(localT3d);  // beam's trans in world coords       localT3d.get(currVec);            // get (x,y,z) component           currVec.sub(targetVec);    // calc distance between two positions       double sqLen = currVec.lengthSquared(  );       if (sqLen < HIT_RANGE*HIT_RANGE)         return true;       return false;     } 

The code tests to see if the beam is within HIT_RANGE units of the center of the target.



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