Win32游戏制作之---FreakOut

       记得自己小时候曾经玩过这个小游戏,如今长大了,有了一定的知识就可以实现一些小时候未曾想过的事情,或者说梦想去做的事情!虽然这次实现的小游戏比较简单,但是也算游戏吧,比较自己还不是这方面的大神。

       如果想要用Win32项目制作一个小游戏,那么首先你要对对C/C++语言熟悉,对Windows编程有一定的了解。其实还有一点就是你在开始制作小游戏之前,你要知道你的游戏的逻辑结构。对于一般的小游戏结构可以用下图的结构表示。



       Game_main()是游戏的主体函数,不过它在主事件循环每次处理windows消息之后就会调用一次,要注意每次进入和终止该函数的时候,自动变量是瞬间变化的,如果想要一直使用的数据,建议使用全局变量,或者也可以设置为局部静态变量。

       恰好最近正在学Windows编程,想试试Windows是否真的适合制作游戏编程。最后在看了《Windows游戏编程大师技巧》之后,决定将里面的FreakOut游戏实现一下。

       由于平时用习惯了C/C++编程可能开始会有些不习惯,但是用多了就会发现其实最重要的就是要理解Windows编程中的消息驱动原理,简单的说其实就是Windows有其自己的基于事件的中枢神经系统。当我们按下一个键,就会有消息从按键事件中创建并且在系统中散播,直到有程序检出这个消息并使用它。当然Windows编程中的要注意的点还是挺多的,字符编码就是其中一个,不过解决起来的很简单(例如著名的“L”字符和TCHAR)。还有一点就是Windows编程中的主函数WinMain(),如果第一眼看到他,觉得很复杂,但是WinMain并不需要是一个又大又复杂的应用代码大杂烩。


相信大家都玩过FreakOut游戏(俗称打砖块游戏),游戏主要是利用小球消灭所有的砖块,所有的砖块大小相等,小球每次弹到砖块上都会发生反弹,按照物理上的说法就是发生弹性碰撞,当然,小球只能在屏幕(其实说窗口更准确)内运动,所以当小球碰到窗口的边界的时候就会发生反弹,假设小球做的是直线运动,根据速度的分解可知可以将小球的速度正交分解,分成一个X方向上的运动和一个Y方向上的运动。再碰到边界或者是砖块或者你跳板之后都会发生反弹,所以只要保证某一个方向上的速度不变,另一个方向上的速度反向即可。(不过我在具体实现的过程中没有完全按照物理学规律来制作,在碰到砖块或者跳板的时候让他们的速度有一点变化,这样有时候可以避免小球一直做同样的运动)。

下面就通过代码来解释:

首先来看下头文件FreakOut.h:

#pragma once

#include "resource.h"

//define for Windows                   窗口大小定义
#define WINDOW_WIDTH                    640                
#define WINDOW_HEIGHT                   480

//state 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                   2
#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
#define BLOCK_COLOR                      RGB(222,200,125)

//ball defines                          有关小球的定义
#define BALL_COLOR                       RGB(255,0,0)
#define BALL_START_Y                     (WINDOW_HEIGHT/2)
#define BALL_SIZE                        14

//paddle defines                         跳板的定义
#define PADDLE_START_X                   (WINDOW_WIDTH/2 - 16)
#define PADDLE_START_Y                   (WINDOW_HEIGHT - 32 )
#define PADDLE_WIDTH                     50
#define PADDLE_HEIGHT                    8
#define PADDLE_COLOR                     RGB(0,0,255)

//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)

//set bounds         设置关卡数
#define Game_Count                        3

//basic unsigned types
typedef unsigned short USHORT;
typedef unsigned short WORD;
typedef unsigned char UCHAR;
typedef unsigned char BYTE;
头文件主要是一些游戏中一些对象的初始值的有关定义。可以按照个人喜好设置。

下面来看一下内部的实现逻辑,先贴上我的代码FreakOut.cpp:

// FreakOut.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "FreakOut.h"

#define MAX_LOADSTRING 100

// 全局变量: 
HINSTANCE hInst;								// 当前实例
TCHAR szTitle[MAX_LOADSTRING];					// 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING];			// 主窗口类名
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 handle
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 block_hit = 0;                              //tracks number of block hit
DWORD start_clock_count = 0, now_clock = 0;     //used for timing
UCHAR blocks[NUM_BLOCK_ROWS][NUM_BLOCK_COLUMNS];//this contains the game grid data

// 此代码模块中包含的函数的前向声明: 
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
int                 Game_Init(void *parms = NULL);
int                 Game_Shutdown(void *parms = NULL);
int                 Game_Main(void *parms = NULL);
DWORD               Start_Clock();
DWORD               Get_Clock(void);
DWORD               Wait_Clock(DWORD );
void                Change_Mode(HWND, DWORD, int);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO:  在此放置代码。
	MSG msg;
	HACCEL hAccelTable;
	WNDCLASSEX winclass;
	HWND hwnd;
	HDC hdc;
	PAINTSTRUCT ps;

	// 初始化全局字符串
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_FREAKOUT, szWindowClass, MAX_LOADSTRING);

	/*MyRegisterClass(hInstance);*/                  //注册窗口类
	winclass.cbSize = sizeof(WNDCLASSEX);
	winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
	winclass.lpfnWndProc = WndProc;
	winclass.cbClsExtra = 0;
	winclass.cbWndExtra = 0;
	winclass.hInstance = hInstance;
	winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	winclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	winclass.lpszMenuName = MAKEINTRESOURCE(IDC_FREAKOUT);
	winclass.lpszClassName = szWindowClass;
	winclass.hIconSm = LoadIcon(winclass.hInstance, MAKEINTRESOURCE(IDI_SMALL));
	
	if (!RegisterClassEx(&winclass))                  //注册窗口类是否成功
	{
		return FALSE;
	}
	int cxNonClient = GetSystemMetrics(SM_CXBORDER) * 2 + 10;
	int cyNonClient = GetSystemMetrics(SM_CYBORDER) + GetSystemMetrics(SM_CYCAPTION) + 10;
	//Create the window, note the use of WS_POPUP
	hwnd = CreateWindowEx(NULL,szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, 0, 0, WINDOW_WIDTH + cxNonClient, WINDOW_HEIGHT + cyNonClient, NULL, NULL, hInstance, NULL);
	// 执行应用程序初始化
	if (!hwnd)
	{
		return FALSE;
	}
	ShowWindow(hwnd, nCmdShow);      // WM_SIZE消息是由 ShowWindow函数发出的
	UpdateWindow(hwnd);
       //hide mouse
	//ShowCursor(FALSE);

	//save the window handle and instance in a global
	main_window_handle = hwnd;
	main_instance = hInstance;

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_FREAKOUT));
	//perform all game console specific initialization
	Game_Init();

	// 主消息循环: 
	while (true)
	{
		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(10);
	}
	return (int) msg.wParam;
}

/*DRAW Function ********** */
int Draw_Rectangle(int x1, int y1, int x2, int y2, int color)
{
	//this function users 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);
	//SelectObject(hdc, hbrush);
	FillRect(hdc, &rect, hbrush);
	ReleaseDC(main_window_handle, hdc);
	DeleteObject(hbrush);
	return 1;
}

int Draw_Ball(int x1, int y1, int x2, int y2, int color)
{
	HDC hdc;
	HBRUSH hbrush;
	hbrush = CreateSolidBrush(color);
	hdc = GetDC(main_window_handle);
	SelectObject(hdc, hbrush);
	Ellipse(hdc, x1, y1, x2, y2);
	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 color 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
	int row = 0, col = 0;
	for (row = 0; row < level*NUM_BLOCK_ROWS; row++)
	{
		for (col = 0; col < level*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;
	int row = 0, col = 0;
	//draw all the blocks
	for ( row = 0; row < level*NUM_BLOCK_ROWS; row++)
	{
		x1 = BLOCK_ORIGIN_X;
		for (col = 0; col < level*NUM_BLOCK_COLUMNS; col++)
		{
			if (blocks[row][col] != 0)
			{
				Draw_Rectangle(x1, y1, x1 + BLOCK_WIDTH, y1 + BLOCK_HEIGHT, BLOCK_COLOR);
			}
			//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 chessy collision algorithm:)
	//first test for ball block collisions
	//the algorith basically test the ball against each black'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)
	{
		int x = ball_x + (BALL_SIZE / 2);
		int y = ball_y + (BALL_SIZE / 2);
		//test for collision with paddle
		if ((x <= (paddle_x + PADDLE_WIDTH )) && (x >= paddle_x ) && (y >= paddle_y )&& (y <= paddle_y + PADDLE_HEIGHT/2))
		{
			if ( ((ball_dx)<0 && ((-ball_dx)>ball_dy))|| ((ball_dx>0) && (ball_dx>ball_dy)) ) 
			{
				//test if there are no blocks, if so send a message to game loop to start another level
				if (block_hit >= (level*NUM_BLOCK_ROWS)*(level*NUM_BLOCK_COLUMNS))
				{
					level++;
					if (level>Game_Count)
					{
						MessageBox(main_window_handle, L"Congraculation, you pass all customs!", L"FreakOut", MB_OK);
						game_state = GAME_STATE_INIT;
					}
					else
					{
						game_state = GAME_STATE_START_LEVEL;
					}
				}
				//make a little noise
				MessageBeep(MB_OK);
				return;
			}
			else
			{
				//relect ball
				ball_dy = -ball_dy;
				//puch ball out of paddle since it made contact
				ball_y = ball_y + ball_dy;
				//add a little english to ball based on mation of paddle
				if (KEY_DOWN(VK_RIGHT))
				{
					ball_dx -= rand() % 3;                          //change speed
				}
				else if (KEY_UP(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 (block_hit >= (level*NUM_BLOCK_ROWS)*(level*NUM_BLOCK_COLUMNS))
				{
					level++;
					if (level>Game_Count)
					{
						MessageBox(main_window_handle, L"Congraculation, you pass all customs!", L"FreakOut", MB_OK);
						game_state = GAME_STATE_INIT;
					}
					else
					{
						game_state = GAME_STATE_START_LEVEL;
					}
				}
				//make a little noise
				MessageBeep(MB_OK);
				return;
			}
		}
	}
	//now scan thru all the blocks and see of ball hit blocks
	int row, col;
	for (row = 0; row < level*NUM_BLOCK_ROWS; row++)
	{
		x1 = BLOCK_ORIGIN_X;
		for (col = 0; col < level*NUM_BLOCK_COLUMNS; col++)
		{
			if (blocks[row][col])
			{
				//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
					blocks[row][col] = 0;
					//increment global block counter, so we know when to start another level up
					block_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;
	}
}

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 ge here!
	TCHAR buffer[80];
	//what state is the game in?
	if (game_state == GAME_STATE_INIT)
	{
		//send the random number generator so game is different each play
		srand((unsigned)time(NULL));
		//send the paddle position here to the middle buttom
		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
		block_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, RGB(255,255,255));
		//move to paddle
		if (KEY_DOWN(VK_RIGHT))
		{
			//move paddle to right
			paddle_x += 8;
			//make sure that the paddle doesn't go off screen
			if (paddle_x > WINDOW_WIDTH - PADDLE_WIDTH)
			{
				paddle_x = WINDOW_WIDTH - PADDLE_WIDTH;
			}
		}
		if (KEY_DOWN(VK_LEFT))
		{
			//move paddle to left
			paddle_x -= 8;
			//make sure that the 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 < 0)||(ball_x > WINDOW_WIDTH - BALL_SIZE/2))
		{
			//reflect x-axis velocity
			ball_dx = -ball_dx;
			//update position
			ball_x += ball_dx;
		}
		//now y-axis
		if (ball_y < 0)
		{
			//relect y-axis velocity
			ball_dy = -ball_dy;
			//update position
			ball_y += ball_dy;
		}
		else if (ball_y >WINDOW_HEIGHT - BALL_SIZE/2)
		{
			//reflect y-axis velocity
			ball_dy = -ball_dy;
			//update position
			ball_y += ball_dy;
			//minus the score
			score -= 100;
		}
		//now 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, BALL_COLOR);
		Draw_Ball(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, BALL_COLOR);
		//check if it has reword
		
		//now_clock = Get_Clock();
		//Change_Mode(main_window_handle, 15000, score);
		
		//draw the info
		wsprintf(buffer, TEXT("Game Scroe %d       Level %d"), score, level);
		DrawText_GUI(buffer, 8, WINDOW_HEIGHT - 50, 127);
		//sync to 30 fps
		Wait_Clock(30);
		//Sleep(30);
		//check if user is trying to exit
		if (KEY_DOWN(VK_ESCAPE))
		{
			//send to message to windows to exit
			PostMessage(main_window_handle, /*WM_QUIT*/WM_DESTROY, 0, 0);
			//send 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;
	}
	else
	{
		;
	}
	return 1;
}

//CLOCK FUNCTIONS
DWORD Get_Clock(void)
{
	//this function returns the current tickcount
	//return time 
	return GetTickCount();

}

DWORD Start_Clock()
{
	//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();
}

/*void Change_Mode(HWND hWnd, DWORD Times, int score)                //奖励模式
{
	HDC hdc;
	hdc = GetDC(hWnd);
	RECT rect, wnd;
	if (score >= Reword_Score)
	{
		while (Get_Clock() - now_clock < Times)
		{
			rect = { paddle_x - PADDLE_WIDTH, paddle_y, paddle_x + PADDLE_WIDTH, paddle_y + PADDLE_HEIGHT };
			FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 0, 255)));
			wnd = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT - 2 * PADDLE_HEIGHT };
			InvalidateRect(hWnd, &wnd, false);
		}
	}
	score -= 50;
	InvalidateRect(hWnd, NULL, false);
	ReleaseDC(hWnd, hdc);
}*/


//  函数:  WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND	- 处理应用程序菜单
//  WM_PAINT	- 绘制主窗口
//  WM_DESTROY	- 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// 分析菜单选择: 
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO:  在此添加任意绘图代码...
		/*
		//创建内存HDC
		HDC memHDC = CreateCompatibleDC(hdc);

		//获取客户区大小
		RECT rectClient;
		GetClientRect(hWnd,&rectClient);

		//创建位图
		HBITMAP bmpBuff = CreateCompatibleBitmap(hdc, RECT_WIDTH(rectClient), RECT_HEIGHT(rectClient));
		HBITMAP pOldBMP = (HBITMAP)SelectObject(memHDC, bmpBuff);

		// draw something
		DrawBackGround(memHDC);

		//拷贝内存HDC内容到实际HDC
		BOOL tt = BitBlt(hdc, rectClient.left, rectClient.top, RECT_WIDTH(rectClient),
			RECT_HEIGHT(rectClient), memHDC, rectClient.left, rectClient.top, SRCCOPY);

		//内存回收
		SelectObject(memHDC, pOldBMP);
		DeleteObject(bmpBuff);
		DeleteDC(memHDC);*/

		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_ERASEBKGND:
		//防止清除背景造成的白屏
		//什么也不做,返回0使默认窗口回调不再处理这个消息
		return 0;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}
下面是游戏的截图:

初始状态:


游戏状态:



       游戏设计了好几关,每一关的砖块数量都不同。

       一些比较关键的地方都有注释,这个游戏的基本框架可以在《Windows游戏编程大师技巧》之中找到。游戏的美工方面做的挺差的,不过逻辑基本实现了。其实为了更美观你可以添加背景。这样看起来就会更不错一些。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值