Alex Russell's Game Programming Tutorial using DX
Introduction . Chapter 1 . Chapter 2 . Chapter 3 . Chapter 4 . Chapter 5 . Chapter 6 . Chapter 7
Chapter 4
Animation
To make an object appear to move it is re-drawn many times a second, but it is moved a little bit each time it is re-drawn. Each drawing is referred to as a `frame'. To get smooth animation you want to draw at least 30 frames per second, and frames rates of 60 or more are ideal. Modern hardware makes it easy to reach these speeds with straight forward techniques.
So how do you move an object? You change its x and y coordinates.
Bouncing Pixel
We will make a single pixel bounce in straight lines across the screen next. The pixel will be drawn at the coordinates (x,y) each frame. To make the pixel move right x is increased, to move to the left x is decreased. Increasing y makes the pixel move down and decreasing y makes the pixel move up. Once the pixel reaches the edge of the screen we want it to 'bounce'. Bouncing is done by making the direction we move the pixel the opposite when we reach an edge. If we add dx to x each frame then to bouce all we have to do is make dx the negative of itself.
Bouncing Sprite
Drawing a bouncing sprite is exactly the same except you draw a sprite (or bitmap) at (x, y) instead of a single pixel. Only a few minor things change to draw a bitmap compared to the pixel code. We also have to bounce at "screen_width - bitmap_width" (same for height) so that the nitmap stays on the screen.
Multiple Bouncing Sprites
Here is the Source code for a demo of a number of simple animations. dx_04.zip requires the next package of source to be added to the class CgsdxIO
in the DXSmith library. Insert the source cpp functions into DXSmithIO.cpp, and add the prototypes to public section in the class CgsdxIO in DXSmithIO.h
Introduction . Chapter 1 . Chapter 2 . Chapter 3 . Chapter 4 . Chapter 5 . Chapter 6 . Chapter 7
Copyright 2004 (c), Alex Russell, All rights reserved
{
int done;
int x, y, dx, dy, width, height, kick;
DWORD next_time;
width=dx_p->GetGraphicWidth() - 1;
height=dx_p->GetGraphicHeight() - 1;
x=100; // current x position
y=200; // current y position
done=0; // flag for done
dx=1; // amount to move in x direction
dy=1; // amount to move in y direction
next_time=timeGetTime() + 1; // a timer
while ( !done )
{
// move at a steady speed on all computers
// if not enough time has NOT passed, redraw the screen without moving
if ( timeGetTime() >= next_time )
{
// move
x+=dx;
y+=dy;
// check for bouncing
if ( x < 0 )
{
x=0;
dx=-dx; // move in other direction
}
else
{
if ( x > width )
{
x=width;
dx=-dx; // move in other direction
}
}
if ( y < 0 )
{
y=0;
dy=-dy; // move in other direction
}
else
{
if ( y > height )
{
y=height;
dy=-dy; // move in other direction
}
}
next_time=timeGetTime();
}
// draw, as fast as we can
dx_p->Pixel(x, y, RGB(0, 255, 0));
dx_p->Flip();
}
}
gsdxBitmap_t *ball; // bitmap var
POINT p;
// load the bitmap, before main loop
ball=dx_p->LoadBitmap("ball.bmp", NULL);
if ( !ball )
return;
// call Blit() instead of Pixel() in the main loop
// draw the ball
p.x=x;
p.y=y;
dx_p->Blit(&p, ball);
dx_p->Flip();
// info for one bouncing object
typedef struct
{
int x, y;
int dx, dy;
}
mini_t;
// multiple bitmaps
void bounce_bitmap2(CgsdxIO *dx_p)
{
int done;
int width, height, i, x, y;
DWORD next_time;
gsdxBitmap_t *ball;
gsdxBitmap_t *fullscreen=NULL;
POINT p;
mini_t balls[10]; // information on 10 bouncing objects
ball=dx_p->LoadBitmap("ball.bmp", NULL);
if ( !ball )
return;
width=dx_p->GetGraphicWidth() - 1 - ball->width;
height=dx_p->GetGraphicHeight() - 1 - ball->height;
x=10; // current x position
y=20; // current y position
srand(timeGetTime());
// set each balls starting position and direction of travel
for ( i=0; i < 10; i++ )
{
balls[i].x=x + (rand()%150);
balls[i].y=y + (rand()%100);
balls[i].dx=rand()%2 ? 1 : -1;
balls[i].dy=rand()%2 ? 1 : -1;
x+=60;
y+=50;
}
done=0; // flag for done
next_time=timeGetTime() + 1; // a timer
while ( !done )
{
// move at a steady speed on all computers
if ( timeGetTime() >= next_time )
{
// move all 10 objects
for ( i=0; i < 10; i++ )
{
balls[i].x+=balls[i].dx;
balls[i].y+=balls[i].dy;
// check for bouncing
if ( balls[i].x < 0 )
{
balls[i].x=0;
balls[i].dx=-balls[i].dx; // move in other direction
}
else
{
if ( balls[i].x > width )
{
balls[i].x=width;
balls[i].dx=-balls[i].dx; // move in other direction
}
}
if ( balls[i].y < 0 )
{
balls[i].y=0;
balls[i].dy=-balls[i].dy; // move in other direction
}
else
{
if ( balls[i].y > height )
{
balls[i].y=height;
balls[i].dy=-balls[i].dy; // move in other direction
}
}
} // for i
next_time=timeGetTime() + 1;
} // if time
// draw, as fast as we can
p.x=0;
p.y=0;
dx_p->RectFill(NULL, 0); // clear screen
// draw all 10 balls
for ( i=0; i < 10; i++ )
{
p.x=balls[i].x;
p.y=balls[i].y;
dx_p->Blit(&p, ball);
}
dx_p->Flip();
}
delete ball;
}