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


of re-using the master

      sprite, this code would show as all different sprites



      */





   // load all the sprites up.

   // each sprites needs seperate memory for erasing, but they all share

   // the master_sprite for drawing

   for ( sp=sprites, i=0; i < num; i++, sp++ )

      {

      // just make the sprite POINT to the master sprite

      sp->sprite=master_sprite;  // could easily load any sprite here



      // sets its width and height

      _fmemcpy(&sp->width, sp->sprite, 2);

      sp->max_x=screen_width - sp->width;

      sp->max_x--;

      _fmemcpy(&sp->height, sp->sprite+2, 2);

      sp->max_y=screen_height - sp->height;

      sp->max_y--;



      // make space for our erase rect that will be used to erase

      // the old sprite as it moves

      sp->erase=farmalloc(sp->width*sp->height + 4);

      if ( !sp->erase )

         {

         // malloc failed - quit

         // should free the erase rects malloc to here also

         farfree(master_sprite);

         free(sprites);

         return;

         }



      // randomly set a start position and speed (vector)

      sp->x=150 - random(100);

      sp->y=100 + random(75);

      sp->dx= random(10) < 5 ? -1 : 1;

      sp->dy= random(10) < 5 ? -1 : 1;

      if ( random(10) < 5 )

         sp->dx*=2;

      if ( random(10) < 5 )

         sp->dy*=2;



      // initialize the erase sprite

      get_sprite(sp->erase, sp->x, sp->y, sp->width, sp->height);

      sp->old_x=sp->x;

      sp->old_y=sp->y;

      }





   done=0;

   next_time=get_tick() + 1;

   while ( !done )

      {

      // erase all old sprite, in REVERSE order

      for ( sp=sprites+num-1, i=0; i < num; i++, sp-- )

         blit_sprite(sp->erase, sp->old_x, sp->old_y);



      // move all the sprites

      for ( sp=sprites, i=0; i < num; i++, sp++ )

         {

         // move at a steady speed on all computers

         if ( get_tick() >= next_time )

            {

            // move

            sp->x+=sp->dx;

            sp->y+=sp->dy;





            // check for bouncing

            if ( sp->x < 0 )

               {

               sp->x=0;

               sp->dx=-sp->dx;  // move in othr direction

               }

            else

               {

               if ( sp->x > sp->max_x )

                  {

                  sp->x=sp->max_x;

                  sp->dx=-sp->dx;  

                  }

               }



            if ( sp->y < 0 )

               {

               sp->y=0;

               sp->dy=-sp->dy;  // move in othr direction

               }

            else

               {

               if ( sp->y > sp->max_y )

                  {

                  sp->y=sp->max_y;

                  sp->dy=-sp->dy;  

                  }

               }



            next_time=get_tick();

            }

         }



      // draw all the sprites, as fast as we can

      for ( sp=sprites, i=0; i < num; i++, sp++ )

         {

         // get what was under the sprite

         get_sprite(sp->erase, sp->x, sp->y, sp->width, sp->height);

         sp->old_x=sp->x;

         sp->old_y=sp->y;

         // draw new sprite

         blit_sprite(sp->sprite, sp->x, sp->y);

         }



      update_buffer();



      // check for user input

      if ( kbhit() )

         if ( getch() == ESC )

            done=1;

      }





   for ( sp=sprites, i=0; i < num; i++, sp++ )

      farfree(sp->erase);

   farfree(master_sprite);

   free(sprites);





}



This method allows you to make a sprite drawing function that includes getting the erase sprite which could then be called from many different functions without knowing in advance what order things will be drawn in. This simplifies your code for larger projects.
Chapter 3 Exercises1. Write a function that bounces a single pixel in straight lines on the screen that does not leave a trail behind it. Use a plain black background.2. Write a function that bounces a single pixel in straight lines on the screen that does not leave a trail behind it. Load a full screen picture as the background.
3. Write a function that makes a pixel bounce like a ball. Hint: x+=dx; dy+=ddy; y+=dy;
4. Write a function that bounces 10 pixels on the screen. When a pixel has hit an edge 10 times it 'dies'. When all the pixels die the function quits.
5. Write a function that bounces at least 4 identical sprites on a black screen, and erases where the sprites were with a call to rect_fill().
6. Write a function that bounces at least 4 different sprites on a black screen, and erases where the sprites were with a call to rect_fill().
7. Write a function that bounces at least 4 different sprites on a loaded picture, and erases where the sprites were with a sprite grabbed by get_sprite().
8. Write a program that loads 4 different sprites, and bounces them. When a sprite hits the edge for the tenth time it 'dies', and a new sprite (pick one of the four randomly) starts at a random position.


Chapter 4 User Input


Games require fast, responsive input for enjoyable game play. Under DOS this requires a fair bit of knowledge of the underlying hardware and operating system. Most of this specialized knowledge is not applicable to any other game platform. Almost any other game programming platform (win32 and DirectX, consoles, etc.) will have built in support for fast game input. That said, here is what a DOS game requires.
KeyboardThe standard BIOS keyboard handler does not allow multiple key presses to be processed. If you only use the CTRL, SHIFT, and ALT keys you can get away with some simple BIOS tricks, but for more general multi-key support you have to write an int9 (the hardware interrupt used by the keyboard) ISR (Interrupt Replacement Service) that completely replaces the normal BIOS keyboard handler. Our keyboard ISR will get the raw scan codes, and place them in a queue for later processing.
MouseThe standard int33h mouse services are pretty good, but they require you to poll the mouse unless you install your own ISR to work in concert with int33h driver. Polling means that at intervals you ask the mouse driver what the mouse is up to. This isn't good as you could miss a mouse click or other action. Once again our ISR will place mouse events in a queue for later processing.
JoystickThe standard analog joystick is a very slow device to read under DOS. It requires waiting for the joystick to timeout. To prevent it from slowing a game down too much it is only read at regular intervals. The buttons can be read more quickly.
General Input QueueWe will take the input from all devices and place it in one general input queue. This simplifies i/o handling, and makes it easier to add different i/o devices. This is the way all modern operating systems handle i/o.
Keyboard INT9 ISRThis code allows us full control over the keyboard.
I use inline ASM code for much of the hardware access. The ASM code:
in al, 060h
puts the value of port 0x60 into al, and is equivalent to:
unsigned char a;

a=inportb(0x60);

mov al, 020h
out 020h, al

is equivalent to:

a=0x20;
outportb(0x20, a);



#define BYTE unsigned char

#define NUM_SCAN_QUE 256     // this MUST be 256, using BYTE roll-over for

                             // q code



// the interrupt keyword causes the compiler to save all the registers before the

function is called, and restore them on exit. It also makes the function return via a

IRET.



static void interrupt (far *oldkb)(void);   /* BIOS keyboard handler */



// Q code

BYTE gb_scan;

BYTE gb_scan_q[NUM_SCAN_QUE];

BYTE gb_scan_head;

BYTE gb_scan_tail;



/*

   invoked by the hardware keyboard interupt

   ques up the raw scan codes

    stuff raw scan codes into the array gb_scan_q[]

*/

/* ---------------------- get_scan() --------------------- April 17,1993 */

void interrupt get_scan(void)

{



   /* read the raw scan code from the keyboard */

   asm   cli



   asm   {



         in    al, 060h       /* read scan code */

         mov   gb_scan, al

         in    al, 061h       /* read keyboard status */

         mov   bl, al

         or    al, 080h

         out   061h, al       /* set bit 7 and write */

         mov   al, bl

         out   061h, al       /* write again, bit 7 clear */



         mov   al, 020h       /* reset PIC */

         out   020h, al



         /* end of re-set code */



         sti

         }



// save the raw scan code in a 256 byte buffer

   *(gb_scan_q+gb_scan_tail)=gb_scan;

   ++gb_scan_tail;

}



/*



save the old int9 ISR vector, and install our own

*/

/* ---------------------- init_keyboard() ---------------- April 17,1993 */

void init_keyboard(void)

{

   BYTE far *bios_key_state;



   /* save old BIOS key board handler */

   oldkb=getvect(9);



   // turn off num-lock via BIOS

   bios_key_state=MK_FP(0x040, 0x017);

   *bios_key_state&=(~(32 | 64));     // toggle off caps lock and

                                      // num lock bits in the BIOS variable

   oldkb();      // call BIOS key handler to change keyboard lights



   gb_scan_head=0;

   gb_scan_tail=0;

   gb_scan=0;



   /* install our own handler */

   setvect(9, get_scan);



}



/* restore the bios keyboard handler */

/* ---------------------- deinit_keyboard() -------------- April 17,1993 */

void deinit_keyboard(void)

{

   setvect(9, oldkb);

}



As you can see this code is very DOS specific. We will need it for our demo game, but it isn't really useful for anyone thinking about a career in game programming these days. Most keys return two unique

Page : << Previous 6  Next >>