282 lines
13 KiB
Markdown
282 lines
13 KiB
Markdown
### Animation {#Heading6}
|
||
|
||
Gosh. There's just no way I can discuss high-level animation
|
||
fundamentals in any detail here; I could spend an entire (and entirely
|
||
separate) book on animation techniques alone. You might want to have a
|
||
look at Chapters 43 through 46 before attacking the code in this
|
||
chapter; that will have to do us for the present volume. (I will return
|
||
to *3-D* animation in the next chapter.)
|
||
|
||
Basically, I'm going to perform page flipped animation, in which one
|
||
page (that is, a bitmap large enough to hold a full screen) of display
|
||
memory is displayed while another page is drawn to. When the drawing is
|
||
finished, the newly modified page is displayed, and the other—now
|
||
invisible—page is drawn to. The process repeats ad infinitum. For
|
||
further information, some good places to start are *Computer Graphics*,
|
||
by Foley and van Dam (Addison-Wesley); *Principles of Interactive
|
||
Computer Graphics*, by Newman and Sproull (McGraw Hill); and "Real-Time
|
||
Animation" by Rahner James (January 1990, *Dr. Dobb's Journal*).
|
||
|
||
Some of the code in this chapter was adapted for Mode X from the code in
|
||
Chapter 44—yet another reason to read that chapter before finishing this
|
||
one.
|
||
|
||
### Mode X Animation in Action {#Heading7}
|
||
|
||
Listing 49.5 ties together everything I've discussed about Mode X so far
|
||
in a compact but surprisingly powerful animation package. Listing 49.5
|
||
first uses solid and patterned fills and system-memory-to-screen-memory
|
||
masked copying to draw a static background containing a mountain, a sun,
|
||
a plain, water, and a house with puffs of smoke coming out of the
|
||
chimney, and sets up the four alignments of a masked kite image. The
|
||
background is transferred to both display pages, and drawing of 20 kite
|
||
images in the nondisplayed page using fast masked copying begins. After
|
||
all images have been drawn, the page is flipped to show the newly
|
||
updated screen, and the kites are moved and drawn in the other page,
|
||
which is no longer displayed. Kites are erased at their old positions in
|
||
the nondisplayed page by block copying from the background page. (See
|
||
the discussion in the previous chapter for the display memory
|
||
organization used by Listing 49.5.) So far as the displayed image is
|
||
concerned, there is never any hint of flicker or disturbance of the
|
||
background. This continues at a rate of up to 60 times a second until
|
||
Esc is pressed to exit the program. See Figure 49.1 for a screen shot of
|
||
the resulting image—add the animation in your imagination.
|
||
|
||
\
|
||
**Figure 49.1** *An animated Mode X screen.*
|
||
|
||
**LISTING 49.5 L49-5.C**
|
||
|
||
/* Sample mode X VGA animation program. Portions of this code first appeared
|
||
in PC Techniques. Compiled with Borland C++ 2.0 in C compilation mode. */
|
||
|
||
#include <stdio.h>
|
||
#include <conio.h>
|
||
#include <dos.h>
|
||
#include <math.h>
|
||
#include "maskim.h"
|
||
|
||
#define SCREEN_SEG 0xA000
|
||
#define SCREEN_WIDTH 320
|
||
#define SCREEN_HEIGHT 240
|
||
#define PAGE0_START_OFFSET 0
|
||
#define PAGE1_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH)/4)
|
||
#define BG_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH*2)/4)
|
||
#define DOWNLOAD_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH*3)/4)
|
||
|
||
static unsigned int PageStartOffsets[2] = {PAGE0_START_OFFSET,PAGE1_START_OFFSET};
|
||
static char GreenAndBrownPattern[] = {2,6,2,6, 6,2,6,2, 2,6,2,6, 6,2,6,2};
|
||
static char PineTreePattern[] = {2,2,2,2, 2,6,2,6, 2,2,6,2, 2,2,2,2};
|
||
static char BrickPattern[] = {6,6,7,6, 7,7,7,7, 7,6,6,6, 7,7,7,7,};
|
||
static char RoofPattern[] = {8,8,8,7, 7,7,7,7, 8,8,8,7, 8,8,8,7};
|
||
|
||
#define SMOKE_WIDTH 7
|
||
#define SMOKE_HEIGHT 7
|
||
static char SmokePixels[] = {
|
||
0, 0,15,15,15, 0, 0,
|
||
0, 7, 7,15,15,15, 0,
|
||
8, 7, 7, 7,15,15,15,
|
||
8, 7, 7, 7, 7,15,15,
|
||
0, 8, 7, 7, 7, 7,15,
|
||
0, 0, 8, 7, 7, 7, 0,
|
||
0, 0, 0, 8, 8, 0, 0};
|
||
static char SmokeMask[] = {
|
||
0, 0, 1, 1, 1, 0, 0,
|
||
0, 1, 1, 1, 1, 1, 0,
|
||
1, 1, 1, 1, 1, 1, 1,
|
||
1, 1, 1, 1, 1, 1, 1,
|
||
1, 1, 1, 1, 1, 1, 1,
|
||
0, 1, 1, 1, 1, 1, 0,
|
||
0, 0, 1, 1, 1, 0, 0};
|
||
#define KITE_WIDTH 10
|
||
#define KITE_HEIGHT 16
|
||
static char KitePixels[] = {
|
||
0, 0, 0, 0,45, 0, 0, 0, 0, 0,
|
||
0, 0, 0,46,46,46, 0, 0, 0, 0,
|
||
0, 0,47,47,47,47,47, 0, 0, 0,
|
||
0,48,48,48,48,48,48,48, 0, 0,
|
||
49,49,49,49,49,49,49,49,49, 0,
|
||
0,50,50,50,50,50,50,50, 0, 0,
|
||
0,51,51,51,51,51,51,51, 0, 0,
|
||
0, 0,52,52,52,52,52, 0, 0, 0,
|
||
0, 0,53,53,53,53,53, 0, 0, 0,
|
||
0, 0, 0,54,54,54, 0, 0, 0, 0,
|
||
0, 0, 0,55,55,55, 0, 0, 0, 0,
|
||
0, 0, 0, 0,58, 0, 0, 0, 0, 0,
|
||
0, 0, 0, 0,59, 0, 0, 0, 0,66,
|
||
0, 0, 0, 0,60, 0, 0,64, 0,65,
|
||
0, 0, 0, 0, 0,61, 0, 0,64, 0,
|
||
0, 0, 0, 0, 0, 0,62,63, 0,64};
|
||
static char KiteMask[] = {
|
||
0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
|
||
0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
|
||
0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
|
||
0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||
1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
||
0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||
0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||
0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
|
||
0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
|
||
0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
|
||
0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
|
||
0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
|
||
0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
|
||
0, 0, 0, 0, 1, 0, 0, 1, 0, 1,
|
||
0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
|
||
0, 0, 0, 0, 0, 0, 1, 1, 0, 1};
|
||
static MaskedImage KiteImage;
|
||
|
||
#define NUM_OBJECTS 20
|
||
typedef struct {
|
||
int X,Y,Width,Height,XDir,YDir,XOtherPage,YOtherPage;
|
||
MaskedImage *Image;
|
||
} AnimatedObject;
|
||
AnimatedObject AnimatedObjects[] = {
|
||
{ 0, 0,KITE_WIDTH,KITE_HEIGHT, 1, 1, 0, 0,&KiteImage},
|
||
{ 10, 10,KITE_WIDTH,KITE_HEIGHT, 0, 1, 10, 10,&KiteImage},
|
||
{ 20, 20,KITE_WIDTH,KITE_HEIGHT,-1, 1, 20, 20,&KiteImage},
|
||
{ 30, 30,KITE_WIDTH,KITE_HEIGHT,-1,-1, 30, 30,&KiteImage},
|
||
{ 40, 40,KITE_WIDTH,KITE_HEIGHT, 1,-1, 40, 40,&KiteImage},
|
||
{ 50, 50,KITE_WIDTH,KITE_HEIGHT, 0,-1, 50, 50,&KiteImage},
|
||
{ 60, 60,KITE_WIDTH,KITE_HEIGHT, 1, 0, 60, 60,&KiteImage},
|
||
{ 70, 70,KITE_WIDTH,KITE_HEIGHT,-1, 0, 70, 70,&KiteImage},
|
||
{ 80, 80,KITE_WIDTH,KITE_HEIGHT, 1, 2, 80, 80,&KiteImage},
|
||
{ 90, 90,KITE_WIDTH,KITE_HEIGHT, 0, 2, 90, 90,&KiteImage},
|
||
{100,100,KITE_WIDTH,KITE_HEIGHT,-1, 2,100,100,&KiteImage},
|
||
{110,110,KITE_WIDTH,KITE_HEIGHT,-1,-2,110,110,&KiteImage},
|
||
{120,120,KITE_WIDTH,KITE_HEIGHT, 1,-2,120,120,&KiteImage},
|
||
{130,130,KITE_WIDTH,KITE_HEIGHT, 0,-2,130,130,&KiteImage},
|
||
{140,140,KITE_WIDTH,KITE_HEIGHT, 2, 0,140,140,&KiteImage},
|
||
{150,150,KITE_WIDTH,KITE_HEIGHT,-2, 0,150,150,&KiteImage},
|
||
{160,160,KITE_WIDTH,KITE_HEIGHT, 2, 2,160,160,&KiteImage},
|
||
{170,170,KITE_WIDTH,KITE_HEIGHT,-2, 2,170,170,&KiteImage},
|
||
{180,180,KITE_WIDTH,KITE_HEIGHT,-2,-2,180,180,&KiteImage},
|
||
{190,190,KITE_WIDTH,KITE_HEIGHT, 2,-2,190,190,&KiteImage},
|
||
};
|
||
void main(void);
|
||
void DrawBackground(unsigned int);
|
||
void MoveObject(AnimatedObject *);
|
||
extern void Set320x240Mode(void);
|
||
extern void FillRectangleX(int, int, int, int, unsigned int, int);
|
||
extern void FillPatternX(int, int, int, int, unsigned int, char*);
|
||
extern void CopySystemToScreenMaskedX(int, int, int, int, int, int,
|
||
char *, unsigned int, int, int, char *);
|
||
extern void CopyScreenToScreenX(int, int, int, int, int, int,
|
||
unsigned int, unsigned int, int, int);
|
||
extern unsigned int CreateAlignedMaskedImage(MaskedImage *,
|
||
unsigned int, char *, int, int, char *);
|
||
extern void CopyScreenToScreenMaskedX(int, int, int, int, int, int,
|
||
MaskedImage *, unsigned int, int);
|
||
extern void ShowPage(unsigned int);
|
||
|
||
void main()
|
||
{
|
||
int DisplayedPage, NonDisplayedPage, Done, i;
|
||
union REGS regset;
|
||
Set320x240Mode();
|
||
/* Download the kite image for fast copying later. */
|
||
if (CreateAlignedMaskedImage(&KiteImage, DOWNLOAD_START_OFFSET,
|
||
KitePixels, KITE_WIDTH, KITE_HEIGHT, KiteMask) == 0) {
|
||
regset.x.ax = 0x0003; int86(0x10, ®set, ®set);
|
||
printf("Couldn't get memory\n"); exit();
|
||
}
|
||
/* Draw the background to the background page. */
|
||
DrawBackground(BG_START_OFFSET);
|
||
/* Copy the background to both displayable pages. */
|
||
CopyScreenToScreenX(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0,
|
||
BG_START_OFFSET, PAGE0_START_OFFSET, SCREEN_WIDTH, SCREEN_WIDTH);
|
||
CopyScreenToScreenX(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0,
|
||
BG_START_OFFSET, PAGE1_START_OFFSET, SCREEN_WIDTH, SCREEN_WIDTH);
|
||
/* Move the objects and update their images in the nondisplayed
|
||
page, then flip the page, until Esc is pressed. */
|
||
Done = DisplayedPage = 0;
|
||
do {
|
||
NonDisplayedPage = DisplayedPage ^ 1;
|
||
/* Erase each object in nondisplayed page by copying block from
|
||
background page at last location in that page. */
|
||
for (i=0; i<NUM_OBJECTS; i++) {
|
||
CopyScreenToScreenX(AnimatedObjects[i].XOtherPage,
|
||
AnimatedObjects[i].YOtherPage,
|
||
AnimatedObjects[i].XOtherPage +
|
||
AnimatedObjects[i].Width,
|
||
AnimatedObjects[i].YOtherPage +
|
||
AnimatedObjects[i].Height,
|
||
AnimatedObjects[i].XOtherPage,
|
||
AnimatedObjects[i].YOtherPage, BG_START_OFFSET,
|
||
PageStartOffsets[NonDisplayedPage], SCREEN_WIDTH, SCREEN_WIDTH);
|
||
}
|
||
/* Move and draw each object in the nondisplayed page. */
|
||
for (i=0; i<NUM_OBJECTS; i++) {
|
||
MoveObject(&AnimatedObjects[i]);
|
||
/* Draw object into nondisplayed page at new location */
|
||
CopyScreenToScreenMaskedX(0, 0, AnimatedObjects[i].Width,
|
||
AnimatedObjects[i].Height, AnimatedObjects[i].X,
|
||
AnimatedObjects[i].Y, AnimatedObjects[i].Image,
|
||
PageStartOffsets[NonDisplayedPage], SCREEN_WIDTH);
|
||
}
|
||
/* Flip to the page into which we just drew. */
|
||
ShowPage(PageStartOffsets[DisplayedPage = NonDisplayedPage]);
|
||
/* See if it's time to end. */
|
||
if (kbhit()) {
|
||
if (getch() == 0x1B) Done = 1; /* Esc to end */
|
||
}
|
||
} while (!Done);
|
||
/* Restore text mode and done. */
|
||
regset.x.ax = 0x0003; int86(0x10, ®set, ®set);
|
||
}
|
||
void DrawBackground(unsigned int PageStart)
|
||
{
|
||
int i,j,Temp;
|
||
/* Fill the screen with cyan. */
|
||
FillRectangleX(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, PageStart, 11);
|
||
/* Draw a green and brown rectangle to create a flat plain. */
|
||
FillPatternX(0, 160, SCREEN_WIDTH, SCREEN_HEIGHT, PageStart,
|
||
GreenAndBrownPattern);
|
||
/* Draw blue water at the bottom of the screen. */
|
||
FillRectangleX(0, SCREEN_HEIGHT-30, SCREEN_WIDTH, SCREEN_HEIGHT,
|
||
PageStart, 1);
|
||
/* Draw a brown mountain rising out of the plain. */
|
||
for (i=0; i<120; i++)
|
||
FillRectangleX(SCREEN_WIDTH/2-30-i, 51+i, SCREEN_WIDTH/2-30+i+1,
|
||
51+i+1, PageStart, 6);
|
||
/* Draw a yellow sun by overlapping rects of various shapes. */
|
||
for (i=0; i<=20; i++) {
|
||
Temp = (int)(sqrt(20.0*20.0 - (float)i*(float)i) + 0.5);
|
||
FillRectangleX(SCREEN_WIDTH-25-i, 30-Temp, SCREEN_WIDTH-25+i+1,
|
||
30+Temp+1, PageStart, 14);
|
||
}
|
||
/* Draw green trees down the side of the mountain. */
|
||
for (i=10; i<90; i += 15)
|
||
for (j=0; j<20; j++)
|
||
FillPatternX(SCREEN_WIDTH/2+i-j/3-15, i+j+51,SCREEN_WIDTH/2+i+j/3-15+1,
|
||
i+j+51+1, PageStart, PineTreePattern);
|
||
/* Draw a house on the plain. */
|
||
FillPatternX(265, 150, 295, 170, PageStart, BrickPattern);
|
||
FillPatternX(265, 130, 270, 150, PageStart, BrickPattern);
|
||
for (i=0; i<12; i++)
|
||
FillPatternX(280-i*2, 138+i, 280+i*2+1, 138+i+1, PageStart, RoofPattern);
|
||
/* Finally, draw puffs of smoke rising from the chimney. */
|
||
for (i=0; i<4; i++)
|
||
CopySystemToScreenMaskedX(0, 0, SMOKE_WIDTH, SMOKE_HEIGHT, 264,
|
||
110-i*20, SmokePixels, PageStart, SMOKE_WIDTH,SCREEN_WIDTH, SmokeMask);
|
||
}
|
||
/* Move the specified object, bouncing at the edges of the screen and
|
||
remembering where the object was before the move for erasing next time. */
|
||
void MoveObject(AnimatedObject * ObjectToMove) {
|
||
int X, Y;
|
||
X = ObjectToMove->X + ObjectToMove->XDir;
|
||
Y = ObjectToMove->Y + ObjectToMove->YDir;
|
||
if ((X < 0) || (X > (SCREEN_WIDTH - ObjectToMove->Width))) {
|
||
ObjectToMove->XDir = -ObjectToMove->XDir;
|
||
X = ObjectToMove->X + ObjectToMove->XDir;
|
||
}
|
||
if ((Y < 0) || (Y > (SCREEN_HEIGHT - ObjectToMove->Height))) {
|
||
ObjectToMove->YDir = -ObjectToMove->YDir;
|
||
Y = ObjectToMove->Y + ObjectToMove->YDir;
|
||
}
|
||
/* Remember previous location for erasing purposes. */
|
||
ObjectToMove->XOtherPage = ObjectToMove->X;
|
||
ObjectToMove->YOtherPage = ObjectToMove->Y;
|
||
ObjectToMove->X = X; /* set new location */
|
||
ObjectToMove->Y = Y;
|
||
}
|