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

原创粉丝点击