2.创建适合游戏的窗口和消息循环
2.創(chuàng)建適合游戲的窗口和消息循環(huán)
? 本章前言:
??? 創(chuàng)建游戲窗口和處理消息循環(huán)是很重要的事情,我嘗試過幾種不同的窗口處理方式,這次打算使用WS_POPUP樣式的窗口(無邊框)。上一次的框架代碼把創(chuàng)建窗口和消息循環(huán)放入了一個新的線程,這樣在有不斷的消息的時候(例如拖動窗口)也不會讓主界面停止重繪。我曾經(jīng)在一些書上看到有人這么推薦,但實際效果并不理想,因為消息處理函數(shù)也是在新線程被調(diào)用,這樣一些消息的處理(例如WM_CHAR消息)需要和主線程同步,反而導致很繁瑣。另外,可拖動改變大小的邊框,在窗口大小發(fā)生變化的時候需要讓DirectxX更改主顯示表面大小以及處理設(shè)備丟失(DirectX 11沒有那么麻煩,但還是要處理)。全屏模式下支持的分辨率和顯示器有關(guān),即便是窗口模式,我認為也應(yīng)該符合顯示器支持的全屏模式分辨率大小。
? 目標要點總結(jié):
1.? 無邊框窗口
2.? 窗口大小限制為顯示器全屏下支持的分辨率
3.? 消息處理在主線程
? 最終效果:
??? 用戶建立Win32項目之后,只需要如下使用便可創(chuàng)建默認的窗口:
//main.cpp
#include <DND.h>
using namespaceDND;
System* sys;
DNDMain()
{
??? sys = System::Instance();
??? sys->Enter_GameLoop();
}
//
??? 默認窗口的大小使用顯示器所支持的最小分辨率。其值為通過DXGI枚舉顯示器支持的分辨率,取第一個。
? 前題簡要:
??? System為系統(tǒng)單例類,主要引擎功能集中在此類。繼承于Singleton模板類,可以快速的實現(xiàn)單例的效果。
? 具體實現(xiàn):
??? 在主函數(shù)中,獲取System類實例后,調(diào)用Enter_GameLoop函數(shù)進入游戲循環(huán)。此函數(shù)會設(shè)置幀函數(shù)(每幀調(diào)用的函數(shù),分為幀前和幀后),然后創(chuàng)建窗口,進入游戲循環(huán)。
//
void System_imp::Enter_GameLoop(void(*fixed_update)()/*= 0*/, void(*late_update)()/*= 0*/)
{
??? //設(shè)置幀函數(shù)
??? m_fixed_update = fixed_update;
??? m_late_update = late_update;
??? //創(chuàng)建窗口
??? _create_window();
??? //游戲循環(huán)
??? 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);
??????? }
??????? //游戲內(nèi)容
??????? if (m_fixed_update)m_fixed_update();
??????? //引擎需要執(zhí)行的代碼
??????? if (m_late_update)m_late_update();
??? }
}
//
??? 在while(true)中的游戲循環(huán)中,首先處理窗口消息,然后執(zhí)行幀函數(shù)。這個true也可以改為一個標志值代表是否結(jié)束程序。另外引擎的內(nèi)部代碼可以穿插在里面的各個部分。例如update和render放置在兩個幀函數(shù)之間,按鍵檢測放置在幀前函數(shù)之前。幀率計算和控制FPS都能通過在這里添加語句來實現(xiàn)。
??? 其中_create_window函數(shù)創(chuàng)建了窗口,來看看具體的代碼:
//
//窗口類
WNDCLASS wc;
wc.style =CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc =WindowProc; //消息處理函數(shù)
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是窗口消息處理函數(shù)。m_instance是實例句柄,通過調(diào)用GetMoudleHandle(0)賦值。LoadImage函數(shù)加載一個圖像作為圖標,由于這個方法是在程序運行的時候才執(zhí)行,所以只對狀態(tài)欄和窗口左上角的圖標有效。
??? 接下來是創(chuàng)建窗口
//
//創(chuàng)建窗口
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"創(chuàng)建窗口失敗!\n");
??? return;
}
//
??? 其中WS_MINIMIZEBOX | WS_POPUP樣式讓窗口無邊框并且能夠被最小化。并且窗口大小為100,100,顯示在屏幕區(qū)域之外。接著再應(yīng)用窗口的大小和位置。
//
//設(shè)置窗口
//設(shè)置大小
Set_Window_Size(m_info_system.window_size);
//設(shè)置窗口位置居中
Set_Window_Center();
?
ShowWindow(m_hwnd,SW_SHOW);
UpdateWindow(m_hwnd);
SetFocus(m_hwnd);
//
??? 上面主要調(diào)用Windows API MoveWindow來實現(xiàn)移動窗口和更改窗口大小。設(shè)置窗口居中的函數(shù)代碼如下:
//
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();//這里面調(diào)用MoveWindow
}
//
??? 獲取桌面大小:
//
Size System_imp::Get_Desktop_Size()
{
??? return Size((GetSystemMetrics(SM_CXSCREEN)), (GetSystemMetrics(SM_CYSCREEN)));
}
//
??? 最后是窗口消息處理函數(shù)WindowProc,由于定義在System類內(nèi)部,所以加上static聲明為靜態(tài)函數(shù)。
//
static LRESULTCALLBACKWindowProc(HWNDhwnd,UINTmsg,WPARAMwparam,LPARAMlparam);
//
? DXGI相關(guān)
??? 在配置好DirectX 11環(huán)境后,,其中一些對象需要存儲,在頭文件里是這樣:
//dxgi
IDXGIFactory* m_dxgi;
//主顯卡
IDXGIAdapter* m_adapter;
//主顯示器
IDXGIOutput* m_output;
// 支持的全屏顯示模式的數(shù)組
DXGI_MODE_DESC* m_display_modes;
UINT m_display_mode_length;
//篩選后的信息
Array<Size>m_array_windowsize;
//
??? 首先需要創(chuàng)建DXGI對象:
//
if (FAILED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&m_dxgi)))
{
??? assert(0&& L"DXGIFactory 創(chuàng)建失敗!");
??? 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類型數(shù)組就包含分辨率大小信息。還包括刷新率、掃描方式、縮放方式等等數(shù)據(jù),但我們只需要篩選出支持60HZ刷新率的分辨率大小,其他的不用關(guān)心。
???
?
作者:略游
日期:17-05-22
QQ:1339484752
總結(jié)
以上是生活随笔為你收集整理的2.创建适合游戏的窗口和消息循环的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1.封装WinMain至动态链接库
- 下一篇: 3.vector实现字符串类