Topic : DOS Game Programming
Author : Alexander Russell
Page : << Previous 2  Next >>
Go to page :


this later.
Code to enter mode13h.


int old_mode;

void enter_mode13h(void)

{

   union REGS in, out;



   // get old video mode

   in.h.ah=0xf;

   int86(0x10, &in, &out);

   old_mode=out.h.al;



   // enter mode 13h

   in.h.ah=0;

   in.h.al=0x13;

   int86(0x10, &in, &out);

}



void leave_mode13h(void)

{

   union REGS in, out;



   // change to the video mode we were in before we switched to mode 13h

   in.h.ah=0;

   in.h.al=old_mode;

   int86(0x10, &in, &out);

}



Chapter 1 Exercises1. What does this do:


int i;

int *p;



p=malloc(100*sizeof(int))

for ( i=0; i < 100; i++ )

   *p++=i;



2. Write a function that returns 0 for even numbers, and non-zero for odd numbers.3. Write a function to reverse the contents of an integer array. Do not use pointers.
4. Write a function to reverse the contents of an integer array using pointers.
5. Write a function to return the larger of two integers.
6. Write a function to swap the value of two integers, use this prototype:
void swap(int *x, int *y);7. Write a function to fill an array of integers with even numbers using a `for' loop.
8. Write a function to fill an array of integers with odd numbers using a while loop.
9. Write a function to allocate 100 bytes of memory, and set it to the letters of the alphabet. When you reach Z, start over at A until you reach 100.
10. Write a function that copies a file using a 1Kb buffer allocated with malloc. Include error checking.
Chapter One has no source code.


Chapter 2.1More Graphic Primitives
A sprite is a small bitmap that is animated on the screen. One of the basic things done in a game is to animate sprites quickly on the screen. Virtually everything that moves in a 2D game is a sprite.
Solid spritesThe simplest type of sprite is the solid sprite. The whole rectangle of the sprite is drawn to the screen, and nothing can be seen 'through' it. This boils down to copying a rectangle of memory to the off screen buffer.
This is the simple structure that we will use to store a sprite:



WORD width

WORD height

Bytes bitmap (width*height bytes)



// blit (draw) a solid sprite

// sprite points to a chunk of memory that contains:

// width (2 bytes), height (2 bytes), bitmap (width*height bytes)



void blit_sprite(unsigned char far *spr, int x, int y)

{

   unsigned char far *p;

   int width, height;



   // make p point to starting point in off_screen

   p=off_screen + y*screen_width + x;



   // get the width and height, and make spr point to the

   // start of the bitmap

   _fmemcpy(&width, spr, 2);

   spr+=2;

   _fmemcpy(&height, spr, 2);

   spr+=2;



   while ( height-- )

      {

      _fmemcpy(p, spr, width); // copy one line of sprite to off_screen

      spr+=width;           // move to next line in sprite

      p+=screen_width;        // move to next line on screen

      }



}


Transparent spritesSolid sprites are not used that often in games, except for tiles. Usually an irregular shaped image is what you want to draw, e.g. a monster. To draw a sprite with transparent parts the simplest method is to simply skip pixels that are a certain 'key' colour. We will use colour zero (0) as our transparent colour. This is a simple method to implement, but it isn't very quick as you have to do a compare for every pixel. Once we master this method a faster method will be presented.


// draw a sprite, skipping pixels that are zero.

// This is a SLOW way to draw a transparent sprite

void blit_sprite_masked(unsigned char far *spr, int x, int y)

{

   unsigned char far *p;

   int width, height, w, adj;



   // make p point to starting point in off_screen

   p=off_screen + y*screen_width + x;



   // get the width and height, and make spr point to the

   // start of the bitmap

   _fmemcpy(&width, spr, 2);

   spr+=2;

   _fmemcpy(&height, spr, 2);

   spr+=2;



   // adj is the amount to move p to get to the next line

   // we will be moving p by width as we draw

   adj=screen_width - width;



   while ( height-- )

      {

      w=width;

      while ( w-- )   // draw one 'line' of the sprite

         {

         if ( *spr )  // check if *spr is zero

            {

            *p++=*spr++;  // draw one pixel, move p, and spr one to left

            }

         else

            {

            spr++;   // skip one pixel

            p++;

            }



         }



      p+=adj;   // move down one line

      }



}


RLE transparent spritesDoing a compare for each pixel is a slow way to draw a transparent sprite. A better (one of many better ways) is to encode the sprite with RLE (Run Length Encoding). RLE is a simple compression method that we will alter to speed up drawing transparent sprites. This is a two step process, 1. The sprite is converted to a RLE sprite, 2. We draw the RLE sprite. A RLE sprite has codes imbedded in it that tell us to skip, or draw strings of pixels. A string of pixels just means some pixels in a row.
To make a RLE sprite we read in the sprite, and count zero, and non zero pixels. These are then replaced with codes that indicate when we skip, and when we draw. This speeds things up because we only have to do a compare at the end of each string, instead of for each pixel.
Example
A zero byte means the next byte is the number of bytes to SKIP. A non-zero bytes means the next byte is the number of pixels to draw, and it will be followed by that number of pixels.
Lets look at one line of a simple bitmap that has zero bytes on each end.
000000000111224567864450000000This will be encoded as follows:

00,09,01,14, 11122456786445, 00,07

a  b  c  d   e               f  g


Byte a is a zero, so the next byte will be the number of pixels to skip. Byte b is 9, the number of pixels to skip Byte c is a 1 so the next byte will be the number of pixels to draw. Byte d is 14 which means the there are 14 pixels to draw Byte e is the start of the 14 pixels to draw Byte f is zero so the next byte is the number of pixels to skip Byte g is the number of pixels to skip which brings us to the end of the lineThere are also other methods, for example a sprite can be compiled which turns it into code that draws the sprite. You then draw the sprite by actually 'calling' the sprite as a function.


/*

blit a RLE encoded sprite



   0 byte next byte is number of bytes to skip

   1 byte next byte is number of bytes to draw

*/

void blit_sprite_rle(unsigned char far *spr, int x, int y)

{

   unsigned char far *p, cnt;

   int width, height, w, adj;



   // make p point to starting point in off_screen

   p=off_screen + y*screen_width + x;



   // get the width and height, and make spr point to the

   // start of the bitmap

   _fmemcpy(&width, spr, 2);

   spr+=2;

   _fmemcpy(&height, spr, 2);

   spr+=2;



   // adj is the amount to move p to get to the next line

   // we will be moving p by width as we draw

   adj=screen_width - width;



   while ( height-- )

      {

      w=0;

      while ( w < width )

         {

         if ( *spr )

            {

            // set cnt to the number of pixels to draw

            spr++;

            cnt=*spr;

            spr++;

            while ( cnt-- ) // draw pixels

               *p++=*spr++;

            }

         else

            {

            // set cnt to the number of pixels to skip

            spr++;

            cnt=*spr;

            spr++;

            p+=cnt;  // skip cnt pixels

            }



         w+=cnt;

         }



      p+=adj;

      }



}



Restoring backgroundsNow we can draw sprites of any shape on the screen. The next step would be to animate the sprite. One immediate problem is that we will need to erase the sprite as we move it. This is done by saving what is UNDER the sprite before we draw it, then restoring what we saved before drawing the next frame. When animating many sprites these saved patches of screen must be restored in the reverse order that they were saved in. We will cover getting a rectangle of memory from the off screen buffer here, and go into more detail on restoring screens later.
To save what is under the sprite is as simple as copying a rectangle of memory from the off screen buffer into a temporary sprite. This is just the opposite of blit_sprite().



// get a patch of screen from off screen and save it as a sprite

// spr must point to enough memory to hold width*height + 4 bytes

void get_sprite(unsigned char far *spr, int x, int y, int width, int height)

{

   unsigned char far *p;



   // copy width and height to the sprite

   _fmemcpy(spr, &width, 2);

   spr+=2;

   _fmemcpy(spr, &height, 2);

   spr+=2;



   // make p point to starting point in off_screen

   p=off_screen + y*screen_width + x;



   // copy memory from off_screen to the sprite

   while ( height-- )

      {

      _fmemcpy(spr, p, width);

      spr+=width;

      p+=screen_width;

      }

}



Graphic TextAll games require that some text be printed on the screen. This is done by drawing the text on the screen pixel by pixel using various fonts as a template. The VGA card has a number of built in fonts that can be accessed, and we can create our own fonts. Fonts are usually stored in a compact format where each bit defines whether a pixel is on or off. This saves space, and as fonts are monochrome, all the information we need to draw it. This is the way the VGA stores its own ROM fonts. We will be learning how to use the VGA's built in fonts to draw text.
Drawing text requires a few steps: ask the bios where the fonts are, decide which font to use, and actually

Page : << Previous 2  Next >>