建立一个win32窗口(用于开发游戏)
2015-08-05 23:24
267 查看
随着游戏引擎的发展,我们能越来越方便的制作各种简单好玩的游戏,但是,对于一些古老的,简单粗暴的方法,我们是不是有必要回顾一下呢,这里,我将展示怎样建立一个些游戏用的windows窗口。
游戏开发和软件开发还是不同的,首先,游戏 的windows窗口,我们只是要一个窗口而已,至于里面的菜单栏等等等等,我们有自己的UI设计,如果使用win32 的窗口的菜单的话,估计就没有人玩了,还有一点,众所周知,游戏是一个大循环,没错,在没有结束游戏之前,游戏理想状态下,以每秒钟60次的速度不断循环着运行代码,所以,我们需要将消息队列写成循环的,接下来,我会大致讲一下Win32窗口的创建过程
1.声明一些变量,什么??#include<Windows.h>??好吧,这个大家应该都知道吧,应该是常识。我把这些东西都写在Utility,h里面,确实,就这点东西再新建个文件有点兴师动众,但是,这是一个习惯,以后会把所有工具类 的东西都写在Utility里,这里只不过是我们现在用到的东西少而已,但是这个意识必须要从头培养,好吧,我们来说正题
1.声明一些变量,这里我们需要创建一个窗口实例句柄,和一个应用程序实例句柄,窗口实例句柄用来标识这个窗口的id号,而窗口实例句柄用来标识应用程序的一个实例,一个程序可以有多个实例,那么,实例是什么呢,比如啊,你打开一个程序,就是一个实例,然后你有开了一个相同的程序,又多了一个实例,你多开了好几个,就出现了这一个应用程序的多个实例
2.声明一个消息处理函数,这个函数的作用就是处理消息的,消息这个东西我会在后面消息机制那篇博客里说明,这个函数的格式是固定的,对没错,不能改,前面的CALLBACK 意思是这个函数是回调函数,什么事回调函数呢,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数,当然这是百度说的,总之就是这个函数被一个指针指着,而这个指针在别的函数里被调用了
3.书写WinMain函数,这个WinMain跟控制台的main函数一样,都是程序的入口函数,他的函数定义如下,这个格式也是固定的
下面的步骤,是写在WinMain函数里面的,我就当作第三部的子步骤写了
1)创建一个窗口类结构体对象,并通过该对象注册一个“窗口类”,这个窗口类结构体微软已经给定义好了,我们要做的就是创建它,然后给它每个元素赋值而已
3)处理Windows消息循环,这段代码,else中锁帧的部分是自己假的,它的原理就是让当前时间和上一次的时间相减,得出一个差,看看这个差够不够1000/60,如果不够,让系统睡一会,通过这种方法,保证每秒钟刷新60次,也就是我们说的60帧
4.填写消息处理函数的函数体,这个函数的作用就是处理消息,对我们前面声明过,也写过作用,但是,函数只声明不定义,程序会报错的,所以我们在后面定义下它,之后我们的消息,都会往这里写,注意,最后的return DefWindowProc(_hWnd,_uMsg,_wParam,_lParam);一定不能少,DefWindowProc这个函数名都是系统定义好的,不能改,参数就把我们的消息处理函数的形参给它当实参就好,里面消息的具体含义,请见消息机制那篇博客
下面是完整代码,复制即可用
游戏开发和软件开发还是不同的,首先,游戏 的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); }
相关文章推荐
- Codeforces Round #250 (Div. 1) D. The Child and Sequence(线段树暴力)
- Linux sort 命令
- LeetCode221——Maximal Square
- 第一张罚单
- 代理模式
- CodeForces 85D Sum of Medians(线段树 + 离散化)
- strchr strstr (判断 子字符(串))
- perl应用:一些perl例(不断更新中ing........)
- Auto Layout
- Provided id of the wrong type for class
- 怎样快速学习一门新技术?
- PHP扩展的基本结构
- web前端的成长之路(START)
- Quartz2D-07.利用Quartz实现自定义截图效果
- 取石子游戏(hdu1527+威佐夫博弈)
- Nginx 笔记与总结(8)Location:归纳总结
- CSS3样式工具箱
- 简单工厂模式
- 电磁学教学日历
- 如此抄袭Apps之OscHub(三)