• 《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的神奇世界!


  • 相关阅读:
    java mybatis 新增记录 与 insertSelective 保存问题
    01 开发环境搭建
    2021年:系列文章总结
    在win10上安装MTK驱动(附驱动下载链接)
    Gerrit 大量代码提交流程优化
    mysqldump的使用
    配置 Gerrit 迁移
    解决:编译安卓源码时 JDK 报错 error='Not enough space' (errno=12)
    修改Git Commit提交记录的用户名Name和邮箱Email
    Android 各层架构
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157842.html
Copyright © 2020-2023  润新知