注:以下内容为学习笔记,多数是从书本、资料中得来,只为加深印象,及日后参考。然而本人表达能力较差,写的不好。因非翻译、非转载,只好选原创,但多数乃摘抄,实为惭愧。但若能帮助一二访客,幸甚!
前一阵子学习Win32 SDK 时自己写过一个拼图游戏作为练习,基本功能是都完成了,但到后来随着代码量的增多,代码的组织上有点混乱。考虑到是第一次独立用Win32 SDK编写程序,就先放过了。但总觉的不爽。
最近看到一本名为《Windows 游戏编程大师技巧》的牛书,看到第一章打砖块的示例,欲效仿其组织代码的技巧,故将其源代码分拆成容易理解的若干部分,修改抄录于下,供他日重读或供无缘见到此书者查阅。
程序版权归原作者所有,吾仅是学习模仿,不敢掠美。
但源程序使用到了DirectX SDK,由于暂时不想涉及D3D,故改为用Win32 API 绘图。当然以后有空,或会学习一下DirectX SDK~
我们需要的是下面几个功能:
1)切换至任意图像模式
2)在屏幕上绘制各种颜色的矩形
3)获取键盘输入
4)使用一些定时函数同步游戏循环
5)在屏幕上画彩色字符串
/*
* FreakOut.cpp (改编自《Windows 游戏编程大师技巧》第一章示例程序)
* [email protected]
* 2012-11-29
*/
/* INCLUDES *****************************************************************************/
#include
/* DEFINES ******************************************************************************/
// defines for windows
#define WINDOW_CLASS_NAME TEXT("WIN32CLASS")
#define WINDOW_WIDTH 640
#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
/* 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
/* WINDPROC ******************************************************************************/
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
// this is the main message handler of the system
PAINTSTRUCT ps;
HDC hdc;
switch (msg)
{
case WM_CREATE:
{
return 0;
}
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
default:
break;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
/* WINMAIN *******************************************************************************/
int WINAPI WinMain(HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
WNDCLASS winclass;
HWND hwnd;
MSG msg;
HDC hdc;
PAINTSTRUCT ps;
/* CS_DBLCLKS Specifies that the window should be notified of double clicks with
* WM_xBUTTONDBLCLK messages
* CS_OWNDC标志,属于此窗口类的窗口实例都有自己的DC(称为私有DC),私有DC仅属于该窗口实例,
* 所以程序只需要调用一次GetDC或BeginPaint获取DC,系统就为窗口初始化一个DC,并且保存程序
* 对其进行的改变。ReleaseDC和EndPaint函数不再需要了,因为其他程序无法访问和改变私有DC。
*/
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
hwnd = CreateWindow(
WINDOW_CLASS_NAME, // class
TEXT("WIN32 Game Console"), // title
WS_OVERLAPPEDWINDOW, // style
0, 0, // initial x, y
WINDOW_WIDTH, // initial width
WINDOW_HEIGHT, // initial height
NULL, // handle to parent
NULL, // handle to menu
hinstance, // instance
NULL); // creation parms
if (! hwnd)
return 0;
ShowWindow(hwnd, ncmdshow);
UpdateWindow(hwnd);
// 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 msg
if (msg.message == WM_QUIT)
break;
// translate any accelerator keys
TranslateMessage(&msg);
// send the message to the window proc
DispatchMessage(&msg);
}
// main game processing goes here
//Game_Main();
}
// shutdown game and release all resources
//Game_Shutdown();
// show mouse
//ShowCursor(TRUE);
// return to windows like this
return (msg.wParam);
}
int Game_Init(void *parms)
{
// this function is where you do all the initialization for your game
return 1;
}
int Game_Shutdown(void *parms)
{
// this function is where you shutdown your game and release all resources
// that you allocated
return 1;
}
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!
TCHAR buffer[80];
// what state is the game in?
if (game_state == GAME_STATE_INIT)
{
// seed the random number generator so game is different each play
srand((unsigned int)time(0));
// 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() % (WINDOW_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;
}
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;
}
else if (game_state == GAME_STATE_RUN)
{
// start the timing clock
Start_Clock();
// clear drawing surface for next frame of animation
Draw_Rectangle(0, 0, WINDOW_WIDTH-1, WINDOW_HEIGHT-1, 0);
// 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 > (WINDOW_WIDTH - PADDLE_WIDTH))
paddle_x = WINDOW_WIDTH - PADDLE_WIDTH;
}
else if (KEY_DOWN(VK_LEFT))
{
// move paddle to left
paddle_x += 8;
// make sure paddle doesn't go off screen
if (paddle_x < 0)
paddle_x = 0;
}
// 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 > (WINDOW_WIDTH - BALL_SIZE) || ball_x < 0)
{
// reflect x-axis velocity
ball_dx = -ball_dx;
// update position
ball_x += ball_dx;
}
// now y-axis
if (ball_y < 0)
{
// reflect y-axis velocity
ball_dy = -ball_dy;
// update position
ball_y += ball_dy;
}
else if (ball_y > (WINDOW_HEIGHT - BALL_SIZE))
{
// reflect y-axis velocity
ball_dy = -ball_dy;
// update position
ball_y += ball_dy;
// minus the score
score -= 100;
}
// 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
Draw_Rectangle(paddle_x, paddle_y, paddle_x+PADDLE_WIDTH, paddle_y+PADDLE_HEIGHT, PADDLE_COLOR);
// draw the ball
Draw_Rectangle(ball_x, ball_y, ball_x+BALL_SIZE, ball_y+BALL_SIZE, 255);
// draw the info
wsprintf(buffer, TEXT("FREAKOUT Score %d Level %d"), score, level);
DrawText_GUI(buffer, 8, WINDOW_HEIGHT-16, 127);
// sync to 30 fps
Wait_Clock(30);
// check if 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;
}
}
else if (game_state == GAME_STATE_SHUTDOWN)
{
// in this state shut everything down and release resources
// switch to exit state
game_state = GAME_STATE_EXIT;
}
return 1;
}
/* DRAW FUNCTION **************************************************************************/
int Draw_Rectangle(int x1, int y1, int x2, int y2, int color)
{
// this function uses Win32 API to draw a filled rectangle
HBRUSH hbrush;
HDC hdc;
RECT rect;
SetRect(&rect, x1, y1, x2, y2);
hbrush = CreateSolidBrush(color);
hdc = GetDC(main_window_handle);
FillRect(hdc, &rect, hbrush);
ReleaseDC(main_window_handle, hdc);
DeleteObject(hbrush);
return 1;
}
int DrawText_GUI(TCHAR *text, int x, int y, int color)
{
HDC hdc;
hdc = GetDC(main_window_handle);
// set the colors for the text up
SetTextColor(hdc, color);
// set background mode to transparent so black isn't copied
SetBkMode(hdc, TRANSPARENT);
// draw the text
TextOut(hdc, x, y, text, lstrlen(text));
// release the dc
ReleaseDC(main_window_handle, hdc);
return 1;
}
/* GAME PROGRAMMING CONSOLE FUNCTIONS *****************************************************/
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;
}
void Draw_Blocks(void)
{
// this function draws all the blocks in row major form
int x1 = BLOCK_ORIGIN_X;
int y1 = BLOCK_ORIGIN_Y;
// draw all the blocks
for (int row = 0; row < NUM_BLOCK_ROWS; row++)
{
// reset column position
x1 = BLOCK_ORIGIN_X;
for (int col = 0; col < NUM_BLOCK_COLUMNS; col++)
{
if (blocks[row][col] != 0)
{
Draw_Rectangle(x1, y1, x1+BLOCK_WIDTH, y1+BLOCK_HEIGHT, blocks[row][col]);
}
// advance column position
x1 += BLOCK_X_GAP;
}
// advance to next row position
y1 += BLOCK_Y_GAP;
}
}
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
// current rendering position
int x1 = BLOCK_ORIGIN_X;
int y1 = BLOCK_ORIGIN_Y;
// computer center of ball
int ball_cx = ball_x + (BALL_SIZE/2);
int ball_cy = ball_y + (BALL_SIZE/2);
// test the ball has hit the paddle
if (ball_y > (WINDOW_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++;
}
// make a little noise
MessageBeep(MB_OK);
return;
}
}
// now scan thru all the blocks and see of ball hit blocks
for (int row = 0; row < NUM_BLOCK_ROWS; row++)
{
x1 = BLOCK_ORIGIN_X;
// scan this row of blocks
for (int col = 0; col < NUM_BLOCK_COLUMNS; col++)
{
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));
return;
}
}
// advance column position
x1 += BLOCK_X_GAP;
}
// advance row position
y1 += BLOCK_Y_GAP;
}
}
/* CLOCK FUNCTIONS ************************************************************************/
DWORD Get_Clock(void)
{
// this function returns the current tick count
// return time
return GetTickCount();
}
DWORD Start_Clock(void)
{
// this function starts the block, that is, saves the current count,
// use in conjunction with Wait_Clock()
return (start_clock_count = 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();
}
1)颜色太暗:
原因可能是int 到RGB转换的问题,解决办法是传参数时直接用RGB构造。
2)挡板和分数都看不到
原因是原程序没有标题栏,是全屏显示的,现在有了标题栏,占了一定的空间,导致位置计算的不对,修改窗口style 为WS_POPUP即可。
3)界面闪烁及耗费CPU
原因是每次都先绘制黑色屏幕清除全屏,然后重新绘制,较为浪费,且一直在循环,耗费CPU。
解决办法是减少重绘区域,每次只绘制需要绘制的地方,而不是重绘全屏。修改使用死循环来耗费时间,改为Sleep。
完整代码:
/*
* FreakOut.cpp (改编自《Windows 游戏编程大师技巧》第一章示例程序)
* [email protected]
* 2012-11-29
*/
/* INCLUDES *******************************************************************************/
#include
#include
#include
#include
/* DEFINES ********************************************************************************/
// defines for windows
#define WINDOW_CLASS_NAME TEXT("WIN32CLASS")
#define WINDOW_WIDTH 640
#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 (WINDOW_WIDTH/2 - 16)
#define PADDLE_START_Y (WINDOW_HEIGHT - 32);
#define PADDLE_WIDTH 32
#define PADDLE_HEIGHT 8
#define PADDLE_COLOR RGB(0, 0, 255)
// ball defines
#define BALL_START_Y (WINDOW_HEIGHT/2)
#define BALL_SIZE 4
// color defines
#define BACKGROUND_COLOR RGB(0, 0, 0)
#define BLOCK_COLOR RGB(125, 0, 0)
#define BALL_COLOR RGB(222, 0, 222)
// 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)
/* basic unsigned types *******************************************************************/
typedef unsigned short USHORT;
typedef unsigned short WORD;
typedef unsigned char UCHAR;
typedef unsigned char BYTE;
/* FUNCTION DECLARATION *******************************************************************/
int Game_Init(void *parms = NULL);
int Game_Shutdown(void *parms = NULL);
int Game_Main(void *parms = NULL);
DWORD Start_Clock(void);
DWORD Wait_Clock(DWORD count);
/* 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
DWORD start_clock_count = 0; // used for timing
// this contains the game grid data
UCHAR blocks[NUM_BLOCK_ROWS][NUM_BLOCK_COLUMNS];
/* WINDPROC ********************************************************************************/
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
// this is the main message handler of the system
PAINTSTRUCT ps;
HDC hdc;
switch (msg)
{
case WM_CREATE:
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
break;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
/* WINMAIN ********************************************************************************/
int WINAPI WinMain(HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
WNDCLASS winclass;
HWND hwnd;
MSG msg;
HDC hdc;
PAINTSTRUCT ps;
/* CS_DBLCLKS Specifies that the window should be notified of double clicks with
* WM_xBUTTONDBLCLK messages
* CS_OWNDC标志,属于此窗口类的窗口实例都有自己的DC(称为私有DC),私有DC仅属于该窗口实例,
* 所以程序只需要调用一次GetDC或BeginPaint获取DC,系统就为窗口初始化一个DC,并且保存程序
* 对其进行的改变。ReleaseDC和EndPaint函数不再需要了,因为其他程序无法访问和改变私有DC。
*/
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
hwnd = CreateWindow(
WINDOW_CLASS_NAME, // class
TEXT("WIN32 Game Console"), // title
WS_POPUP, // style
200, // initial x
100, // initial y
WINDOW_WIDTH, // initial width
WINDOW_HEIGHT, // initial height
NULL, // handle to parent
NULL, // handle to menu
hinstance, // instance
NULL); // creation parms
if (! hwnd)
return 0;
ShowWindow(hwnd, ncmdshow);
UpdateWindow(hwnd);
// 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 msg
if (msg.message == WM_QUIT)
break;
// translate any accelerator keys
TranslateMessage(&msg);
// send the message to the window proc
DispatchMessage(&msg);
}
// main game processing goes here
Game_Main();
Sleep(30);
}
// shutdown game and release all resources
Game_Shutdown();
// show mouse
//ShowCursor(TRUE);
// return to windows like this
return (msg.wParam);
}
/* DRAW FUNCTION **************************************************************************/
int Draw_Rectangle(int x1, int y1, int x2, int y2, int color)
{
// this function uses Win32 API to draw a filled rectangle
HBRUSH hbrush;
HDC hdc;
RECT rect;
SetRect(&rect, x1, y1, x2, y2);
hbrush = CreateSolidBrush(color);
hdc = GetDC(main_window_handle);
FillRect(hdc, &rect, hbrush);
ReleaseDC(main_window_handle, hdc);
DeleteObject(hbrush);
return 1;
}
int DrawText_GUI(TCHAR *text, int x, int y, int color)
{
HDC hdc;
hdc = GetDC(main_window_handle);
// set the colors for the text up
SetTextColor(hdc, color);
// set background mode to transparent so black isn't copied
SetBkMode(hdc, TRANSPARENT);
// draw the text
TextOut(hdc, x, y, text, lstrlen(text));
// release the dc
ReleaseDC(main_window_handle, hdc);
return 1;
}
/* GAME PROGRAMMING CONSOLE FUNCTIONS *****************************************************/
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] = 1;
}
void Draw_Blocks(void)
{
// this function draws all the blocks in row major form
int x1 = BLOCK_ORIGIN_X;
int y1 = BLOCK_ORIGIN_Y;
// draw all the blocks
for (int row = 0; row < NUM_BLOCK_ROWS; row++)
{
// reset column position
x1 = BLOCK_ORIGIN_X;
for (int col = 0; col < NUM_BLOCK_COLUMNS; col++)
{
if (blocks[row][col] != 0)
{
Draw_Rectangle(x1, y1, x1+BLOCK_WIDTH, y1+BLOCK_HEIGHT, BLOCK_COLOR);
}
else
{
Draw_Rectangle(x1, y1, x1+BLOCK_WIDTH, y1+BLOCK_HEIGHT, BACKGROUND_COLOR);
}
x1 += BLOCK_X_GAP; // advance column position
}
y1 += BLOCK_Y_GAP; // advance to next row position
}
}
int 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
// current rendering position
int x1 = BLOCK_ORIGIN_X;
int y1 = BLOCK_ORIGIN_Y;
// computer center of ball
int ball_cx = ball_x + (BALL_SIZE/2);
int ball_cy = ball_y + (BALL_SIZE/2);
// test the ball has hit the paddle
if (ball_y > (WINDOW_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))
{
ball_dy = -ball_dy; // reflect ball
ball_y += ball_dy; // push ball out of paddle since it made contact
// 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);
// make a little noise
MessageBeep(MB_OK);
return 0;
}
}
// now scan thru all the blocks and see of ball hit blocks
for (int row = 0; row < NUM_BLOCK_ROWS; row++)
{
x1 = BLOCK_ORIGIN_X;
// scan this row of blocks
for (int col = 0; col < NUM_BLOCK_COLUMNS; col++)
{
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))
{
blocks[row][col] = 0; // remove the block
// increment global block counter, so we know when to start another level up
blocks_hit++;
ball_dy = -ball_dy; // bounce the ball
ball_dx += (-1 + rand() % 3); // add a little english
MessageBeep(MB_OK); // make a little noise
// 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++;
}
// add some points
score += 5 * (level + (abs)(ball_dx));
return 1;
}
}
x1 += BLOCK_X_GAP; // advance column position
}
y1 += BLOCK_Y_GAP; // advance row position
}
return 0;
}
int Game_Init(void *parms)
{
// this function is where you do all the initialization for your game
return 1;
}
int Game_Shutdown(void *parms)
{
// this function is where you shutdown your game and release all resources
// that you allocated
return 1;
}
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!
TCHAR buffer[80];
BOOL bPaddleMoved = FALSE;
int old_paddle_x, old_paddle_y;
int old_ball_x, old_ball_y;
// what state is the game in?
if (game_state == GAME_STATE_INIT)
{
// seed the random number generator so game is different each play
srand((unsigned int)time(0));
// 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() % (WINDOW_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;
}
else if (game_state == GAME_STATE_START_LEVEL)
{
// get a new level ready to run
Init_Blocks(); // initialize the blocks
blocks_hit = 0; // reset block counter
Draw_Blocks(); // draw blocks
// draw the paddle
Draw_Rectangle(paddle_x, paddle_y, paddle_x+PADDLE_WIDTH, paddle_y+PADDLE_HEIGHT, PADDLE_COLOR);
game_state = GAME_STATE_RUN;// transition to run state
}
else if (game_state == GAME_STATE_RUN)
{
// move the paddle
if (KEY_DOWN(VK_RIGHT))
{
old_paddle_x = paddle_x;
old_paddle_y = paddle_y;
paddle_x += 8; // move paddle to right
// make sure paddle doesn't go off screen
if (paddle_x > (WINDOW_WIDTH - PADDLE_WIDTH))
paddle_x = WINDOW_WIDTH - PADDLE_WIDTH;
bPaddleMoved = TRUE;
}
else if (KEY_DOWN(VK_LEFT))
{
old_paddle_x = paddle_x;
old_paddle_y = paddle_y;
paddle_x -= 8; // move paddle to left
// make sure paddle doesn't go off screen
if (paddle_x < 0)
paddle_x = 0;
bPaddleMoved = TRUE;
}
old_ball_x = ball_x;
old_ball_y = ball_y;
// 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 > (WINDOW_WIDTH - BALL_SIZE) || ball_x < 0)
{
ball_dx = -ball_dx; // reflect x-axis velocity
ball_x += ball_dx; // update position
}
// now y-axis
if (ball_y < 0)
{
ball_dy = -ball_dy; // reflect y-axis velocity
ball_y += ball_dy; // update position
}
else if (ball_y > (WINDOW_HEIGHT - BALL_SIZE))
{
ball_dy = -ball_dy; // reflect y-axis velocity
ball_y += ball_dy; // update position
score -= 100; // minus the score
}
// 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
if (Process_Ball())
{
Draw_Blocks(); // draw blocks
}
if (bPaddleMoved)
{
// 覆盖旧的paddle
Draw_Rectangle(old_paddle_x, old_paddle_y, old_paddle_x+PADDLE_WIDTH, old_paddle_y+PADDLE_HEIGHT,
BACKGROUND_COLOR);
// draw the paddle
Draw_Rectangle(paddle_x, paddle_y, paddle_x+PADDLE_WIDTH, paddle_y+PADDLE_HEIGHT, PADDLE_COLOR);
}
// 覆盖旧的ball
Draw_Rectangle(old_ball_x, old_ball_y, old_ball_x+BALL_SIZE, old_ball_y+BALL_SIZE, BACKGROUND_COLOR);
// draw the ball
Draw_Rectangle(ball_x, ball_y, ball_x+BALL_SIZE, ball_y+BALL_SIZE, BALL_COLOR);
// draw the info
wsprintf(buffer, TEXT("FREAKOUT Score %d Level %d"), score, level);
Draw_Rectangle(8, WINDOW_HEIGHT-26, WINDOW_WIDTH, WINDOW_HEIGHT, BACKGROUND_COLOR);
DrawText_GUI(buffer, 8, WINDOW_HEIGHT-26, RGB(255, 255, 128));
// check if 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;
}
}
else if (game_state == GAME_STATE_SHUTDOWN)
{
// in this state shut everything down and release resources
// switch to exit state
game_state = GAME_STATE_EXIT;
}
return 1;
}
/* CLOCK FUNCTIONS ************************************************************************/
DWORD Get_Clock(void)
{
// this function returns the current tick count
// return time
return GetTickCount();
}
DWORD Start_Clock(void)
{
// this function starts the block, that is, saves the current count,
// use in conjunction with Wait_Clock()
return (start_clock_count = 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();
}
不足之处:因为之前经常在linux下编程,用的是game_state类型的变量风格,最近在windows下想学所谓的匈牙利表示法,但这段代码作者用了game_state类型的变量风格,我又被俘获了。。。
编程风格要统一,不能总是变来变去的!切记。