先说一下学习这本书的初衷,现在的游戏开发一般都会直接使用游戏引擎来渲染和组织一些功能,笔者以前只用过u3d做过一次cs场景类的程序(严格来说不是游戏。。。)但是作为被封装好的引擎,作为初学者不能更好的理解引擎内部的运行机制,所以我打算用很久的空余时间来学习这本书。不使用引擎,自己做渲染等,这是一个挑战,但相信对长远发展会有帮助。
大师的代码最好使用dx7sdk,因为这本书的许多程序需要调用d3dim.lib直接模式(或立即模式)导入库,他是D3D immediatel mode使用的LIB,而在dx8.0(包括8.0)的以上版本取消了这种区别,也就是取消了这个库,所以在vs2010中会提示缺少d3dim.lib库。
所以推荐使用版本
dx7sdk-7001
编译器这本书使用的是vc++6.0,但是现在都是vc有点老了,所以我使用了vs2010,话说我现在才知道微软以前的编译器叫vc,在6.0之后的版本就开始叫vs了。。
这次主要的目标就是在vs2010上搭建我们的开发环境。
开始:
首先安装dx7sdk-7001
选择好使用的路径,点击unzip就可以了,实际上dx7.0作为很古老的版本,我们使用的也仅仅是它的一些include,lib文件。
然后在vs2010中配置dx环境
打开vs2010新建一个空的win32项目
然后右键点击这个项目
选择属性
在配置属性里面
选择vc++目录,我们需要修改的是第二行的包含目录和库目录。
我这里把刚才安装的dx7.0放在了d盘,以此为例。
包含目录修改成:
$(IncludePath);D:\dx7sdk-700.1\include
注意一定要把$(IncludePath);(原路径)放在最前面,手动修改的时候是默认在后面的。如果放在前面会出现很严重的程序错误。至少vs2010是这么不智能的。。。
库目录修改成
$(LibraryPath);D:\dx7sdk-700.1\lib;D:\dx7sdk-700.1\lib\borland
这三个路径同样需要把$(LibraryPath);这个原路径放在最前面,稍微提及一下dx7.0的borland产品,在使用声音等处理的时候还是加上这个库,他在dx7.0中的lib文件中的一个子文件。
修改完之后点击应用即可。
做完这些并不可以,还需要修改连接器的lib
同样是选择工程的右键属性点击链接器,选择输入属性,然后我们会看到附加依赖值这一项
点击编辑一项之后
我们需要添加的项目是
ddraw.lib
dinput.lib
dinput8.lib
dsound.lib
d3dim.lib
dxguid.lib
winmm.lib
这几项,d3dim.lib是dx7.0特有的文件,所以推荐使用dx7.0不然在本书的大多程序会报错。
修改完这些之后,大功告成,开始我们的开发之旅把。
第一个跑起来的游戏就从打砖块开始把:(因为没有引擎所以使用了blackbox.h进行渲染)
// FREAKOUT.CPP - break game demo
// INCLUDES ///
#define WIN32_LEAN_AND_MEAN // include all macros
#define INITGUID // include all GUIDs
#include <windows.h> // include important windows stuff
#include <windowsx.h>
#include <mmsystem.h>
#include <iostream> // include important C/C++ stuff
#include <conio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
#include <io.h>
#include <fcntl.h>
#include <ddraw.h> // directX includes
#include "blackbox.h" // game library includes
// DEFINES
// defines for windows
#define WINDOW_CLASS_NAME "WIN3DCLASS" // class name
#define WINDOW_WIDTH 640 // size of window
#define WINDOW_HEIGHT 480
// states for game loop
#define GAME_STATE_INIT 0
#define GAME_STATE_START_LEVEL 1
#define GAME_STATE_RUN 2
#define GAME_STATE_SHUTDOWN 3
#define GAME_STATE_EXIT 4
// block defines
#define NUM_BLOCK_ROWS 6
#define NUM_BLOCK_COLUMNS 8
#define BLOCK_WIDTH 64
#define BLOCK_HEIGHT 16
#define BLOCK_ORIGIN_X 8
#define BLOCK_ORIGIN_Y 8
#define BLOCK_X_GAP 80
#define BLOCK_Y_GAP 32
// paddle defines
#define PADDLE_START_X (SCREEN_WIDTH/2 - 16)
#define PADDLE_START_Y (SCREEN_HEIGHT - 32);
#define PADDLE_WIDTH 32
#define PADDLE_HEIGHT 8
#define PADDLE_COLOR 191
// ball defines
#define BALL_START_Y (SCREEN_HEIGHT/2)
#define BALL_SIZE 4
// PROTOTYPES /
// game console
int Game_Init(void *parms=NULL);
int Game_Shutdown(void *parms=NULL);
int Game_Main(void *parms=NULL);
// GLOBALS
HWND main_window_handle = NULL; // save the window handle
HINSTANCE main_instance = NULL; // save the instance
int game_state = GAME_STATE_INIT; // starting state
int paddle_x = 0, paddle_y = 0; // tracks position of paddle
int ball_x = 0, ball_y = 0; // tracks position of ball
int ball_dx = 0, ball_dy = 0; // velocity of ball
int score = 0; // the score
int level = 1; // the current level
int blocks_hit = 0; // tracks number of blocks hit
// this contains the game grid data
UCHAR blocks[NUM_BLOCK_ROWS][NUM_BLOCK_COLUMNS];
// FUNCTIONS //
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
// this is the main message handler of the system
PAINTSTRUCT ps; // used in WM_PAINT
HDC hdc; // handle to a device context
// what is the message
switch(msg)
{
case WM_CREATE:
{
// do initialization stuff here
return(0);
} break;
case WM_PAINT:
{
// start painting
hdc = BeginPaint(hwnd,&ps);
// the window is now validated
// end painting
EndPaint(hwnd,&ps);
return(0);
} break;
case WM_DESTROY:
{
// kill the application
PostQuitMessage(0);
return(0);
} break;
default:break;
} // end switch
// process any messages that we didn't take care of
return (DefWindowProc(hwnd, msg, wparam, lparam));
} // end WinProc
// WINMAIN
int WINAPI WinMain( HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
// this is the winmain function
WNDCLASS winclass; // this will hold the class we create
HWND hwnd; // generic window handle
MSG msg; // generic message
HDC hdc; // generic dc
PAINTSTRUCT ps; // generic paintstruct
// first fill in the window class stucture
winclass.style = CS_DBLCLKS | CS_OWNDC |
CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName = NULL;
winclass.lpszClassName = WINDOW_CLASS_NAME;
// register the window class
if (!RegisterClass(&winclass))
return(0);
// create the window, note the use of WS_POPUP
if (!(hwnd = CreateWindow(WINDOW_CLASS_NAME, // class
"WIN3D Game Console", // title
WS_POPUP | WS_VISIBLE,
0,0, // initial x,y
GetSystemMetrics(SM_CXSCREEN), // intial width
GetSystemMetrics(SM_CYSCREEN), // initial height
NULL, // handle to parent
NULL, // handle to menu
hinstance,// instance
NULL))) // creation parms
return(0);
// hide mouse
ShowCursor(FALSE);
// save the window handle and instance in a global
main_window_handle = hwnd;
main_instance = hinstance;
// perform all game console specific initialization
Game_Init();
// enter main event loop
while(1)
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
// test if this is a quit
if (msg.message == WM_QUIT)
break;
// translate any accelerator keys
TranslateMessage(&msg);
// send the message to the window proc
DispatchMessage(&msg);
} // end if
// main game processing goes here
Game_Main();
} // end while
// shutdown game and release all resources
Game_Shutdown();
// show mouse
ShowCursor(TRUE);
// return to Windows like this
return(msg.wParam);
} // end WinMain
// T3DX GAME PROGRAMMING CONSOLE FUNCTIONS
int Game_Init(void *parms)
{
// this function is where you do all the initialization
// for your game
// return success
return(1);
} // end Game_Init
///
int Game_Shutdown(void *parms)
{
// this function is where you shutdown your game and
// release all resources that you allocated
// return success
return(1);
} // end Game_Shutdown
///
void Init_Blocks(void)
{
// initialize the block field
for (int row=0; row < NUM_BLOCK_ROWS; row++)
for (int col=0; col < NUM_BLOCK_COLUMNS; col++)
blocks[row][col] = row*16+col*3+16;
} // end Init_Blocks
///
void Draw_Blocks(void)
{
// this function draws all the blocks in row major form
int x1 = BLOCK_ORIGIN_X, // used to track current position
y1 = BLOCK_ORIGIN_Y;
// draw all the blocks
for (int row=0; row < NUM_BLOCK_ROWS; row++)
{
// reset column position
x1 = BLOCK_ORIGIN_X;
// draw this row of blocks
for (int col=0; col < NUM_BLOCK_COLUMNS; col++)
{
// draw next block (if there is one)
if (blocks[row][col]!=0)
{
// draw block
Draw_Rectangle(x1-4,y1+4,
x1+BLOCK_WIDTH-4,y1+BLOCK_HEIGHT+4,0);
Draw_Rectangle(x1,y1,x1+BLOCK_WIDTH,
y1+BLOCK_HEIGHT,blocks[row][col]);
} // end if
// advance column position
x1+=BLOCK_X_GAP;
} // end for col
// advance to next row position
y1+=BLOCK_Y_GAP;
} // end for row
} // end Draw_Blocks
///
void Process_Ball(void)
{
// this function tests if the ball has hit a block or the paddle
// if so, the ball is bounced and the block is removed from
// the playfield note: very cheesy collision algorithm :)
// first test for ball block collisions
// the algorithm basically tests the ball against each
// block's bounding box this is inefficient, but easy to
// implement, later we'll see a better way
int x1 = BLOCK_ORIGIN_X, // current rendering position
y1 = BLOCK_ORIGIN_Y;
int ball_cx = ball_x+(BALL_SIZE/2), // computer center of ball
ball_cy = ball_y+(BALL_SIZE/2);
// test of the ball has hit the paddle
if (ball_y > (SCREEN_HEIGHT/2) && ball_dy > 0)
{
// extract leading edge of ball
int x = ball_x+(BALL_SIZE/2);
int y = ball_y+(BALL_SIZE/2);
// test for collision with paddle
if ((x >= paddle_x && x <= paddle_x+PADDLE_WIDTH) &&
(y >= paddle_y && y <= paddle_y+PADDLE_HEIGHT))
{
// reflect ball
ball_dy=-ball_dy;
// push ball out of paddle since it made contact
ball_y+=ball_dy;
// add a little english to ball based on motion of paddle
if (KEY_DOWN(VK_RIGHT))
ball_dx-=(rand()%3);
else
if (KEY_DOWN(VK_LEFT))
ball_dx+=(rand()%3);
else
ball_dx+=(-1+rand()%3);
// test if there are no blocks, if so send a message
// to game loop to start another level
if (blocks_hit >= (NUM_BLOCK_ROWS*NUM_BLOCK_COLUMNS))
{
game_state = GAME_STATE_START_LEVEL;
level++;
} // end if
// make a little noise
MessageBeep(MB_OK);
// return
return;
} // end if
} // end if
// now scan thru all the blocks and see of ball hit blocks
for (int row=0; row < NUM_BLOCK_ROWS; row++)
{
// reset column position
x1 = BLOCK_ORIGIN_X;
// scan this row of blocks
for (int col=0; col < NUM_BLOCK_COLUMNS; col++)
{
// if there is a block here then test it against ball
if (blocks[row][col]!=0)
{
// test ball against bounding box of block
if ((ball_cx > x1) && (ball_cx < x1+BLOCK_WIDTH) &&
(ball_cy > y1) && (ball_cy < y1+BLOCK_HEIGHT))
{
// remove the block
blocks[row][col] = 0;
// increment global block counter, so we know
// when to start another level up
blocks_hit++;
// bounce the ball
ball_dy=-ball_dy;
// add a little english
ball_dx+=(-1+rand()%3);
// make a little noise
MessageBeep(MB_OK);
// add some points
score+=5*(level+(abs(ball_dx)));
// that's it -- no more block
return;
} // end if
} // end if
// advance column position
x1+=BLOCK_X_GAP;
} // end for col
// advance to next row position
y1+=BLOCK_Y_GAP;
} // end for row
} // end Process_Ball
///
int Game_Main(void *parms)
{
// this is the workhorse of your game it will be called
// continuously in real-time this is like main() in C
// all the calls for you game go here!
char buffer[80]; // used to print text
// what state is the game in?
if (game_state == GAME_STATE_INIT)
{
// initialize everything here graphics
DD_Init(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP);
// seed the random number generator
// so game is different each play
srand(Start_Clock());
// set the paddle position here to the middle bottom
paddle_x = PADDLE_START_X;
paddle_y = PADDLE_START_Y;
// set ball position and velocity
ball_x = 8+rand()%(SCREEN_WIDTH-16);
ball_y = BALL_START_Y;
ball_dx = -4 + rand()%(8+1);
ball_dy = 6 + rand()%2;
// transition to start level state
game_state = GAME_STATE_START_LEVEL;
} // end if
else
if (game_state == GAME_STATE_START_LEVEL)
{
// get a new level ready to run
// initialize the blocks
Init_Blocks();
// reset block counter
blocks_hit = 0;
// transition to run state
game_state = GAME_STATE_RUN;
} // end if
///
else
if (game_state == GAME_STATE_RUN)
{
// start the timing clock
Start_Clock();
// clear drawing surface for the next frame of animation
Draw_Rectangle(0,0,SCREEN_WIDTH-1, SCREEN_HEIGHT-1,200);
// move the paddle
if (KEY_DOWN(VK_RIGHT))
{
// move paddle to right
paddle_x+=8;
// make sure paddle doesn't go off screen
if (paddle_x > (SCREEN_WIDTH-PADDLE_WIDTH))
paddle_x = SCREEN_WIDTH-PADDLE_WIDTH;
} // end if
else
if (KEY_DOWN(VK_LEFT))
{
// move paddle to right
paddle_x-=8;
// make sure paddle doesn't go off screen
if (paddle_x < 0)
paddle_x = 0;
} // end if
// draw blocks
Draw_Blocks();
// move the ball
ball_x+=ball_dx;
ball_y+=ball_dy;
// keep ball on screen, if the ball hits the edge of
// screen then bounce it by reflecting its velocity
if (ball_x > (SCREEN_WIDTH - BALL_SIZE) || ball_x < 0)
{
// reflect x-axis velocity
ball_dx=-ball_dx;
// update position
ball_x+=ball_dx;
} // end if
// now y-axis
if (ball_y < 0)
{
// reflect y-axis velocity
ball_dy=-ball_dy;
// update position
ball_y+=ball_dy;
} // end if
else
// penalize player for missing the ball
if (ball_y > (SCREEN_HEIGHT - BALL_SIZE))
{
// reflect y-axis velocity
ball_dy=-ball_dy;
// update position
ball_y+=ball_dy;
// minus the score
score-=100;
} // end if
// next watch out for ball velocity getting out of hand
if (ball_dx > 8) ball_dx = 8;
else
if (ball_dx < -8) ball_dx = -8;
// test if ball hit any blocks or the paddle
Process_Ball();
// draw the paddle and shadow
Draw_Rectangle(paddle_x-8, paddle_y+8,
paddle_x+PADDLE_WIDTH-8,
paddle_y+PADDLE_HEIGHT+8,0);
Draw_Rectangle(paddle_x, paddle_y,
paddle_x+PADDLE_WIDTH,
paddle_y+PADDLE_HEIGHT,PADDLE_COLOR);
// draw the ball
Draw_Rectangle(ball_x-4, ball_y+4, ball_x+BALL_SIZE-4,
ball_y+BALL_SIZE+4, 0);
Draw_Rectangle(ball_x, ball_y, ball_x+BALL_SIZE,
ball_y+BALL_SIZE, 255);
// draw the info
sprintf(buffer,"F R E A K O U T Score %d Level %d",score,level);
Draw_Text_GDI(buffer, 8,SCREEN_HEIGHT-16, 127);
// flip the surfaces
DD_Flip();
// sync to 33ish fps
Wait_Clock(30);
// check of user is trying to exit
if (KEY_DOWN(VK_ESCAPE))
{
// send message to windows to exit
PostMessage(main_window_handle, WM_DESTROY,0,0);
// set exit state
game_state = GAME_STATE_SHUTDOWN;
} // end if
} // end if
///
else
if (game_state == GAME_STATE_SHUTDOWN)
{
// in this state shut everything down and release resources
DD_Shutdown();
// switch to exit state
game_state = GAME_STATE_EXIT;
} // end if
// return success
return(1);
} // end Game_Main
///
// BLACKBOX.CPP - Game Engine
// INCLUDES ///
#define WIN32_LEAN_AND_MEAN // make sure all macros are included
// 添加这句,告诉链接器链接这个lib文件
#include <windows.h> // include important windows stuff
#include <windowsx.h>
#include <mmsystem.h>
#include <iostream> // include important C/C++ stuff
#include <conio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
#include <io.h>
#include <fcntl.h>
#include <ddraw.h> // directX includes
#include "blackbox.h" // game library includes
// DEFINES
// TYPES //
// PROTOTYPES /
// EXTERNALS //
extern HWND main_window_handle; // save the window handle
extern HINSTANCE main_instance; // save the instance
// GLOBALS
LPDIRECTDRAW7 lpdd = NULL; // dd object
LPDIRECTDRAWSURFACE7 lpddsprimary = NULL; // dd primary surface
LPDIRECTDRAWSURFACE7 lpddsback = NULL; // dd back surface
LPDIRECTDRAWPALETTE lpddpal = NULL; // a pointer to the created dd palette
LPDIRECTDRAWCLIPPER lpddclipper = NULL; // dd clipper
PALETTEENTRY palette[256]; // color palette
PALETTEENTRY save_palette[256]; // used to save palettes
DDSURFACEDESC2 ddsd; // a direct draw surface description struct
DDBLTFX ddbltfx; // used to fill
DDSCAPS2 ddscaps; // a direct draw surface capabilities struct
HRESULT ddrval; // result back from dd calls
DWORD start_clock_count = 0; // used for timing
// these defined the general clipping rectangle
int min_clip_x = 0, // clipping rectangle
max_clip_x = SCREEN_WIDTH-1,
min_clip_y = 0,
max_clip_y = SCREEN_HEIGHT-1;
// these are overwritten globally by DD_Init()
int screen_width = SCREEN_WIDTH, // width of screen
screen_height = SCREEN_HEIGHT, // height of screen
screen_bpp = SCREEN_BPP; // bits per pixel
// FUNCTIONS //
int DD_Init(int width, int height, int bpp)
{
// this function initializes directdraw
int index; // looping variable
// create object and test for error
if (DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL)!=DD_OK)
return(0);
// set cooperation level to windowed mode normal
if (lpdd->SetCooperativeLevel(main_window_handle,
DDSCL_ALLOWMODEX | DDSCL_FULLSCREEN |
DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT)!=DD_OK)
return(0);
// set the display mode
if (lpdd->SetDisplayMode(width,height,bpp,0,0)!=DD_OK)
return(0);
// set globals
screen_height = height;
screen_width = width;
screen_bpp = bpp;
// Create the primary surface
memset(&ddsd,0,sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
// we need to let dd know that we want a complex
// flippable surface structure, set flags for that
ddsd.ddsCaps.dwCaps =
DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
// set the backbuffer count to 1
ddsd.dwBackBufferCount = 1;
// create the primary surface
lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL);
// query for the backbuffer i.e the secondary surface
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
lpddsprimary->GetAttachedSurface(&ddscaps,&lpddsback);
// create and attach palette
// create palette data
// clear all entries defensive programming
memset(palette,0,256*sizeof(PALETTEENTRY));
// create a R,G,B,GR gradient palette
for (index=0; index < 256; index++)
{
// set each entry
if (index < 64)
palette[index].peRed = index*4;
else // shades of green
if (index >= 64 && index < 128)
palette[index].peGreen = (index-64)*4;
else // shades of blue
if (index >= 128 && index < 192)
palette[index].peBlue = (index-128)*4;
else // shades of grey
if (index >= 192 && index < 256)
palette[index].peRed = palette[index].peGreen =
palette[index].peBlue = (index-192)*4;
// set flags
palette[index].peFlags = PC_NOCOLLAPSE;
} // end for index
// now create the palette object
if (lpdd->CreatePalette(DDPCAPS_8BIT | DDPCAPS_INITIALIZE | DDPCAPS_ALLOW256,
palette,&lpddpal,NULL)!=DD_OK)
return(0);
// attach the palette to the primary
if (lpddsprimary->SetPalette(lpddpal)!=DD_OK)
return(0);
// clear out both primary and secondary surfaces
DD_Fill_Surface(lpddsprimary,0);
DD_Fill_Surface(lpddsback,0);
// attach a clipper to the screen
RECT screen_rect = {0,0,screen_width,screen_height};
lpddclipper = DD_Attach_Clipper(lpddsback,1,&screen_rect);
// return success
return(1);
} // end DD_Init
///
int DD_Shutdown(void)
{
// this function release all the resources directdraw
// allocated, mainly to com objects
// release the clipper first
if (lpddclipper)
lpddclipper->Release();
// release the palette
if (lpddpal)
lpddpal->Release();
// release the secondary surface
if (lpddsback)
lpddsback->Release();
// release the primary surface
if (lpddsprimary)
lpddsprimary->Release();
// finally, the main dd object
if (lpdd)
lpdd->Release();
// return success
return(1);
} // end DD_Shutdown
///
LPDIRECTDRAWCLIPPER DD_Attach_Clipper(LPDIRECTDRAWSURFACE7 lpdds,
int num_rects,
LPRECT clip_list)
{
// this function creates a clipper from the sent clip list and attaches
// it to the sent surface
int index; // looping var
LPDIRECTDRAWCLIPPER lpddclipper; // pointer to the newly created dd clipper
LPRGNDATA region_data; // pointer to the region data that contains
// the header and clip list
// first create the direct draw clipper
if ((lpdd->CreateClipper(0,&lpddclipper,NULL))!=DD_OK)
return(NULL);
// now create the clip list from the sent data
// first allocate memory for region data
region_data = (LPRGNDATA)malloc(sizeof(RGNDATAHEADER)+num_rects*sizeof(RECT));
// now copy the rects into region data
memcpy(region_data->Buffer, clip_list, sizeof(RECT)*num_rects);
// set up fields of header
region_data->rdh.dwSize = sizeof(RGNDATAHEADER);
region_data->rdh.iType = RDH_RECTANGLES;
region_data->rdh.nCount = num_rects;
region_data->rdh.nRgnSize = num_rects*sizeof(RECT);
region_data->rdh.rcBound.left = 64000;
region_data->rdh.rcBound.top = 64000;
region_data->rdh.rcBound.right = -64000;
region_data->rdh.rcBound.bottom = -64000;
// find bounds of all clipping regions
for (index=0; index<num_rects; index++)
{
// test if the next rectangle unioned with the current bound is larger
if (clip_list[index].left < region_data->rdh.rcBound.left)
region_data->rdh.rcBound.left = clip_list[index].left;
if (clip_list[index].right > region_data->rdh.rcBound.right)
region_data->rdh.rcBound.right = clip_list[index].right;
if (clip_list[index].top < region_data->rdh.rcBound.top)
region_data->rdh.rcBound.top = clip_list[index].top;
if (clip_list[index].bottom > region_data->rdh.rcBound.bottom)
region_data->rdh.rcBound.bottom = clip_list[index].bottom;
} // end for index
// now we have computed the bounding rectangle region and set up the data
// now let's set the clipping list
if ((lpddclipper->SetClipList(region_data, 0))!=DD_OK)
{
// release memory and return error
free(region_data);
return(NULL);
} // end if
// now attach the clipper to the surface
if ((lpdds->SetClipper(lpddclipper))!=DD_OK)
{
// release memory and return error
free(region_data);
return(NULL);
} // end if
// all is well, so release memory and send back the pointer to the new clipper
free(region_data);
return(lpddclipper);
} // end DD_Attach_Clipper
///
int DD_Flip(void)
{
// this function flip the primary surface with the secondary surface
// flip pages
while(lpddsprimary->Flip(NULL, DDFLIP_WAIT)!=DD_OK);
// flip the surface
// return success
return(1);
} // end DD_Flip
///
DWORD Start_Clock(void)
{
// this function starts the clock, that is, saves the current
// count, use in conjunction with Wait_Clock()
return(start_clock_count = Get_Clock());
} // end Start_Clock
///
DWORD Get_Clock(void)
{
// this function returns the current tick count
// return time
return(GetTickCount());
} // end Get_Clock
///
DWORD Wait_Clock(DWORD count)
{
// this function is used to wait for a specific number of clicks
// since the call to Start_Clock
while((Get_Clock() - start_clock_count) < count);
return(Get_Clock());
} // end Wait_Clock
///
int DD_Fill_Surface(LPDIRECTDRAWSURFACE7 lpdds,int color)
{
DDBLTFX ddbltfx; // this contains the DDBLTFX structure
// clear out the structure and set the size field
DD_INIT_STRUCT(ddbltfx);
// set the dwfillcolor field to the desired color
ddbltfx.dwFillColor = color;
// ready to blt to surface
lpdds->Blt(NULL, // ptr to dest rectangle
NULL, // ptr to source surface, NA
NULL, // ptr to source rectangle, NA
DDBLT_COLORFILL | DDBLT_WAIT | DDBLT_ASYNC, // fill and wait
&ddbltfx); // ptr to DDBLTFX structure
// return success
return(1);
} // end DD_Fill_Surface
///
int Draw_Rectangle(int x1, int y1, int x2, int y2, int color,
LPDIRECTDRAWSURFACE7 lpdds)
{
// this function uses directdraw to draw a filled rectangle
DDBLTFX ddbltfx; // this contains the DDBLTFX structure
RECT fill_area; // this contains the destination rectangle
// clear out the structure and set the size field
DD_INIT_STRUCT(ddbltfx);
// set the dwfillcolor field to the desired color
ddbltfx.dwFillColor = color;
// fill in the destination rectangle data (your data)
fill_area.top = y1;
fill_area.left = x1;
fill_area.bottom = y2+1;
fill_area.right = x2+1;
// ready to blt to surface, in this case blt to primary
lpdds->Blt(&fill_area, // ptr to dest rectangle
NULL, // ptr to source surface, NA
NULL, // ptr to source rectangle, NA
DDBLT_COLORFILL | DDBLT_WAIT | DDBLT_ASYNC, // fill and wait
&ddbltfx); // ptr to DDBLTFX structure
// return success
return(1);
} // end Draw_Rectangle
///
int Draw_Text_GDI(char *text, int x,int y,int color, LPDIRECTDRAWSURFACE7 lpdds)
{
// this function draws the sent text on the sent surface
// using color index as the color in the palette
HDC xdc; // the working dc
// get the dc from surface
if (lpdds->GetDC(&xdc)!=DD_OK)
return(0);
// set the colors for the text up
SetTextColor(xdc,RGB(palette[color].peRed,palette[color].peGreen,palette[color].peBlue) );
// set background mode to transparent so black isn't copied
SetBkMode(xdc, TRANSPARENT);
// draw the text a
TextOut(xdc,x,y,text,strlen(text));
// release the dc
lpdds->ReleaseDC(xdc);
// return success
return(1);
} // end Draw_Text_GDI
///
// BLACKBOX.H - Header file for demo game engine library
// watch for multiple inclusions
#ifndef BLACKBOX
#define BLACKBOX
#include <ddraw.h> // directX includes
#pragma comment(lib, "ddraw.lib") // 添加这句,告诉链接器链接这个lib文件
// DEFINES
// default screen size
#define SCREEN_WIDTH 640 // size of screen
#define SCREEN_HEIGHT 480
#define SCREEN_BPP 8 // bits per pixel
#define MAX_COLORS 256 // maximum colors
// MACROS /
// these read the keyboard asynchronously
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
// initializes a direct draw struct
#define DD_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); }
// TYPES //
// basic unsigned types
typedef unsigned short USHORT;
typedef unsigned short WORD;
typedef unsigned char UCHAR;
typedef unsigned char BYTE;
// EXTERNALS //
extern LPDIRECTDRAW7 lpdd; // dd object
extern LPDIRECTDRAWSURFACE7 lpddsprimary; // dd primary surface
extern LPDIRECTDRAWSURFACE7 lpddsback; // dd back surface
extern LPDIRECTDRAWPALETTE lpddpal; // a pointer to the created dd palette
extern LPDIRECTDRAWCLIPPER lpddclipper; // dd clipper
extern PALETTEENTRY palette[256]; // color palette
extern PALETTEENTRY save_palette[256]; // used to save palettes
extern DDSURFACEDESC2 ddsd; // a direct draw surface description struct
extern DDBLTFX ddbltfx; // used to fill
extern DDSCAPS2 ddscaps; // a direct draw surface capabilities struct
extern HRESULT ddrval; // result back from dd calls
extern DWORD start_clock_count; // used for timing
// these defined the general clipping rectangle
extern int min_clip_x, // clipping rectangle
max_clip_x,
min_clip_y,
max_clip_y;
// these are overwritten globally by DD_Init()
extern int screen_width, // width of screen
screen_height, // height of screen
screen_bpp; // bits per pixel
// PROTOTYPES /
// DirectDraw functions
int DD_Init(int width, int height, int bpp);
int DD_Shutdown(void);
LPDIRECTDRAWCLIPPER DD_Attach_Clipper(LPDIRECTDRAWSURFACE7 lpdds, int num_rects, LPRECT clip_list);
int DD_Flip(void);
int DD_Fill_Surface(LPDIRECTDRAWSURFACE7 lpdds,int color);
// general utility functions
DWORD Start_Clock(void);
DWORD Get_Clock(void);
DWORD Wait_Clock(DWORD count);
// graphics functions
int Draw_Rectangle(int x1, int y1, int x2, int y2, int color,LPDIRECTDRAWSURFACE7 lpdds=lpddsback);
// gdi functions
int Draw_Text_GDI(char *text, int x,int y,COLORREF color, LPDIRECTDRAWSURFACE7 lpdds=lpddsback);
int Draw_Text_GDI(char *text, int x,int y,int color, LPDIRECTDRAWSURFACE7 lpdds=lpddsback);
#endif