The Alien SpriteAlienSprite implements the basic behavior of an alien sprite and is subclassed to create the AlienAStarSprite and AlienQuadSprite classes. AlienSprite is a subclass of TiledSprite. Alien behavior can best be understood by considering the statechart in Figure 13-15. The plan move state is entered by the WorldDisplay object, notifying the alien that the player has moved. This gives it the opportunity to recalculate its current direction or destination, but the precise algorithm will vary from one AlienSprite subclass to another. The other activity is the usual update/draw cycle driven by the animation loop in AlienTilesPanel. The alien tries to hit the player while in the attack state. A successful hit is reported to the WorldDisplay object, and the alien stays where it is. Otherwise, the alien updates its position, in the hope of getting closer to the player. In the draw state, the sprite's tile coordinates are mapped to a pixel location and the sprite's image is rendered. Figure 13-15. Alien statechartResponding to a player's movement is sprite-specific, so playerHasMoved( ) is empty in AlienSprite: public void playerHasMoved(Point playerLoc) { } PlayerLoc contains the current tile coordinates for the PlayerSprite object. Updating the AlienSpriteThe attack, stationary, and move states are encapsulated in update( ): // globals private final static int UPDATE_FREQ = 30; private int updateCounter = 0; public void update( ) { updateCounter = (updateCounter+1)%UPDATE_FREQ; if (updateCounter == 0) { // reduced update frequency if (!hitPlayer( )) move( ); } } update( ) is called from AlienTilesPanel's animation loop, which executes at 40 FPS. This makes the aliens respond too quickly. The solution is to use a counter to reduce the update frequency.
hitPlayer( ) checks if the alien is on the same tile as the player. If it is, then the WorldDisplay object will be informed of a hit: private boolean hitPlayer( ) { Point playerLoc = world.getPlayerLoc( ); if (playerLoc.equals( getTileLoc( ) )) { world.hitByAlien( ); // whack! return true; } return false; } The details of the move state will vary from one alien to another, which translates to the alien subclasses overriding the move( ) method. AlienSprite's move( ) carries out a random walk. getrandDirection( ) (a method inherited from TiledSprite) returns a quadrant, and this is tried out with TiledSprite's tryMove( ) method: protected void move( ) { int quad = getRandDirection( ); Point newPt; while ((newPt = tryMove(quad)) == null) quad = getRandDirection( ); // the loop could repeat for a while, // but it should eventually find a direction setMove(newPt, quad); } The new tile coordinate is use to update the sprite's position in setMove( ): protected void setMove(Point newPt, int quad) { if (world.validTileLoc(newPt.x, newPt.y)) { // should be ok setTileLoc(newPt); if ((quad == NE) || (quad == SE)) setImage("baddieRight"); else if ((quad == SW) || (quad == NW)) setImage("baddieLeft"); else System.out.println("Unknown alien quadrant: " + quad); } else System.out.println("Cannot move alien to (" + newPt.x + ", " + newPt.y + ")"); } // end of doMove( ) setMove( ) double-checks the validity of the new tile and changes the sprite's appearance. The method is protected since only subclasses of AlienSprite will use it (as part of the subclasses' versions of move( )). update( ) handles the attack, stationary, and move states of the alien statechart. This leads to the question: Where is the draw state processed? As with the PlayerSprite class, this task is part of the drawing operation carried out by WorldDisplay through its WorldItems object. |