2.创建适合游戏的窗口和消息循环
来源:互联网 发布:怎样上淘宝买东西 编辑:程序博客网 时间:2024/06/04 15:08
2.创建适合游戏的窗口和消息循环
本章前言:
创建游戏窗口和处理消息循环是很重要的事情,我尝试过几种不同的窗口处理方式,这次打算使用WS_POPUP样式的窗口(无边框)。上一次的框架代码把创建窗口和消息循环放入了一个新的线程,这样在有不断的消息的时候(例如拖动窗口)也不会让主界面停止重绘。我曾经在一些书上看到有人这么推荐,但实际效果并不理想,因为消息处理函数也是在新线程被调用,这样一些消息的处理(例如WM_CHAR消息)需要和主线程同步,反而导致很繁琐。另外,可拖动改变大小的边框,在窗口大小发生变化的时候需要让DirectxX更改主显示表面大小以及处理设备丢失(DirectX 11没有那么麻烦,但还是要处理)。全屏模式下支持的分辨率和显示器有关,即便是窗口模式,我认为也应该符合显示器支持的全屏模式分辨率大小。
目标要点总结:
1. 无边框窗口
2. 窗口大小限制为显示器全屏下支持的分辨率
3. 消息处理在主线程
最终效果:
用户建立Win32项目之后,只需要如下使用便可创建默认的窗口:
//////////////////////main.cpp////////////////////////
#include <DND.h>
using namespaceDND;
System* sys;
DNDMain()
{
sys = System::Instance();
sys->Enter_GameLoop();
}
//////////////////////////////////////////////////////
默认窗口的大小使用显示器所支持的最小分辨率。其值为通过DXGI枚举显示器支持的分辨率,取第一个。
前题简要:
System为系统单例类,主要引擎功能集中在此类。继承于Singleton模板类,可以快速的实现单例的效果。
具体实现:
在主函数中,获取System类实例后,调用Enter_GameLoop函数进入游戏循环。此函数会设置帧函数(每帧调用的函数,分为帧前和帧后),然后创建窗口,进入游戏循环。
//////////////////////////////////////////////////////
void System_imp::Enter_GameLoop(void(*fixed_update)()/*= 0*/,void(*late_update)()/*= 0*/)
{
//设置帧函数
m_fixed_update = fixed_update;
m_late_update = late_update;
//创建窗口
_create_window();
//游戏循环
MSG msg;
ZeroMemory(&msg,sizeof(MSG));
while (true)
{
//首先处理消息
if (PeekMessage(&msg,NULL, 0, 0,PM_REMOVE))
{
if (msg.message ==WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//游戏内容
if (m_fixed_update)m_fixed_update();
//引擎需要执行的代码
if (m_late_update)m_late_update();
}
}
//////////////////////////////////////////////////////
在while(true)中的游戏循环中,首先处理窗口消息,然后执行帧函数。这个true也可以改为一个标志值代表是否结束程序。另外引擎的内部代码可以穿插在里面的各个部分。例如update和render放置在两个帧函数之间,按键检测放置在帧前函数之前。帧率计算和控制FPS都能通过在这里添加语句来实现。
其中_create_window函数创建了窗口,来看看具体的代码:
//////////////////////////////////////////////////////
//窗口类
WNDCLASS wc;
wc.style =CS_HREDRAW |CS_VREDRAW;
wc.lpfnWndProc =WindowProc;//消息处理函数
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance =m_instance;//实例句柄
wc.hIcon = (HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON, 0, 0,LR_DEFAULTSIZE |LR_LOADFROMFILE);
wc.hCursor =LoadCursor(0,IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//背景白色
wc.lpszMenuName = 0;
wc.lpszClassName =Value::WINDOW_CLASS_NAME;//窗口类名
if (!RegisterClass(&wc))
{
assert(0&& L"注册窗口类失败!\n");
return;
}
//////////////////////////////////////////////////////
首先是注册一个窗口类,其中WindowProc是窗口消息处理函数。m_instance是实例句柄,通过调用GetMoudleHandle(0)赋值。LoadImage函数加载一个图像作为图标,由于这个方法是在程序运行的时候才执行,所以只对状态栏和窗口左上角的图标有效。
接下来是创建窗口
//////////////////////////////////////////////////////
//创建窗口
m_hwnd = CreateWindow(
Value::WINDOW_CLASS_NAME,
Value::WINDOW_DEFAULT_TITLE,
WS_MINIMIZEBOX | WS_POPUP,
-100,
-100,
100,
100,
0 /*parenthwnd*/, 0 /* menu */, m_instance, 0 /*extra*/);
if (!m_hwnd)
{
assert(0&& L"创建窗口失败!\n");
return;
}
//////////////////////////////////////////////////////
其中WS_MINIMIZEBOX | WS_POPUP样式让窗口无边框并且能够被最小化。并且窗口大小为100,100,显示在屏幕区域之外。接着再应用窗口的大小和位置。
//////////////////////////////////////////////////////
//设置窗口
//设置大小
Set_Window_Size(m_info_system.window_size);
//设置窗口位置居中
Set_Window_Center();
ShowWindow(m_hwnd,SW_SHOW);
UpdateWindow(m_hwnd);
SetFocus(m_hwnd);
//////////////////////////////////////////////////////
上面主要调用Windows API MoveWindow来实现移动窗口和更改窗口大小。设置窗口居中的函数代码如下:
//////////////////////////////////////////////////////
void System_imp::Set_Window_Center()
{
Size size =Get_Desktop_Size();//获取桌面大小
m_info_system.window_pos= (size- m_info_system.window_size)/ 2;
//位置 等于 桌面大小减去窗口大小的一半
_apply_window();//这里面调用MoveWindow
}
//////////////////////////////////////////////////////
获取桌面大小:
//////////////////////////////////////////////////////
Size System_imp::Get_Desktop_Size()
{
return Size((GetSystemMetrics(SM_CXSCREEN)), (GetSystemMetrics(SM_CYSCREEN)));
}
//////////////////////////////////////////////////////
最后是窗口消息处理函数WindowProc,由于定义在System类内部,所以加上static声明为静态函数。
//////////////////////////////////////////////////////
static LRESULTCALLBACKWindowProc(HWNDhwnd,UINTmsg,WPARAMwparam,LPARAMlparam);
//////////////////////////////////////////////////////
DXGI相关
在配置好DirectX 11环境后,,其中一些对象需要存储,在头文件里是这样:
//////////////////////dxgi////////////////////////
IDXGIFactory* m_dxgi;
//主显卡
IDXGIAdapter* m_adapter;
//主显示器
IDXGIOutput* m_output;
// 支持的全屏显示模式的数组
DXGI_MODE_DESC* m_display_modes;
UINT m_display_mode_length;
//筛选后的信息
Array<Size>m_array_windowsize;
//////////////////////////////////////////////////////
首先需要创建DXGI对象:
//////////////////////////////////////////////////////
if (FAILED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&m_dxgi)))
{
assert(0&& L"DXGIFactory 创建失败!");
return;
}
//////////////////////////////////////////////////////
接着取得第一个显卡,也就是默认显卡:
//////////////////////////////////////////////////////
if (m_dxgi->EnumAdapters(0,&m_adapter) ==DXGI_ERROR_NOT_FOUND)
{
assert(0&& L"未找到显卡!");
return;
}
//////////////////////////////////////////////////////
然后通过显卡取得默认的显示器(output):
//////////////////////////////////////////////////////
if (m_adapter->EnumOutputs(0,&m_output) ==DXGI_ERROR_NOT_FOUND)
{
assert(0&& L"未找到显示器!");
return;
}
//////////////////////////////////////////////////////
下面就可以枚举全屏模式下所有支持的分辨率了:
////////////////////获取显示器支持的全屏显示模式//////////////////////////
m_display_modes = 0;
m_display_mode_length = 0;
DXGI_FORMAT format = DXGI_FORMAT_B8G8R8A8_UNORM;//32位真彩色
m_output->GetDisplayModeList(format, 0,&m_display_mode_length,NULL);
m_display_modes = newDXGI_MODE_DESC[m_display_mode_length];
m_output->GetDisplayModeList(format,0, &m_display_mode_length,m_display_modes);
//////////////////////////////////////////////////////
其中返回的DXGI_MODE_DESC类型数组就包含分辨率大小信息。还包括刷新率、扫描方式、缩放方式等等数据,但我们只需要筛选出支持60HZ刷新率的分辨率大小,其他的不用关心。
作者:略游
日期:17-05-22
QQ:1339484752
- 2.创建适合游戏的窗口和消息循环
- QT创建窗口程序、消息循环和WinMain函数
- Windows编程 从消息窗口到基本窗口 游戏循环窗口框架的简单实现
- 消息的概念和窗口的创建
- win32创建窗口及其消息循环
- 创建窗口第三步 消息循环(详解)
- QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数
- 详解QT源码之QT创建窗口程序、消息循环和WinMain函数
- QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数
- QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数
- QT源码之QT创建窗口程序、消息循环和WinMain函数
- QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数
- 理解MiniGUI消息循环和窗口过程
- 理解消息循环和窗口过程(转)
- 创建自己的窗口消息
- Android消息处理机制(二):循环和消息队列的创建
- 父子窗口分属不同消息循环在WinXP和WinCE的差异
- win32 创建仅接收消息的窗口
- SikuliX安装
- C++之函数模板与类模板的区别(三)
- JQuery基础知识
- OpenGL导入3D模型的准备工作
- Android-ViewPager-循环滑动(轮播)
- 2.创建适合游戏的窗口和消息循环
- Makefile常用万能模板(包括静态链接库、动态链接库、可执行文件)
- 动态代理
- SVN与Git的区别
- 第一个项目的总结
- hdu i need offer
- 计算机网络漫谈之传输层
- 关于phpredis拓展hScan的一点小坑
- 洛谷OJ