Managing the WorldWorldDisplay manages:
The communication between the player and sprites in the game is rudimentary, mainly involving the transmission of position information and the number of pickups left. However, the coding technique of passing this information through the WorldDisplay is a useful one since it allows WorldDisplay to monitor and control the interactions between the sprites. WorldDisplay utilizes three main data structures:
These are simply declared as variables in the class:
WorldDisplay's methods fall into five main groups, which I'll consider in detail in the following subsections:
Loading Floor InformationThe floor image is a single GIF, so additional information must state where the odd and even tile rows are located and give the dimensions for a tile (a diamond). These details are shown in Figure 13-9. Figure 13-9. Floor informationThe relevant information is stored in worldInfo.txt in the World/ subdirectory and read in by loadWorldInfo( ). The file contains the following:
The image used is surface.gif, which should be in the Images/ subdirectory below the AlienTiles/ directory. There are 16 columns of tiles, and 23 rows. Each tile is 56 pixels wide, at its widest point, and 29 pixels high. The first even row (row 0) starts at pixel coordinate (12,8), the first odd row (row 1) at (40,23). The starting point is taken to be the top-left corner of the rectangle that surrounds the diamond. With this information, translating any tile coordinate into a pixel location in the floor image is possible. Storing floor dataThe data read in by loadFloorInfo( ) and its secondary methods are stored in a series of globals in WorldDisplay:
Most of them are used only to initialize the WorldItems object:
The WorldItems object organizes details about the surface entities (blocks, pickups, and sprites) by tile row to ensure they are drawn to the JPanel with the correct z-ordering. The floor information is required so an entity's tile coordinates can be translated to pixel locations. Creating obstaclesThe number of tiles on the surface is used to initialize the obstacles[][] array:
Obstacles are registered (i.e., particular cells are set to true) as WorldDisplay loads entity information (see the next section for details). Sprites utilize validTileLoc( ) to check if a particular tile (x, y) can be entered:
Loading World Entity InformationRather than specify the entity positions as constants in the code, the information is read in by loadWorldObjects( ) from the file worldObjs.txt in the subdirectory World/. The data come in three flavorsno-go areas, blocks, and pickupsplaced at a given tile coordinate and unable to move. Sprites aren't included in this category since their position can change during game play. Consequently, worldObjs.txt supports three data formats:
An n is for no-go, followed by multiple lines of (x, y) coordinates defining which tiles are inaccessible. The sequence of coordinates is terminated with a #. A b line starts with a block name, which corresponds to the name of the GIF file for the block, and is followed by a sequence of tile coordinates where the block appears. The name on a p line is mapped to a GIF file name but is followed only by a single coordinate. A pickup is assumed to only appear once on the floor.
Here is a fragment of worldObjs.txt:
As the information is parsed by loadWorldObjects( ) and its helper methods, the obstacles[][] array and the WorldItems objects are passed through the entity details. For instance, in getsBlocksLine( ), the following code fragment is executed when a (x, y) coordinate for a block has been found:
addItem( ) adds information about the block to the WorldItems object. The relevant obstacles[][] cell is set to true. Similar code is executed for a pickup in getPickup( ):
The obstacles[][] array is not modified since a sprite must be able to move to a tile occupied by a pickup (so it can pick it up). BLOCK, PICKUP, and SPRITE are constants used by WorldItems to distinguish between tile entities. Pickup MethodsWorldDisplay offers a range of pickup-related methods used by the sprites. For example, the PlayerSprite object calls removePickup( ) to pick up a named item:
WorldDisplay communicates with its WorldItems object to attempt the removal and decrements of its numPickups counter. If the counter reaches 0, then the player has collected all the pickups and AlienTilesPanel (atPanel) can be told the game is over. Player MethodsThe player sprite and the aliens don't communicate directly; instead, their interaction is handled through WorldDisplay. This allows code in WorldDisplay the potential to modify, add, or delete information. For example, WorldDisplay might not pass the player's exact position to the aliens, thereby making it harder for them to find him. This version of the application doesn't change or limit information transfer, but that sort of behavior could be introduced without much difficulty. One of the more complicated player methods is playerHasMoved( ) called by the PlayerSprite object when it moves to a new tile.
The player passes in a Point object holding its new tile coordinate, as well as the quadrant direction that brought the sprite to the tile. The moveQuad value can be the constant NE, SE, SW, NW, or STILL, which correspond to the four possible compass directions that a sprite can use, plus the no-movement state. The new tile location is passed to the aliens, which can use it to modify their intended destination. The quadrant direction is passed to updateOffsets( ) to change the surface image's offset from the enclosing JPanel. As mentioned earlier, the player sprite doesn't move at all. A careful examination of AlienTiles during execution shows that the sprite always stays at the center of the game's JPanel. The floor image and its contents (blocks, pickups, aliens) move instead. For instance, when the player sprite is instructed to move northwest (the quadrant direction NW), the sprite does nothing, but the floor and its contents shifts southeast. The floor offset is maintained in two globals:
xOffset and yOffset hold the pixel offsets for drawing the top-left corner of the floor image (and its contents) relative to the top-left corner (0,0) of the JPanel, as shown in Figure 13-10. The offsets may have negative values. The offsets are the final part of the mapping required to translate a tile coordinate into an on-screen pixel location. This approach means that a stationary block or pickup, always positioned on the same tile, will be drawn at different places inside the JPanel as the xOffset and yOffset values change. The offsets are adjusted by updateOffsets( ):
Figure 13-10. The floor offset from the JPanel
Drawing the WorldAlienTilesPanel delegates the world drawing task to draw( ) in WorldDisplay:
WorldDisplay draws the floor GIF, suitably offset, but the entities resting on the floor (the blocks, pickups, and sprites) are left to WorldItems to render. During WorldDisplay's loading phase, the WorldItems object is initialized with the locations of the blocks and pickups, but not sprites. The reason is that sprites move about at run time, so they would have to be reordered repeatedly in WorldItems' internal data structures. Instead, whenever the game surface needs to be drawn, the sprites' current positions are recorded temporarily in WorldItems by calling positionSprites( ). After the drawing is completed, the sprite data are deleted with removeSprites( ). This approach simplifies the housekeeping tasks carried out by WorldItems, as you'll soon see. A drawback to this approach, though, is the need for repeated insertions and deletions of sprite information. However, there are only five sprites in AlienTiles, so the overhead isn't excessive.
|
Wednesday, December 30, 2009
Managing the World
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment