DirectX And Game Programming Organization

Started by
2 comments, last by bglanzer 11 years, 10 months ago
Hey guys, i'm just starting some actual programming with DirectX, and I was hoping someone could go over my code so far and tell me how well it's organized, and how well I could fix it. Thanks! All the program does so far is display a sprite that moves itself across the screen.

[spoiler]//main source code file. contains windows api code


#include <d3d9.h>
#include <d3dx9.h>
#include <ctime>
#include <cstdio>
#include "dxgraphics.h"
#include "game.h"



//window event callback function
LRESULT WINAPI WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
//release the DIrect3D Device
if(d3ddev != NULL)
d3ddev->Release();

//release the direct3d object'
if(d3d != NULL)
d3d->Release();

//call the "font end" shutdown function
gameEnd(hWnd);

//tell windows to kill this program
PostQuitMessage(0);
return 0;
}

return DefWindowProc(hWnd, msg, wParam, lParam);
}




//helper function to set up the window properties
ATOM MyRegisterClass(HINSTANCE hInstance)
{
//create the window class structure
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);

//fill the struct with info
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = GAMETITLE;
wc.hIconSm = NULL;

//set up the window with the class info
return RegisterClassEx(&wc);
}







//entry point for a windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
HWND hWnd;

//register the class
MyRegisterClass(hInstance);

//set up the screen in windowed or fullscreen mode?
DWORD style;

if(FULLSCREEN)
style = WS_EX_TOPMOST | WS_VISIBLE | WS_POPUP;
else
style = WS_OVERLAPPEDWINDOW;


//create a new window
hWnd = CreateWindow(
GAMETITLE, //window class
GAMETITLE, //title bar
style, //windows style
CW_USEDEFAULT, // x position of the window
CW_USEDEFAULT, //y positin of the window
1280, //width
720, //height of the window
NULL, //parent window
NULL, //menu
hInstance, //application instance
NULL ); // window parameters

//was there an error creating the window?
if(!hWnd)
return false;

//display the window
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

if(!Init_Direct3D(hWnd, SCREEN_WIDTH, SCREEN_HEIGHT, FULLSCREEN))
return 0;

//initialize the game
if(!gameInit(hWnd))
{
MessageBoxA(hWnd, "Error initializing game", "Error", MB_OK);
return 0;
}

//main message loop
int done = 0;
while(!done)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
//look for quit message
if(msg.message == WM_QUIT)
done = 1;

//decode and pass messages on to WndProc
TranslateMessage(&msg);
DispatchMessage(&msg);
}

else
//process game loop (else prevents from running after window is closed)
gameRun(hWnd);
}

return msg.wParam;

}


/GAME.h
#ifndef GAME_H
#define GAME_H



#include <d3d9.h>
#include <d3dx9.h>
#include <d3dx9math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>

#include "dxgraphics.h"
#include "sprite.h"
#include "player.h"




//application title
#define GAMETITLE "Zombie Game"

//screen set up
#define FULLSCREEN 0 //0 = windowed, 1 = fullscreen
#define SCREEN_WIDTH 1280
#define SCREEN_HEIGHT 720

//function prototypes
int gameInit(HWND);
void gameRun(HWND);
void gameEnd(HWND);


#endif


//GAME.CPP
#include "game.h"
#include "sprite.h"



//Sprite Texture Objects (Holds full sprite sheet bmp)
LPDIRECT3DTEXTURE9 main_character = NULL; //Sprite sheet for main character
SPRITE mainCharacter;
LPDIRECT3DSURFACE9 back;
LPD3DXSPRITE sprite_handler;

HRESULT result;

//timing variable
long start = GetTickCount();





//initialize the game
int gameInit(HWND hwnd)
{
//set random number seed
srand(time(NULL));

//create sprite handler object
result = D3DXCreateSprite(d3ddev, &sprite_handler);
if(result != D3D_OK)
return 0;


//load the main character sprite sheet
main_character = LoadTexture("mainCharacter.bmp", D3DCOLOR_XRGB(255, 0, 255));
if(main_character == NULL)
return 0;

back = LoadSurface("background.bmp", NULL);

//initializes the sprites properties
mainCharacter.x = 100;
mainCharacter.y = 150;
mainCharacter.width = 64;
mainCharacter.height = 64;
mainCharacter.curframe = 0;
mainCharacter.lastframe = 1;
mainCharacter.animdelay = 8;
mainCharacter.animcount = 0;
mainCharacter.movex = 5;
mainCharacter.movey = 0;

return 1;
}



//the main game loop
void gameRun(HWND hwnd)
{
//make sure the d3d device is valid
if(d3ddev == NULL)
return;

//after short delay, ready for next frame?
//this keeps the game running at a steady frame rate
if(GetTickCount() - start >= 30)
{
//reset timing
start = GetTickCount();

//move the sprite
mainCharacter.x += mainCharacter.movex;
mainCharacter.y += mainCharacter.movey;

//warp the sprite on the edges of the screen
if(mainCharacter.x > SCREEN_WIDTH - mainCharacter.width)
mainCharacter.x = 0;
if(mainCharacter.x < 0)
mainCharacter.x = SCREEN_WIDTH - mainCharacter.width;

//has animation delay reached threshold?
if(++mainCharacter.animcount > mainCharacter.animdelay)
{
//reset counter
mainCharacter.animcount = 0;

//animate the sprite
if(++mainCharacter.curframe > mainCharacter.lastframe)
mainCharacter.curframe = 0;
}

}

//start rendering
if(d3ddev->BeginScene())
{
//erase the entire backgoround
d3ddev->StretchRect(back, NULL, backbuffer, NULL, D3DTEXF_NONE);

//start sprite handler
sprite_handler->Begin(D3DXSPRITE_ALPHABLEND);

//create vector to update sprite position
D3DXVECTOR3 position((float)mainCharacter.x, (float)mainCharacter.y, 0);

//configure the rect for the source file
RECT srcRect;
int columns = 2;

srcRect.left = (mainCharacter.curframe % columns) * mainCharacter.width;
srcRect.top = (mainCharacter.curframe / columns) * mainCharacter.height;
srcRect.right = srcRect.left + mainCharacter.width;
srcRect.bottom = srcRect.top + mainCharacter.height;

//draw the sprite
sprite_handler->Draw(
main_character,
&srcRect,
NULL,
&position,
D3DCOLOR_XRGB(255, 255, 255));

//stop drawing
sprite_handler->End();

//stop rendering
d3ddev->EndScene();
}

//display the back buffer on the screen
d3ddev->Present(NULL, NULL, NULL, NULL);

}


void gameEnd(HWND hwnd)
{
if(back != NULL)
back->Release();

if(sprite_handler != NULL)
sprite_handler->Release();

if(main_character != NULL)
main_character->Release();
}


//SPRITE.H
#ifndef SPRITE_H
#define SPRITE_H




//sprite structure
typedef struct
{
int x, y;
int width, height;
int movex, movey;
int curframe, lastframe;
int animdelay, animcount;
} SPRITE;

//function prototypes
int Collision(SPRITE sprite1, SPRITE sprite2); //collision detection function



#endif


//SPRITE.CPP
#include "sprite.h"
#include <Windows.h>
#include <d3d9.h>




//collision function. tests for collision
int Collision(SPRITE sprite1, SPRITE sprite2)
{
RECT rect1;
rect1.left = sprite1.x + 1;
rect1.top = sprite1.y + 1;
rect1.right = sprite1.x + sprite1.width - 1;
rect1.bottom = sprite1.y + sprite1.height - 1;

RECT rect2;
rect2.left = sprite2.x + 1;
rect2.top = sprite2.y + 1;
rect2.right = sprite2.x + sprite2.width - 1;
rect2.bottom = sprite2.y + sprite2.height -1;

RECT dest;
return IntersectRect(&dest, &rect1, &rect2);
}






//dxgraphics.h
#ifndef DXGRAPHICS_H
#define DXGRAPHICS_H




//function prototypes
int Init_Direct3D(HWND, int, int, int);
LPDIRECT3DSURFACE9 LoadSurface(char*, D3DCOLOR);
LPDIRECT3DTEXTURE9 LoadTexture(char*, D3DCOLOR);

//variable declarations
extern LPDIRECT3D9 d3d;
extern LPDIRECT3DDEVICE9 d3ddev;
extern LPDIRECT3DSURFACE9 backbuffer;

#endif


//DXGRAPHICS.CPP
#include <d3d9.h>
#include <d3dx9.h>
#include "dxgraphics.h"

//variable declarations
LPDIRECT3D9 d3d = NULL;
LPDIRECT3DDEVICE9 d3ddev = NULL;
LPDIRECT3DSURFACE9 backbuffer = NULL;




int Init_Direct3D(HWND hwnd, int width, int height, int fullscreen)
{
//initialize direct3d
d3d = Direct3DCreate9(D3D_SDK_VERSION);
if(d3d == NULL)
{
MessageBoxA(hwnd, "Error init-ing Direct3D", "Error", MB_OK);
return 0;
}

D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));

d3dpp.Windowed = (!fullscreen);
d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferWidth = width;
d3dpp.BackBufferHeight = height;
d3dpp.hDeviceWindow = hwnd;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

//create the Direct3D device
d3d->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hwnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
&d3ddev);

if(d3ddev == NULL)
{
MessageBoxA(hwnd, "Error creating Direct3D device", "Error", MB_OK);
return 0;
}

//clear the back buffer to black
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

//create pointer to the back buffer
d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);

return 1;
}



LPDIRECT3DSURFACE9 LoadSurface(char* filename, D3DCOLOR transcolor)
{
LPDIRECT3DSURFACE9 image = NULL;
D3DXIMAGE_INFO info;
HRESULT result;

//get width and height from bitmap file
result = D3DXGetImageInfoFromFile(filename, &info);
if(result != D3D_OK)
return NULL;

//create surface
result = d3ddev->CreateOffscreenPlainSurface(
info.Width, //width of the surface
info.Height, //height of the surface
D3DFMT_X8R8G8B8, //surface format
D3DPOOL_DEFAULT, //memory pool to use
&image, //pointer to the surface
NULL); //reserved(always NULL)

if(result != D3D_OK)
return NULL;

//load surface from file into newly created surface
result = D3DXLoadSurfaceFromFile(
image, //destination surface
NULL, //destionation pallatte
NULL, //destionation rectangle
filename, //source filename
NULL, //source rectangle
D3DX_DEFAULT, //controls how the image is filtered
transcolor, //for transparency (0 for none)
NULL); //source mage info(USUALLY NULL)

//make sure the file was loaded okay
if(result != D3D_OK)
return NULL;

return image;
}


LPDIRECT3DTEXTURE9 LoadTexture(char *filename, D3DCOLOR transcolor)
{
//the texture poitner
LPDIRECT3DTEXTURE9 texture = NULL;

//the struct for reading bitmap file info
D3DXIMAGE_INFO info;

//standard Windows return value
HRESULT result;

//get width and height from bitmap file
result = D3DXGetImageInfoFromFile(filename, &info);
if(result != D3D_OK)
return NULL;

//create the new texture by loading a bitmap image file
D3DXCreateTextureFromFileEx(
d3ddev, //Drect3D device object
filename, //bitmap filename
info.Width, //bitmap image width
info.Height, //bitmap image height
1, //mip map levels(1 for no chain)
D3DPOOL_DEFAULT, // the type of surface (standard)
D3DFMT_UNKNOWN, //surface format (default)'
D3DPOOL_DEFAULT, //memory class for the texture
D3DX_DEFAULT, //image filter
D3DX_DEFAULT, //mip filter
transcolor, //color key for transparency
&info, //bitmap file info
NULL, //color palatte
&texture); //destination texture

//make sure the bitmap texture was loaded correctly
if(result != D3D_OK)
return NULL;

return texture;
}
[/spoiler]
Advertisement
There are lots of ways to do things and one way is not always best. Some methods may have advantages over others though. In your case you want to think about mobility and reusability. What if you wanted to make another game? You would have to retype or cut and paste all of your windows and directx initiallization over again. It might be easier to wrap everything up in classes and built into a library that way you can reuse it and spend more time programming gameplay rather than typing code to access hardware.

Brendon Glanzer

Well, aside from the sprite programming, I have the DirectX code set aside to reuse. What I was mainly asking though, which I see may have been a bit confusing, was that I was looking for advice on how to just make what I have so far cleaner, and maybe a better way to put all the sprite stuff into one class. I realize that there are many ways to do things, but I was looking for opinions on how to just make this code cleaner.
In the case of your sprite I think its pretty good. Here is simple quick example of a sprite class I did in the past, just to give you an example of animated sprites

Hopefully the classes are understandable. Pretty much because animations can run at different speeds and a sprite can have many different animations you want to separate your sprite animations. Also it makes it easier to put things like positions and scale into class called Transform which is derived from a Component class. Then allow additional
components to be added that hold things like velocity, speed, bounding box, etc.

Currently what you have is good. But when you want to create more complex sprites below is a structure that worked for me.

****EDIT**** On second thought you might want Transform and Component under a GameObject class which Sprite is derived from.


class SpriteAnimation {
public:
//Set methods, Get methods, update method
protected:
int m_iCellWidth;
int m_iCellHeight;
int m_iFirstFrame;
int m_iLastFrame;
int m_iCurrentFrame;
int m_iAnimationDirection; // 1 for forward -1 for backward

bool m_bLoop;
bool m_bAnimating;
bool m_bCustomFrameList;

float m_fFrameDelay;
float m_fTimer;
TArray<int> customFrameArray;
};

class Sprite {
public:
//Constructors, Destructors, Setters, Getters
bool AddAnimation( SpriteAnimation* animation );
bool AddComponent( Component* component);

protected:
Transform m_transform; //transform hold position, scale, ect
TArray<Component *> m_components; //Holds any components
TArray<SpriteAnimations *> m_animations; //holds all animations
};

Brendon Glanzer

This topic is closed to new replies.

Advertisement