The User s Touring Sprite


The Basic 3D Sprite

Sprite3D is the base class for creating 3D sprites. The TourSprite subclass is used to create the user's robot sprite, and AlienSprite is a subclass of TourSprite for the alien hand.

TourSprite is controlled by TouristControls, which monitors user key presses and can adjust the sprite's position or the user's viewpoint as needed. AlienSprite is periodically updated by the TimeBehavior class to make the alien hand chase the user's sprite.

Figure 18-6 shows the public methods of the Sprite3D and Behavior classes and the relationships between them.

Figure 18-6. Sprite3D and Behavior classes


Sprite3D represents a model able to move about the XZ plane, rotate around the y-axis, and detect obstacles and floor boundaries. The sprite can be made inactive, which will cause it to disappear from the scene.

The constructor for Sprite3D utilizes PropManager to load the model representing the sprite. It then adds a Switch node and transformGroup above the model's graph. The result can be seen in the branches for the robot (Coolrobo.3ds) and alien hand (hand1.obj) in Figure 18-4. Here is the relevant code:

     PropManager propMan = new PropManager(fnm, true);     visSwitch = new Switch( );   // for sprite visibility     visSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);     visSwitch.addChild( propMan.getTG( ) );     // add obj to switch     visSwitch.setWhichChild( Switch.CHILD_ALL );   // make visible     objectTG = new TransformGroup( );  // for sprite moves     objectTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);     objectTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);     objectTG.addChild( visSwitch );   // add switch to TG

The objectTG node is made available to the application's scene via getTG( ), which is called by WrapTour3D.

Visibility

The Switch node is used to control the image branch's visibility. This is done in the setActive( ) method of Sprite3D:

     public void setActive(boolean b)     { isActive = b;       if (!isActive)          visSwitch.setWhichChild( Switch.CHILD_NONE ); //make invisible       else if (isActive)         visSwitch.setWhichChild( Switch.CHILD_ALL );   // make visible     }

In Java 3D, the visibility of a model can be controlled in at least three ways:

  • Use setWhichChild( ) on a Switch above the model (as here).

  • Use setVisible( ) on the model's rendering attributes.

  • Use Java 3D TRansparencyAttributes, as detailed in the LoaderInfo3D application in the section "Making a Shape Almost Transparent" in Chapter 16.

The overhead of manipulating rendering or transparency attributes can be high and will continue to produce an overhead during rendering. A Switch node placed above the model in the scene graph means that rendering doesn't need to visit the model at all when the Switch node is set to CHILD_NONE, a clear gain in efficiency.

Another advantage of Switch is it can be placed above Group nodes to control the visibility of subgraphs in the scene. The subgraph might be a group of shapes (e.g., a group of soldiers) who should all disappear at the same time when zapped by a laser. Attribute approaches only apply to individual Shape3D nodes.

Movement and Rotation

The addition of another transformGroup to a model's scene branch (labeled as TG in Figure 18-4) is for coding simplicity. It means that a Sprite3D object can be moved and rotated without the code having to delve into the graph structure returned by PropManager's getTG( ).

A sprite can be moved with setPosition( ) and moveBy( ) and can be rotated with doRotateY( ). These methods affect the objectTG TransformGroup, which corresponds to the TG node above each model in Figure 18-4.

The movement and rotation methods affect the same transformGroup so that rotations will influence movement. For example, when a sprite moves "forward," it will move forward according to the direction it is currently facing. In other words, the sprite's rotation affects its movement. doMove( ) makes the sprite move by the distance specified in a vector:

     private void doMove(Vector3d theMove)     // move the sprite by the amount in theMove     {       objectTG.getTransform( t3d );       toMove.setTranslation(theMove);    // overwrite previous trans       t3d.mul(toMove);       objectTG.setTransform(t3d);     }

The transform3D objects t3d and toMove are declared globally and created in the constructor of Sprite3D for efficiency reasons. The alternative would be to create a transform3D object inside doMove( ) each time it was called. This would generate a lot of temporary objects over time, which would need to be garbage collected, causing the application to slow down while the objects were removed by the JVM.

doRotateY( ) is similar to doMove( ) and uses another global transform3D object called toRot:

     public void doRotateY(double radians)     // rotate the sprite by radians around its y-axis     {       objectTG.getTransform( t3d );       toRot.rotY(radians);   // overwrite previous rotation       t3d.mul(toRot);       objectTG.setTransform(t3d);     }

Obstacle and Boundary Detection

The sprite should not pass through obstacles or move off the floor. This behavior is achieved by utilizing a Obstacles object, called obs. A reference to obs is passed into the sprite at creation time and used in moveBy( ). moveBy( ) is the public movement method for the sprite and accepts a (x, z) step:

     public boolean moveBy(double x, double z)     // Move the sprite by offsets x and z, but only if within     // the floor and there is no obstacle nearby.     {       if (isActive( )) {         Point3d nextLoc = tryMove(new Vector3d(x, 0, z));         if (obs.nearObstacle(nextLoc, radius*OBS_FACTOR))  // ask Obstacles object           return false;         else {           doMove( new Vector3d(x,0,z) );           return true;         }       }       else  // not active         return false;     }

moveBy( ) calculates its next position by calling tryMove( ), which is almost the same as doMove( ) except that it does not adjust the position of objectTG. The possible new location, nextLoc, is passed to Obstacles's nearObstacle( ) for testing. If the new location is acceptable, the step will be made by calling doMove( ).

This approach nicely separates the issues of obstacle and boundary detection from the sprite, placing them in the Obstacles class. Another aim was to implement this form of collision detection without utilizing features in Java 3D.

Java 3D can be employed for collision detection in two main ways:

  • Java 3D can generate an event when one shape intersects with another, which is processed by a Behavior object. The drawback is that such events only occur once the shapes have intersected. What is required is an event just before the shapes intersect.

  • Java 3D picking can query whether moving the user's viewpoint will result in a collision with an object in the scene. Picking is a technique for selecting shapes inside the scene by shooting a line (or cone) from the viewer's location, through the mouse position, into the scene. When the line intersects with a shape in the scene, the shape's been picked. This approach is suitable for first-person games where the viewpoint represents the player. Tour3D is the beginnings of a third-person game, where the viewer is distinct from the player (the robot). I'll return to this picky question when I look at first-person games in Chapter 24.

Updating the Sprite

A comparison of Sprite (the 2D sprite class from Chapter 11) and Sprite3D highlights an important difference between the 2D and 3D games programming styles. The 2D games all use an update redraw cycle, with timer calculations to control the cycle's frequency. Sprite3D has no redraw method, and no explicit timer control of its redraw rate.

The difference is due to the high-level nature of Java 3D's scene graph. Java 3D controls graph rendering, so it handles redraws, including their frequency. At the programming level, you only have to change the scene (e.g., by adjusting the objectTG node) and let Java 3D do its thing. If you do want direct control, you can switch from Java 3D's default retained mode to immediate mode, but I won't explore that approach here. Immediate mode allows the programmer to specify when a scene should be rendered, but it's also the programmer's responsibility to manage the rendering scene data, which is a considerable amount of work.



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