_Designing Isometric Game Environments_ by Nate Goudie Listing One /******************************************************************** AXOVIEW.H Header file for the Axoview Engine v1.0 Contains engine constants, global data structures, and function prototypes. Written by Nate Goudie 1996 ********************************************************************/ // Defines for Tile Totality Constants #define BASE_COUNT 7 // Number of base tiles #define WALL_COUNT 5 // Number of wall tiles #define PROP_COUNT 3 // Number of prop tiles // Defines for Tile Bitmap Dimension Constants #define BASE_WIDTH 30 // Width of base tile #define BASE_HEIGHT 16 // Height of base tile #define WALL_WIDTH 17 // Width of wall tile #define WALL_HEIGHT 28 // Height of wall tile #define PROP_WIDTH 30 // Width of prop tile #define PROP_HEIGHT 33 // Height of prop tile // Defines for Viewport Dimension Constants #define FULL_SCREEN_SIZE 64000 // Size of a full screen buffer #define VIEWPORT_X_START 0 // Starting X coord of viewport #define VIEWPORT_X_END 319 // Ending X coord of viewport #define VIEWPORT_Y_START 0 // Starting Y coord of viewport #define VIEWPORT_Y_END 199 // Ending Y coord of viewport #define VIEWPORT_WIDTH 320 // Horizontal width of viewport #define VIEWPORT_HEIGHT 200 // Vertical height of viewport // Defines for Map Dimension Constants #define LEVEL_MAX 4 // Maximum number of levels #define XMAP_MAX 50 // Maximum X dimension of map #define YMAP_MAX 50 // Maximum Y dimension of map #define SCROLL_STEP 2 // World coordinate scroll step #define MAX_CELL_UNITS 8 // No. of cell units per map unit #define CELL_COORD_MASK 0x0007 // Cell coordinate mask #define MAP_COORD_MASK 0xfff8 // Map coordinate mask #define X_VPTM_OFFSET 10 // Viewport-to-map X offset #define Y_VPTM_OFFSET 90 // Viewport-to-map Y offset // Defines for Cell Block Dimension Constants #define CELL_START_X 14 // Init X for cell -> screen op #define CELL_START_Y 0 // Init Y for cell -> screen op #define CELL_FULL_WIDTH 32 // X of cellA - X of adj cellB #define CELL_HALF_WIDTH 16 // Half the full width #define CELL_FULL_HEIGHT 16 // Height of base tile #define CELL_HALF_HEIGHT 8 // Y of cellA - Y of next cellB #define XWALL_XOFFSET -1 // X offset for the cell's X-Wall #define YWALL_XOFFSET +14 // X offset for the cell's Y-Wall #define WALL_YOFFSET -20 // Y offset for the cell's walls #define PROP_YOFFSET -18 // Y offset for the prop tile #define LEVEL_ADJUST 10 // Offset to move up/down a level // Defines for view rectification of walls #define START_YS_OFFSET 32 // Initial Y screen offset #define START_XS_OFFSET 0 // Initial X screen offset #define START_YM_OFFSET 2 // Initial Y map offset #define START_XM_OFFSET 2 // Initial X map offset #define START_XADD 16 // Initial Xadd value // Declare global data structures unsigned char palette[256*3]; // array for the color palette // Declare arrays for the tile maps unsigned char base_map[LEVEL_MAX][XMAP_MAX*YMAP_MAX]; unsigned char xwall_map[LEVEL_MAX][XMAP_MAX*YMAP_MAX]; unsigned char ywall_map[LEVEL_MAX][XMAP_MAX*YMAP_MAX]; unsigned char prop_map[LEVEL_MAX][XMAP_MAX*YMAP_MAX]; // Declare instances of the Tile class Tile base_tiles(BASE_COUNT, BASE_WIDTH, BASE_HEIGHT), xwall_tiles(WALL_COUNT, WALL_WIDTH, WALL_HEIGHT), ywall_tiles(WALL_COUNT, WALL_WIDTH, WALL_HEIGHT), prop_tiles(PROP_COUNT, PROP_WIDTH, PROP_HEIGHT); // Function prototypes void draw_tilesRPA(int x, int y, int top_level, int current_level, unsigned char far *screenbuf); int load_files(); Listing Two /******************************************************************** AXOVIEW.CPP The Axoview Engine v1.0. An engine which renders free scrolling tile-based environments in axonometric perspective. Main program and engine functions. Written by Nate Goudie 1996 ********************************************************************/ #include #include #include #include #include #include #include #include "tile.h" #include "screen.h" #include "axoview.h" void main() { clrscr(); // Allocate memory for the offscreen buffer unsigned char far *screenbuf; screenbuf=new unsigned char[FULL_SCREEN_SIZE]; // Get old video mode number int oldmode=*(int *)MK_FP(0x40,0x49); // Create pointer to video memory char far *screen=(char far *)MK_FP(0xa000,0); // Clear video memory ( set each byte to 0 ) memset(screen, 0, FULL_SCREEN_SIZE); // Call function to load files into memory, exit on error if(!load_files()) exit(1); // Call assembly routines to set graphics mode and palette setgmode(0x13); // Set mode to 13h setpalette(palette); // Set VGA palette // Initialize starting variables int bye=0; // Exit program flag int x=0,y=0; // Initial world coord position int tlevel=(LEVEL_MAX-1); // Top level to display int clevel=0; // Current level position // Main rendering loop do { // Clear the offscreen buffer: 1st Step in RPA memset(screenbuf, 0, FULL_SCREEN_SIZE); // Render the axonometric tile display draw_tilesRPA(x, y, tlevel, clevel, screenbuf); // Copy offscreen buffer to the screen memmove(screen,screenbuf,FULL_SCREEN_SIZE); // Check to see if user hit a key, and process if so if ( kbhit() ) { int xmap, ymap; char ch; ch=getch(); switch (ch) { case 0: ch=getch(); switch (ch) { // Left Arrow Key - shift map to the right case 'M': x+=SCROLL_STEP; xmap=int(x & MAP_COORD_MASK)/MAX_CELL_UNITS; if (xmap>(XMAP_MAX-1)) x-=SCROLL_STEP; break; // Right Arrow Key - shift map to the left case 'K': x-=SCROLL_STEP; xmap=int(x & MAP_COORD_MASK)/MAX_CELL_UNITS; if (xmap<0) x+=SCROLL_STEP; break; // Up Arrow Key - shift map downward case 'P': y+=SCROLL_STEP; ymap=int(y & MAP_COORD_MASK)/MAX_CELL_UNITS; if (ymap>(YMAP_MAX-1)) y-=SCROLL_STEP; break; // Down Arrow Key - shift map upward case 'H': y-=SCROLL_STEP; ymap=int(y & MAP_COORD_MASK)/MAX_CELL_UNITS; if (ymap<0) y+=SCROLL_STEP; break; } break; case 27: bye=1; break; } // Clear the keyboard buffer (eliminates backup) while(kbhit()) ch=getch(); } } while(!bye); // Restore the old video mode and clear the screen setgmode(oldmode); clrscr(); return; } /******************************************************************* Function: draw_tilesRPA(); Purpose: Renders the axonometric tile display using the "Reverse Painter's Algorithm" Arguments: x, y - pair of world coordinates on which display should be centered top_level - topmost level to display current_level - level on which display should be centered screenbuf - pointer to offscreen buffer Comments: The destination viewport (screenbuf) must be cleared (set to 0 values) before this function is called *******************************************************************/ void draw_tilesRPA(int x, int y, int top_level, int current_level, unsigned char far *screenbuf) { // Add viewport-to-map offsets corresponding to the distances // from the center to the lower left corner of the viewport x+=X_VPTM_OFFSET; y+=Y_VPTM_OFFSET; // Adjust world position to compensate for levels which must // be drawn above the current_level position for ( int k=current_level; k=0; level-- ) { // Calculate map location (map coordinates) int mapx=int(x & MAP_COORD_MASK)/MAX_CELL_UNITS; int mapy=int(y & MAP_COORD_MASK)/MAX_CELL_UNITS; // Calculate location within cell (cell coordinates) int cellx=(x & CELL_COORD_MASK); int celly=(y & CELL_COORD_MASK); // Set up initial values for conversion from cell // coordinates to screen coordinates int xpos=CELL_START_X; int ypos=CELL_START_Y; // Calulate x and y screen coordinates using the // cell-to-screen conversion formulas xpos+=(2*cellx-2*celly); ypos+=cellx+celly; // Adjust screen coordinates to the viewport xpos=(VIEWPORT_X_START-xpos); ypos=(VIEWPORT_Y_END-ypos); // Make adustments to the map and screen coordinates // (go below the viewport) to ensure that the walls/objects // of offscreen base tiles may still be visible (drawn) mapy+=START_YM_OFFSET; mapx+=START_XM_OFFSET; xpos+=START_XS_OFFSET; ypos+=START_YS_OFFSET; // Set initial x increment value int xadd=START_XADD; // Hold on to starting values of map coordinates int mxhold=mapx, myhold=mapy; // Create temporary holding variables for the screen position int tempx=xpos, tempy=ypos; // Loop through and draw tiles: left->right, bottom->top do { mapx=mxhold; mapy=myhold; // Set to starting map values tempx=xpos; // Get starting x value for run do { // Check if current map position is in bounds, if not, skip if ((mapx>=0)&&(mapx=0)&&(mapy0) // If xadd is positive, myhold-=1; // decrease starting y map pos else // If xadd is negative, mxhold-=1; // increase starting y map pos } while ( tempy >= (VIEWPORT_Y_START-CELL_FULL_HEIGHT) ); // Adjust world coordinates in order to draw the next level x+=-LEVEL_ADJUST; y+=-LEVEL_ADJUST; } return; } /******************************************************************* Function: load_files(); Purpose: Reads files and loads data for engine into memory Arguments: none Comments: returns 1 if successful returns 0 if error is encountered *******************************************************************/ int load_files(void) { int j; // Load palette, and load in straight tile bitmaps FILE *in; if ((in = fopen("pics.wad", "rb")) == NULL) { fprintf(stderr, "Cannot find file: PICS.WAD \n"); return(0); } fread(palette, sizeof(palette), 1, in); for (j=0; j #include #include #include "tile.h" // Declare constants for the viewport clipping #define XMIN 0 #define XMAX 319 #define YMIN 0 #define YMAX 199 #define SCREENWIDTH 320 #define SCREENHEIGHT 200 /******************************************************************* Function: Tile::Tile(); Purpose: The Tile class constructor Arguments: num_tiles - number of tiles (bitmaps) w - width of the tile in pixels (bytes) h - height of the tile in pixels (bytes) Comments: Will compute size of each tile in bytes, and will allocate memory for the tile bitmaps *******************************************************************/ Tile::Tile(int num_tiles,int w,int h) { width=w; height=h; // Calculate size of tile bitmap size=w*h; // Allocate memory for tile bitmaps image=new char far *[num_tiles]; for (int j=0; jXMAX) txend=XMAX-x+1; if ((y+height-1)>YMAX) tyend=YMAX-y+1; // Calculate tile and buffer starting offsets int toffset = (tystart*width)+txstart; int poffset = (y+tystart)*SCREENWIDTH+(x+txstart); // Calculate next row increments int toffinc = ((width-txend)+txstart); int poffinc = (SCREENWIDTH-(txend)+txstart); // Dereference one of the pointers to the tile bitmap for speed char far *tileimage = image[tile_num]; // Now loop through and copy the tile bitmap to the screen buffer for(int row=0; row<(tyend-tystart); row++) { for(int column=0; column<(txend-txstart); column++) { // Get pixel from the offscreen buffer int dest_pixel=screen[poffset]; // Check if it is transparent (0), if so, copy // the tile bitmap's pixel over it if (!dest_pixel) screen[poffset] = tileimage[toffset]; poffset++; toffset++; } // Jump to start of next row toffset+=toffinc; poffset+=poffinc; } return; } Example 1: (a) dX(screen) = 2*dX(world) - 2*dY(world) dY(screen) = dX(world) + dY(world) (b) dX(world) = [ dX(screen) + 2*dY(screen) ] / 4 dY(world) = [ 2*dY(screen) - dX(screen ) ] / 4