Simply put, sprites would be dull without animation. So far, we have only
covered unchanging sprites. In this lesson, we will learn how to make a sprite
capable of multiple frames, how those frames are loaded, and how they are programmed.
Rectangles?! We're all set to go over the height of 2D graphics programming
and we're going to talk about simple rectangles?!
Actually, this part is simple and important. I probably should have covered
it earlier, but it was ironically avoided. But don't worry. It is easy.
A rectangle in C++ is represented by a struct called RECT. It has four LONG
values:
the top, the left, the bottom and the right. Each value indicates the distance
from the top of the screen or the left of the screen, as shown here:

Image 3.1 - The Four Values of RECT
As you can see, the Left shows the distance between the left side in pixels and
the left side of the "screen", while the Right shows the distance between the right
side and the left side of the "screen". The same goes for Top and Bottom,
but on the y-axis.
Now let's look at the definition of this struct:
typedef struct _RECT
{
LONG left; // the left value
LONG top; // the top value
LONG right; // the right value
LONG bottom; // the bottom value
} RECT, *PRECT;
Each value is self-explanatory, so I won't go into them any further. Let's
take a look at how this would be used:
RECT rectangle = {100, 100, 300, 250}; // create the rectangle in Image
3.1
To change the rectangle later, you can individually change the values or set all
the values with the SetRect() function. This function has five parameters,
the first containing the address of the RECT, and the other four containing the
four values. The following code does the exact same thing as shown before:
RECT rectangle; // create the rectangle
SetRect(&rectangle, 100, 100, 300, 250); // initialize the rectangle
And now let's find out how to put this to practical use.
So how can we use this RECT to produce animation? Well, let's first take a
look at the Draw() function once again:
HRESULT Draw(LPDIRECT3DTEXTURE9 pTexture,
CONST RECT* pSrcRect,
CONST D3DXVECTOR3* pCenter,
CONST D3DXVECTOR3* pPosition,
D3DCOLOR Color);
The second parameter listed is CONST RECT* pSrcRect, which is a pointer to a RECT.
This RECT (assuming it is not simply set to NULL) contains the portion of a sprite
that is to be drawn. For example, we could take a RECT out of the grid sprite
and draw it separately, like this:

Image 3.2 - Only Some of Panel2.png
This is all very spectacular of course, but how does this allow us to produce animation?
Well, all we now have to do is line up each frame into a single sprite and then
use a RECT to draw the desired frame only. Example:
Here we have a portion of a sprite that contains an animated version of our display
graphic. It is far too large to display the whole thing here, but you can
see the whole thing here.

Image 3.3 - Part of the Animation Strip
In this piece of the sprite, you can see the first four frames.
At the start of our animation, we would use
frame 1, like this:

Image 3.4 - The First Frame
Next, we would increment the Left value by 181 (the width of each frame), and display
the next frame, like this:

Image 3.5 - The Second Frame
We would then continue down the line, until each frame is drawn, and then we would
either repeat, pause, or begin a new animation.
There is really no DirectX code for doing animation, but only simple C++ code.
This example program is actually quite crude in architecture, but only has the purpose
of displaying a single animation.
To run this example, you will need the latest graphic: "Panel3.png".
[
Show Code]
// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>
// define the screen resolution and keyboard macros
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
// include the Direct3D Library file
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")
// global declarations
LPDIRECT3D9 d3d; // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev; // the pointer to the device class
LPD3DXSPRITE d3dspt; // the pointer to our Direct3D Sprite interface
// sprite declarations
LPDIRECT3DTEXTURE9 sprite; // the pointer to the sprite
// function prototypes
void initD3D(HWND hWnd); // sets up and initializes Direct3D
void render_frame(void); // renders a single frame
void cleanD3D(void); // closes Direct3D and releases memory
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HWND hWnd;
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = L"WindowClass";
RegisterClassEx(&wc);
hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our Direct3D Program",
WS_EX_TOPMOST | WS_POPUP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
// set up and initialize Direct3D
initD3D(hWnd);
// enter the main loop:
MSG msg;
while(TRUE)
{
DWORD starting_point = GetTickCount();
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
render_frame();
// check the 'escape' key
if(KEY_DOWN(VK_ESCAPE))
PostMessage(hWnd, WM_DESTROY, 0, 0);
while ((GetTickCount() - starting_point) < 25);
}
// clean up DirectX and COM
cleanD3D();
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;
}
return DefWindowProc (hWnd, message, wParam, lParam);
}
// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
d3d = Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = FALSE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferWidth = SCREEN_WIDTH;
d3dpp.BackBufferHeight = SCREEN_HEIGHT;
// create a device class using this information and the info from
the d3dpp stuct
d3d->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
&d3ddev);
D3DXCreateSprite(d3ddev, &d3dspt); // create
the Direct3D Sprite object
D3DXCreateTextureFromFileEx(d3ddev, // the device
pointer
L"Panel3.png", // the file name
D3DX_DEFAULT, // default width
D3DX_DEFAULT, // default height
D3DX_DEFAULT, // no mip mapping
NULL, // regular usage
D3DFMT_A8R8G8B8, // 32-bit pixels with
alpha
D3DPOOL_MANAGED, // typical memory handling
D3DX_DEFAULT, // no filtering
D3DX_DEFAULT, // no mip filtering
D3DCOLOR_XRGB(255, 0, 255), // the hot-pink
color key
NULL, // no image info struct
NULL, // not using 256 colors
&sprite); // load to sprite
return;
}
// this is the function used to render a single frame
void render_frame(void)
{
// clear the window to a deep blue
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 0, 0),
1.0f, 0);
d3ddev->BeginScene(); // begins the 3D scene
d3dspt->Begin(D3DXSPRITE_ALPHABLEND);
// // begin sprite drawing with transparency
// DRAW THE SPRITE FRAME
// count from 0 to 22 to determine the current frame
static int frame = 21; // start the program on the final frame
if(KEY_DOWN(VK_SPACE)) frame=0; // when the space key is pressed, start at frame 0
if(frame < 21) frame++; // if we aren't on the last frame, go to the next frame
// calculate the x-position
int xpos = frame * 182 + 1;
// draw the selected frame using the coordinates
RECT part;
SetRect(&part, xpos, 0, xpos + 181, 128);
D3DXVECTOR3 center(0.0f, 0.0f, 0.0f); // center at the
upper-left corner
D3DXVECTOR3 position(50.0f, 50.0f, 0.0f); // position
at 50, 50 with no depth
d3dspt->Draw(sprite, &part, ¢er,
&position, D3DCOLOR_ARGB(127, 255, 255, 255));
d3dspt->End(); // end sprite drawing
d3ddev->EndScene(); // ends the 3D scene
d3ddev->Present(NULL, NULL, NULL, NULL);
return;
}
// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
sprite->Release();
d3ddev->Release();
d3d->Release();
return;
}
Like I said, there isn't much DirectX involved in upgrading to animation.
The math that is done is mostly there to figure out which frame should be displayed,
then to run through an algorithm to figure out where that frame is located.
However, go ahead and download the animation image and run the program to get this
result (animated of course):

Image 2.5 - The Animated Grid
With animation we can make the game display look fabulous. However, it takes
lots of practice and artisitic skills to make a
good animated game display.
In addition to a game display, you can also start building 2D games using animated
graphics. I will soon be providing various graphical chipsets you can use
to start building your own games if you don't have access to any.
In the meantime, practice with animation by doing these exercises:
1. Get the grid to open, and then close again when the space bar is pressed
a second time.
2. Have two panels display, each opening and closing with their own keys.
3. Rearrange the frames in the image file so as to descrease its file size.
4. Recode your program to compensate the change.
5. Create your own animation, either a panel or some other animation, and
get it animated in DirectX.
Very shortly we are going to see how to build an actual game display, but before we get there I want to talk a little bit about drawing text.
Next Lesson: Adding Text to the Display
GO! GO! GO!
© 2006-2009 DirectXTutorial.com. All Rights Reserved.