Click here to Skip to main content
15,886,199 members
Articles / Multimedia / DirectX
Article

SpaceWarrior - 2D DirectDraw Game - Part III

Rate me:
Please Sign up or sign in to vote.
2.82/5 (13 votes)
3 Jan 20073 min read 46.3K   30   6
An article on creating a simple 2D DirectDraw game (the DirectX).

Introduction

I will speak here about the heart of the book, Tricks of the Windows Game Programming Gurus, by Andre LaMothe - the DirectX things. This is the place where the Game_Init() and Game_Shutdown() functions (from the part I of this tutorial) will be explained.

The DirectX sub-system

The DirectX has a few (in)dependent components:

  • DirectDraw
  • DirectSound
  • DirectSound3D
  • DirectMusic
  • DirectPlay
  • DirectInput
  • Direct3DIM
  • Direct3DRM
  • DirectSetup

and it is placed just above the hardware we need. Each of these components is connected to some hardware piece inside the computer. DirectDraw represents the graphics card. DirectSound plays digital sounds (WAV files etc.). DirectMusic deals with MIDI files. DirectInput handles input devices like keyboard, mouse, joystick etc. DirectPlay is used to create network (multiplayer) games. Direct3DIM and Direct3DRM support hardware accelerated rendering of 3D computer graphics.

In our game, we will need just the first DirectX component, for now. Later, we'll add sounds and music.

DirectX initialization

It is very easy to initialize the DirectX sub-system. See below (the code should be inside the Game_Init() function):

// Required DirectX includes
#include "ddraw.h"


LPDIRECTDRAW g_lpdd;

// Create base IDirectDraw interface
if (FAILED(DirectDrawCreate(NULL, &g_lpdd, NULL)))
{
     return (0);
}


// Query for IDirectDraw4 interface (or some other newer interface)
LPDIRECTDRAW g_lpdd4;
if (FAILED(g_lpdd->QueryInterface(IID_IDirectDraw4, (LPVOID*)&g_lpdd4)))
{
     return (0);
}

You have initialized your video card now. Let's set some params:

// Set cooperative level to fullscreen
if (FAILED(g_lpdd4->SetCooperativeLevel(g_hMainWnd, 
    DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | 
    DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT)))
{
    return (0);
}

// Set display mode to SCREEN_WIDTH x SCREEN_HEIGHT x SCREEN_BPP
if (FAILED(g_lpdd4->SetDisplayMode(SCREEN_WIDTH, 
           SCREEN_HEIGHT, SCREEN_BPP, 0, 0)))
{
    return (0);
}

Now, we have created a full screen window with params: SCREEN_WIDTH x SCREEN_HEIGHT x SCREEN_BPP. If there is an error, the window will not be created and the function will return the value 0.

Now, we need the so called DirectX surface. There are three types of DirectX surfaces: primary, secondary, and offscreen surface. The primary surface is a primary raster display (monitor screen). The secondary surface is attached to the primary, and is used for flicker-free drawing on the screen. The offscreen surfaces hold sprites in the video memory. Here are the primary and the secondary surfaces:

// Fill primary surface descriptor
DDSURFACEDESC2 g_ddsd;
memset(&g_ddsd, 0, sizeof(DDSURFACEDESC2));
g_ddsd.dwSize = sizeof(DDSURFACEDESC2);
g_ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
g_ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | 
                        DDSCAPS_COMPLEX | DDSCAPS_FLIP;
g_ddsd.dwBackBufferCount = 1;

// Create IDirectDrawSurface4 (primary surface) interface
LPDIRECTDRAWSURFACE4 g_lpddsPrimary;
if (FAILED(g_lpdd4->CreateSurface(&g_ddsd, &g_lpddsPrimary, NULL)))
{
     return (0);
}
    
// Query for attached back-buffer surface
g_ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
LPDIRECTDRAWSURFACE4 g_lpddsBack;
if (FAILED(g_lpddsPrimary->GetAttachedSurface(&g_ddsd.ddsCaps, 
                                              &g_lpddsBack)))
{
     return (0);
}

Finally, we must set the drawing-clipping region for the secondary surface. No drawing will be allowed outside this clipping region.

// Attach clipper to the surface
RECT <CODE>rect_list</CODE>[1] = {{0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}};
LPDIRECTDRAWCLIPPER g_lpddClipper;
if (!(g_lpddClipper=Attach_Clipper(g_lpddsBack, 1, rect_list)))
{
    return (0);
}

DirectX rendering

After we have initialized the DirectX sub-system, we can perform the drawing on the DirectX back-buffer surface, the attached surface of the primary surface. In the book, there are many useful functions defined to complete this job, like:

  • Pixel drawing
  • Line drawing
  • Polygon drawing
  • Text drawing
  • Polygon filling
  • Bitmap blitting

Here are the definitions of the drawing functions:

void Plot_Pixel(int x, int y, UCHAR color, 
     LPVOID* pBuffer, int mempitch);
void Plot_Pixel(int x, int y, UCHAR red, 
     UCHAR green, UCHAR blue, UCHAR alpha, LPVOID* pBuffer, int mempitch);
void Blit_Bitmap(BITMAP_FILE_PTR bitmap, int x, int y, 
     int surfaceWidth, int surfaceHeight, LPVOID* pVideoBuffer, int mempitch);
int Draw_Text(char* text, int x, int y, UCHAR color, 
    LPDIRECTDRAWSURFACE4 lpdds);
void Draw_Line(int x0, int y0, int x1, int y1, 
     DWORD color, LPVOID* pBuffer, int mempitch);
int Draw_Polygon(POLYGON2D_PTR poly, RECT clipRect, 
    LPVOID* pBuffer, int mempitch);
int Fill_Polygon(POLYGON2D_PTR poly, RECT clipRect, 
    LPVOID* pBuffer, int mempitch);
void Fill_Top_Triangle(int x1, int y1, int x2, int y2, int x3, 
     int y3, DWORD color, RECT clipRect, LPVOID* pBuffer, int mempitch);
void Fill_Bottom_Triangle(int x1, int y1, int x2, int y2, int x3, 
     int y3, DWORD color, RECT clipRect, LPVOID* pBuffer, int mempitch);
void Fill_Triangle(int x1, int y1, int x2, int y2, int x3, int y3, 
     DWORD color, RECT clipRect, LPVOID* pBuffer, int mempitch);

So, using these basic functions, we can perform high-speed rendering on DirectDraw surfaces. The other parts are transformation functions. See below:

int Translate_Polygon(POLYGON2D_PTR poly, int dx, int dy);
int Rotate_Polygon(POLYGON2D_PTR poly, int theta);
int Scale_Polygon(POLYGON2D_PTR poly, float sx, float sy);

There is a set of bitmap manipulation functions:

int Load_Bitmap(BITMAP_FILE_PTR bitmap, char* filename);
int Unload_Bitmap(BITMAP_FILE_PTR bitmap);

See the defined structs used above:

typedef struct _BITMAP_FILE_TAG
{
    BITMAPFILEHEADER bitmapFileHeader;
    BITMAPINFOHEADER bitmapInfoHeader;
    PALETTEENTRY palette[256];
    UCHAR* buffer;

} BITMAP_FILE, *BITMAP_FILE_PTR;


typedef struct VERTEX2DF_TYP
{
    float x, y;

} VERTEX2DF, *VERTEX2DF_PTR;


typedef struct POLYGON2D_TYP
{
    int state;
    int num_verts;
    int x0, y0;
    int xv, yv;
    DWORD color;
    VERTEX2DF* vlist;

} POLYGON2D, *POLYGON2D_PTR;

I have added a set of functions that deals with sprites:

int Sprite_Load(char* spriteBitmap, int spriteWidth, int spriteHeight);
int Sprite_Unload(int spriteID);

And also with objects:

int Object_Create(int spriteID, float posX, float posY, 
           float velX, float velY, int animationTime, 
           int startFrame, int endFrame, ANIMATION_TYPE animationType);
int Object_Destroy(int objectID);
int Object_GetSpeed(int objectID, float& velX, float& velY);
int Object_SetSpeed(int objectID, float velX, float velY);
int Object_GetPosition(int objectID, float& posX, float& posY);
int Object_SetPosition(int objectID, float posX, float posY);
int Object_SetAnimation(int objectID, int startFrame, 
           int endFrame, ANIMATION_TYPE animationType);
int Object_GetSprite(int objectID);

Here are the defined structures that hold sprite and object information:

typedef struct _SPRITE_TAG
{
     int spriteCols;
     int spriteWidth;
     int spriteHeight;
     LPDIRECTDRAWSURFACE4 lpddsSprite;

} SPRITE_TAG, *SPRITE_TAG_PTR;


typedef enum ANIMATION_TYPE
{
    AT_STOP = 0,
    AT_REPEAT,
    AT_DIE
};

typedef struct _OBJECT_TAG
{
    int spriteID;
    float posX;
    float posY;
    float velX;
    float velY;
    int animationTime;
    int currentTime;
    int startFrame;
    int endFrame;
    int currentFrame;
    ANIMATION_TYPE animationType;

} OBJECT_TAG, *OBJECT_TAG_PTR;

Most of these structs are defined just for this game, and shouldn't be considered as a general implementation but just as an example. The model is the following: the sprite is a unique object that holds a sprite tile set bitmap. It is created just once. However, you can create a number of objects that use the same sprite (like the asteroids in this game). Considering the animation, you can program whatever you like when it comes to this point.

DirectX shutdown

To shutdown the DirectX sub-system, do the following (the code should be inside the Game_Shutdown() function):

// Release IDirectDrawClipper interface
if (g_lpddClipper)
{
    g_lpddClipper->Release();
    g_lpddClipper = NULL;
}

// Release IDirectDrawSurface4 (primary surface) interface
if (g_lpddsPrimary)
{
    g_lpddsPrimary->Release();
    g_lpddsPrimary = NULL;
}

// Release IDirectDraw4 interface
if (g_lpdd4)
{
    g_lpdd4->Release();
    g_lpdd4 = NULL;
}

// Release base IDirectDraw interface
if (g_lpdd)
{
    g_lpdd->Release();
    g_lpdd = NULL;
}

After this code, you can go back safely to Windows.

Conclusion

You have seen here all the important parts of DirectX, and a library wrapper that makes things more simple when it comes to using DirectX in your applications (games).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) Elektromehanika d.o.o. Nis
Serbia Serbia
He has a master degree in Computer Science at Faculty of Electronics in Nis (Serbia), and works as a C++/C# application developer for Windows platforms since 2001. He likes traveling, reading and meeting new people and cultures.

Comments and Discussions

 
QuestionWhy didn't you combine the articles?? Pin
Ramon Smits3-Jan-07 21:05
Ramon Smits3-Jan-07 21:05 
AnswerRe: Why didn't you combine the articles?? Pin
darkoman3-Jan-07 23:06
darkoman3-Jan-07 23:06 
GeneralRe: Why didn't you combine the articles?? Pin
Shawn Poulson4-Jan-07 1:59
Shawn Poulson4-Jan-07 1:59 
GeneralRe: Why didn't you combine the articles?? Pin
worksopbenny8-Jan-07 12:51
worksopbenny8-Jan-07 12:51 
AnswerRe: Why didn't you combine the articles?? Pin
ednrgc9-Jan-07 2:24
ednrgc9-Jan-07 2:24 
GeneralRe: Why didn't you combine the articles?? Pin
Ramon Smits9-Jan-07 2:40
Ramon Smits9-Jan-07 2:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.