您的位置:首页 > 其它

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

2015-08-05 23:24 267 查看
随着游戏引擎的发展,我们能越来越方便的制作各种简单好玩的游戏,但是,对于一些古老的,简单粗暴的方法,我们是不是有必要回顾一下呢,这里,我将展示怎样建立一个些游戏用的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); }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: