2.創建適合遊戲的窗口和消息循環

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章