《Windows游戏编程大师技巧》 T3D游戏控制台程序实例及第一部分总结


现在我们要创建一个Windows外壳程序,将单调的Windows运行细节隐藏,
Win32/DirectX编程就变得类似于32位DOS的编程过程。在构建好的代码结构下,
我们只需关心Game_*几个有关游戏逻辑的方法就行了。



1.Game_Init()

Game_Init()在WinMain()中的主事件循环之前被调用,并且仅调用一次。它用于初始化游戏
所需的各种资源,如获得资源设备描述表DC。

2.Game_Main()

Game_Main()在主事件循环中每次处理Windows消息之后被调用一次,应当在Game_Main()中
处理所有的图像渲染、声音、人工智能等内容。需要注意的是你必须仅绘制一帧画面然后就
返回。

3.Game_Shutdown()

Game_Shutdown()在主事件循环退出后被调用,清除游戏中被分配的所有资源。

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>


// DEFINE AND GLOBALS //////////////////////////////////////////////////
#define WINDOW_CLASS_NAME "WINCLASS1"
#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

HWND main_window_handle = NULL;
HINSTANCE hinstance_app = NULL;
char buffer[80];


// Function prototype //////////////////////////////////////////////////

LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);

int Game_Init(void *parms = NULL, int num_parms = 0);
int Game_Main(void *parms = NULL, int num_parms = 0);
int Game_Shutdown(void *parms = NULL, int num_parms = 0);


// WINMAIN ////////////////////////////////////////

int WINAPI WinMain(HINSTANCE hinstance, 
					HINSTANCE hprevinstance, 
					LPSTR lpcmdline, 
					int ncmdshow)
{
	WNDCLASSEX winclass;
	HWND hwnd;
	MSG msg;	

	// 1. Fill in the window class structure
	winclass.cbSize = sizeof(WNDCLASSEX);
	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;
	winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
	
	// 2. Register the window class
	if (!RegisterClassEx(&winclass))
		return (0);

	// 3. Create window
	if (!(hwnd = CreateWindowEx(NULL,
								WINDOW_CLASS_NAME,
								"T3D Game Console Version 1.0",
								WS_OVERLAPPEDWINDOW | WS_VISIBLE,
								0, 0,
								400, 300,
								NULL,
								NULL,
								hinstance,
								NULL)))
		return (0);

	// Save global handle
	main_window_handle = hwnd;
	hinstance_app = hinstance;

	// Initialize game here
	Game_Init();

	while (TRUE)
	{
		// Retrieve message
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT)
				break;
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}		
		
		// Main game processing goes here
		Game_Main();
	}

	// Closedown game here
	Game_Shutdown();

	return msg.wParam;
}

///////////////////////////////////////////////////////

LRESULT CALLBACK WindowProc(HWND hwnd,
							UINT msg,
							WPARAM wparam,
							LPARAM lparam)
{
	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));
}

///////////////////////////////////////////////////////

int Game_Init(void *parms, int num_parms)
{
	return (1);
}

///////////////////////////////////////////////////////

int Game_Main(void *parms, int num_parms)
{
	DWORD start_time = GetTickCount();

	// Lock time to 30 fps (1s / 30 = 33ms)
	while ((GetTickCount() - start_time) < 33);

	if (KEYDOWN(VK_ESCAPE))
		SendMessage(main_window_handle, WM_CLOSE, 0, 0);
	return (1);
}

////////////////////////////////////////////////////////

int Game_Shutdown(void *parms, int num_parms)
{
	return (1);
}

下面是一个流星的具体实例,我们在Game_Init()随机初始化256个小星星。之后
每一帧绘制时,首先擦除掉星星位置上的点,之后将星星向屏幕右侧移动并检测
是否到达屏幕边界,最后在新位置上绘制星星。这样就可以进入下一轮的主事件
循环了。

///////////////////////////////////////////////////////
#define NUM_STARS 256
#define WINDOW_WIDTH 400
#define WINDOW_HEIGHT 300

typedef struct STAR_TYP {
	int x, y;
	int vel;
	COLORREF col;
} STAR, *STAR_PTR;

HDC global_dc = NULL;
STAR stars[NUM_STARS];

void Erase_Stars(void);
void Draw_Stars(void);
void Move_Stars(void);
void Init_Stars(void);

///////////////////////////////////////////////////////

int Game_Init(void *parms, int num_parms)
{
	global_dc = GetDC(main_window_handle);
	Init_Stars();
	return (1);
}

///////////////////////////////////////////////////////

int Game_Main(void *parms, int num_parms)
{
	DWORD start_time = GetTickCount();

	Erase_Stars();
	Move_Stars();
	Draw_Stars();

	// Lock time to 30 fps (1s / 30 = 33ms)
	while ((GetTickCount() - start_time) < 33);

	if (KEYDOWN(VK_ESCAPE))
		SendMessage(main_window_handle, WM_CLOSE, 0, 0);
	return (1);
}

////////////////////////////////////////////////////////

int Game_Shutdown(void *parms, int num_parms)
{
	ReleaseDC(main_window_handle, global_dc);
	return (1);
}

////////////////////////////////////////////////////////
void Init_Stars(void)
{
	for (int index = 0; index < NUM_STARS; index++) 
	{
		stars[index].x = rand() % WINDOW_WIDTH;
		stars[index].y = rand() % WINDOW_HEIGHT;

		// random velocity 1-16
		stars[index].vel = 1 + rand() % 16;
				
		int intensity = 15 * (17 - stars[index].vel);
		stars[index].col = RGB(intensity, intensity, intensity);
	}
}

void Erase_Stars(void)
{
	for (int index = 0; index < NUM_STARS; index++)
		SetPixel(global_dc, stars[index].x, stars[index].y, RGB(0, 0, 0));
}

void Draw_Stars(void)
{
	for (int index = 0; index < NUM_STARS; index++)
		SetPixel(global_dc, stars[index].x, stars[index].y, stars[index].col);
}

void Move_Stars(void)
{
	for (int index = 0; index < NUM_STARS; index++)
	{
		stars[index].x += stars[index].vel;
		if (stars[index].x >= WINDOW_WIDTH)
			stars[index].x -= WINDOW_WIDTH;
	}
}

运行起来就是这个样子了。



这是一个使用GDI绘图的不错的小程序,该程序演示了擦除、移动、绘制动画的循环。

第一部分总结

在第2章我们通过一个简单的Windows程序学习了创建一个最简单Windows窗体的步骤:
创建Windows类,注册它,然后创建窗体,编写事件处理函数,最后编写主事件循环
从事件队列获取并分发消息到事件处理函数。并且我们还学会了使用PeekMessage()
而不是阻塞的GetMessage()编写实时的事件循环。

第3章我们学会了如何创建图标、光标、字符串等等各种资源,并在程序中引用它们。
同时我们还学习了GDI基础知识,获得DC句柄,调用TextOut输出文字。以及重要的
窗口、键盘、鼠标事件处理。

第4章我们学习了GDI高级部分绘制各种图形,定时器,常用的按钮等小控件。至此我们
已经掌握了足够的Windows编程知识来应对游戏编程,下面是总结的知识列表,留作日后
复习使用:

1.Windows程序构建步骤:WNDCLASS类、注册、创建窗体、事件处理函数、主事件循环。
2.用PeekMessage()编写实时的事件循环
3.使用图标、光标、字符串等资源,并了解VS自动产生的.RC文件和资源索引文件中有什么。
4.处理常见窗口、键盘、鼠标消息,如WM_CLOSE,WM_SIZE,获得按键,WM_MOUSE*。
5.用GDI绘制文本、点线及各种图形。
6.通过检查系统时钟来控制程序刷新的帧频。
7.使用简单的小控件,如按钮、文本框等等。

接下来我们将进入DirectX的神奇世界!


posted on 2012-04-22 15:20  毛小娃  阅读(208)  评论(0编辑  收藏  举报

导航