建立一个win32窗口(用于开发游戏)

来源:互联网 发布:男友一夜 知乎 编辑:程序博客网 时间:2024/06/10 01:04

       随着游戏引擎的发展,我们能越来越方便的制作各种简单好玩的游戏,但是,对于一些古老的,简单粗暴的方法,我们是不是有必要回顾一下呢,这里,我将展示怎样建立一个些游戏用的windows窗口。

       游戏开发和软件开发还是不同的,首先,游戏 的windows窗口,我们只是要一个窗口而已,至于里面的菜单栏等等等等,我们有自己的UI设计,如果使用win32 的窗口的菜单的话,估计就没有人玩了,还有一点,众所周知,游戏是一个大循环,没错,在没有结束游戏之前,游戏理想状态下,以每秒钟60次的速度不断循环着运行代码,所以,我们需要将消息队列写成循环的,接下来,我会大致讲一下Win32窗口的创建过程

       1.声明一些变量,什么??#include<Windows.h>??好吧,这个大家应该都知道吧,应该是常识。我把这些东西都写在Utility,h里面,确实,就这点东西再新建个文件有点兴师动众,但是,这是一个习惯,以后会把所有工具类 的东西都写在Utility里,这里只不过是我们现在用到的东西少而已,但是这个意识必须要从头培养,好吧,我们来说正题

       1.声明一些变量,这里我们需要创建一个窗口实例句柄,和一个应用程序实例句柄,窗口实例句柄用来标识这个窗口的id号,而窗口实例句柄用来标识应用程序的一个实例,一个程序可以有多个实例,那么,实例是什么呢,比如啊,你打开一个程序,就是一个实例,然后你有开了一个相同的程序,又多了一个实例,你多开了好几个,就出现了这一个应用程序的多个实例

HWND g_hWnd;//handle:句柄(标识符) WND:Window//窗口句柄,计算机用于唯一标识窗口的ID号HINSTANCE g_hInstance;//Instance:实例//应用程序实例句柄

       2.声明一个消息处理函数,这个函数的作用就是处理消息的,消息这个东西我会在后面消息机制那篇博客里说明,这个函数的格式是固定的,对没错,不能改,前面的CALLBACK 意思是这个函数是回调函数,什么事回调函数呢,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数,当然这是百度说的,总之就是这个函数被一个指针指着,而这个指针在别的函数里被调用了

LRESULT CALLBACK WndProc(HWND _hWnd,          //窗口句柄UINT _uMsg,           //传递的消息WPARAM _wParam,       //高位附加信息LPARAM _lParam);      //地位附加信息

      3.书写WinMain函数,这个WinMain跟控制台的main函数一样,都是程序的入口函数,他的函数定义如下,这个格式也是固定的

INT WINAPI WinMain(_In_ HINSTANCE hInstance,//当前应用程序实例句柄_In_opt_ HINSTANCE hPrevInstance,//上一个该应用程序的实例句柄_In_ LPSTR lpCmdLine,//命令行_In_ int nShowCmd)//显示方式{}

       下面的步骤,是写在WinMain函数里面的,我就当作第三部的子步骤写了

               1)创建一个窗口类结构体对象,并通过该对象注册一个“窗口类”,这个窗口类结构体微软已经给定义好了,我们要做的就是创建它,然后给它每个元素赋值而已

WNDCLASS wc;//memset(&wc, 0, sizeof(wc));ZeroMemory(&wc, sizeof(wc));wc.cbClsExtra = 0;//类的附加信息,为0即可wc.cbWndExtra = 0;//窗口的附加信息,为0即可wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);//灰色的窗口背景颜色wc.hCursor = LoadCursor(0, IDC_ARROW);//光标的样式wc.hIcon = LoadIcon(0, IDI_APPLICATION);//图标样式wc.hInstance = hInstance;//应用程序实例句柄wc.lpfnWndProc = WndProc;//当前窗口所对应的消息处理函数wc.lpszClassName = TEXT("linimass");//当前窗口类的名字wc.lpszMenuName = nullptr;//菜单名wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;//窗口的风格(水平重绘/垂直重绘/鼠标双击)RegisterClass(&wc);//跟操作系统注册一个上边结构体描述的窗口类
              2)创建窗口、显示/刷新窗口
 
g_hWnd = CreateWindow(wc.lpszClassName,//窗口类的名字TEXT("FirstWindow"),//窗口的名字WS_OVERLAPPEDWINDOW,//100, 100,//窗口左上角在屏幕上的位置480, 320,//窗口的宽、高0,//父窗口句柄0,//菜单句柄,为0即可hInstance,//应用程序实例句柄0//系统保留参数,为0即可);if (g_hWnd)//如果窗口创建成功{ShowWindow(g_hWnd, SW_SHOWNORMAL);//显示窗口UpdateWindow(g_hWnd);//刷新窗口}else{return 0;}

             3)处理Windows消息循环,这段代码,else中锁帧的部分是自己假的,它的原理就是让当前时间和上一次的时间相减,得出一个差,看看这个差够不够1000/60,如果不够,让系统睡一会,通过这种方法,保证每秒钟刷新60次,也就是我们说的60帧

MSG msg;// 消息结构体对象message:消息  ZeroMemory(&msg, sizeof(msg));while (msg.message != WM_QUIT)//消息循环(游戏的主循环){//从消息队列中获取消息,如果获取到if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){TranslateMessage(&msg);//翻译消息DispatchMessage(&msg);//派发消息(派发给消息处理函数来处理)}else   //没有获取到{static DWORD dwTime = timeGetTime();DWORD dwCurrentTime = timeGetTime();DWORD dwElapsedTime = dwCurrentTime - dwTime;float fElapsedTime = dwElapsedTime*0.001f;</span>            //这段代码的作用为锁帧,60帧,也可以用编译预处理指令控制,并且有玩家决定是否进行锁帧操作//   游戏的主逻辑循环// 游戏的主渲染循环//------------------------------------------if( dwElapsedTime < 1000/60 )Sleep( 1000/60 - dwElapsedTime );dwTime = dwCurrentTime;</span>}}


        4.填写消息处理函数的函数体,这个函数的作用就是处理消息,对我们前面声明过,也写过作用,但是,函数只声明不定义,程序会报错的,所以我们在后面定义下它,之后我们的消息,都会往这里写,注意,最后的return DefWindowProc(_hWnd,_uMsg,_wParam,_lParam);一定不能少,DefWindowProc这个函数名都是系统定义好的,不能改,参数就把我们的消息处理函数的形参给它当实参就好,里面消息的具体含义,请见消息机制那篇博客

LRESULT CALLBACK WndProc(HWND _hWnd,UINT _uMsg,WPARAM _wParam,LPARAM _lParam){switch (_uMsg){case WM_KEYDOWN:if (_wParam == VK_ESCAPE)DestroyWindow(_hWnd);//触发WM_DESTROY这个消息break;case WM_CLOSE:DestroyWindow(_hWnd);break;case WM_DESTROY:PostQuitMessage(0);///触发WM_QUIT这个消息break;}//我们没有处理的消息一旦触发,交给操作系统自动处理return DefWindowProc(_hWnd,_uMsg,_wParam,_lParam);}


       

下面是完整代码,复制即可用


//Utility.h#ifndef WINDOWS_H_#define WINDOWS_H_#include <Windows.h>//WIN32窗口应用程序所需要包含的头文件#include <mmsystem.h>#pragma comment(lib,"Winmm.lib")#endif




//WinMain.cpp#include "Utility.h"/*创建Windows窗口的步骤:(1)声明一些变量(2)声明一个消息处理函数(作用:响应消息)(3)书写WinMain函数(Window窗口应用程序的入口函数,作用:用来创建Windows窗口)1)创建一个窗口类结构体对象,并通过该对象注册一个“窗口类”2)创建窗口、显示/刷新窗口3)处理Windows消息循环(4)填写消息处理函数的函数体*///---------(1)声明一些变量---------HWND g_hWnd;//handle:句柄(标识符) WND:Window//窗口句柄,计算机用于唯一标识窗口的ID号HINSTANCE g_hInstance;//Instance:实例//应用程序实例句柄//---------(2)声明一个消息处理函数(回调函数)---------LRESULT CALLBACK WndProc(HWND _hWnd, UINT _uMsg,WPARAM _wParam, LPARAM _lParam);//--------(3)书写WinMain函数(Window窗口应用程序的入口函数)--------INT WINAPI WinMain(_In_ HINSTANCE hInstance,//当前应用程序实例句柄_In_opt_ HINSTANCE hPrevInstance,//上一个该应用程序的实例句柄_In_ LPSTR lpCmdLine,//命令行_In_ int nShowCmd)//显示方式{// 1)创建一个窗口类结构体对象,并通过该对象注册一个“窗口类”WNDCLASS wc;//memset(&wc, 0, sizeof(wc));ZeroMemory(&wc, sizeof(wc));wc.cbClsExtra = 0;//类的附加信息,为0即可wc.cbWndExtra = 0;//窗口的附加信息,为0即可wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);//灰色的窗口背景颜色wc.hCursor = LoadCursor(0, IDC_ARROW);//光标的样式wc.hIcon = LoadIcon(0, IDI_APPLICATION);//图标样式wc.hInstance = hInstance;//应用程序实例句柄wc.lpfnWndProc = WndProc;//当前窗口所对应的消息处理函数wc.lpszClassName = TEXT("linimass");//当前窗口类的名字wc.lpszMenuName = nullptr;//菜单名wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;//窗口的风格(水平重绘/垂直重绘/鼠标双击)RegisterClass(&wc);//跟操作系统注册一个上边结构体描述的窗口类//(名字是linimass,背景颜色是灰色的......)// 2)创建窗口g_hWnd = CreateWindow(wc.lpszClassName,//窗口类的名字TEXT("FirstWindow"),//窗口的名字WS_OVERLAPPEDWINDOW,//100, 100,//窗口左上角在屏幕上的位置480, 320,//窗口的宽、高0,//父窗口句柄0,//菜单句柄,为0即可hInstance,//应用程序实例句柄0//系统保留参数,为0即可);if (g_hWnd)//如果窗口创建成功{ShowWindow(g_hWnd, SW_SHOWNORMAL);//显示窗口UpdateWindow(g_hWnd);//刷新窗口}else{return 0;}// 3)处理Windows消息循环MSG msg;// 消息结构体对象message:消息  ZeroMemory(&msg, sizeof(msg));while (msg.message != WM_QUIT)//消息循环(游戏的主循环){//从消息队列中获取消息,如果获取到if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){TranslateMessage(&msg);//翻译消息DispatchMessage(&msg);//派发消息(派发给消息处理函数来处理)}else   //没有获取到{static DWORD dwTime = timeGetTime();DWORD dwCurrentTime = timeGetTime();DWORD dwElapsedTime = dwCurrentTime - dwTime;float fElapsedTime = dwElapsedTime*0.001f;</span>            //这段代码的作用为锁帧,60帧,也可以用编译预处理指令控制,并且有玩家决定是否进行锁帧操作//   游戏的主逻辑循环// 游戏的主渲染循环//------------------------------------------if( dwElapsedTime < 1000/60 )Sleep( 1000/60 - dwElapsedTime );dwTime = dwCurrentTime;</span>}}return 0;}//--------(4)填写消息处理函数的函数体--------LRESULT CALLBACK WndProc(HWND _hWnd,UINT _uMsg,WPARAM _wParam,LPARAM _lParam){switch (_uMsg){case WM_KEYDOWN:if (_wParam == VK_ESCAPE)DestroyWindow(_hWnd);//触发WM_DESTROY这个消息break;case WM_CLOSE:DestroyWindow(_hWnd);break;case WM_DESTROY:PostQuitMessage(0);///触发WM_QUIT这个消息break;}//我们没有处理的消息一旦触发,交给操作系统自动处理return DefWindowProc(_hWnd,_uMsg,_wParam,_lParam);}


0 0
原创粉丝点击