Window Initialization

Window Initialization

与所有Microsoft Windows程序一样,DirectX游戏和图形应用程序也需要通过窗口来显示,必须通过调用Windows API来创建这些窗口。列表10.5列出了Game类的头文件,主要包含了用于window initialization的成员。在第11章,“Direct3D Initialization”Game类中将会增加用于initializing Direct3D的成员变量和成员函数。
列表10.5 The Game.h Header File

#pragma once

#include <windows.h>
#include <string>
#include "GameClock.h"
#include "GameTime.h"

using namespace Library;

namespace Library
{
	class Game
	{
	public:
		Game(HINSTANCE instance, const std::wstring& windowClass, const std::wstring& windowTitle, int showCommand);
		~Game();

		HINSTANCE Instance() const;
		HWND WindowHandle() const;
		const WNDCLASSEX& Window() const; 
		const std::wstring& WindowClass() const;
		const std::wstring& WindowTitle() const;
		int ScreenWidth() const;
		int ScreenHeight() const;

		virtual void Run();
		virtual void Exit();
		virtual void Initialize();
		virtual void Update(const GameTime& gameTime);
		virtual void Draw(const GameTime& gameTime);

	protected:
		virtual void InitializeWindow();
		virtual void Shutdown();

		static const UINT DefaultScreenWidth;
		static const UINT DefaultScreenHeight;

		HINSTANCE mInstance;
		std::wstring mWindowClass;
		std::wstring mWindowTitle;
		int mShowCommand;
		
		HWND mWindowHandle;
		WNDCLASSEX mWindow;		

		UINT mScreenWidth;
		UINT mScreenHeight;

		GameClock mGameClock;
		GameTime mGameTime;

	private:
		Game(const Game& rhs);
		Game& operator=(const Game& rhs);

		POINT CenterWindow(int windowWidth, int windowHeight);
		static LRESULT WINAPI WndProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam);		
	};
}

正如你所看到的,初始化一个窗口需要大量的声明。首先,Game类的构造函数接收一个instance handle(实例句柄)和一个window class name的参数。第一个参数是程序实例的一个句柄,包含了windows procedure,用于从操作系统向窗口传递消息的回调。第二个参数(window class name)只是一个string。这两个参数结合一起,就可以唯一的确定操作系统中大量窗口的其中一个。参数showCommand是由程序的入口点WinMain()函数中的showCommand参数传递过来的,用于确定窗口的显示方式。参数windowTitle是一个string应用于窗口的标题栏上。所有这些参数存储到对应的成员变量中,并用于InitializeWindow()函数中。列表10.6列出了InitializeWindow()函数的实现代码。
列表10.6 The Game::InitializeWindow() Method

	void Game::InitializeWindow()
	{
		ZeroMemory(&mWindow, sizeof(mWindow));
		mWindow.cbSize = sizeof(WNDCLASSEX);
		mWindow.style = CS_CLASSDC;
		mWindow.lpfnWndProc = WndProc;
		mWindow.hInstance = mInstance;
		mWindow.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
		mWindow.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);
		mWindow.hCursor = LoadCursor(nullptr, IDC_ARROW);
		mWindow.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
		mWindow.lpszClassName = mWindowClass.c_str();		

		RECT windowRectangle = { 0, 0, mScreenWidth, mScreenHeight };
		AdjustWindowRect(&windowRectangle, WS_OVERLAPPEDWINDOW, FALSE);

		RegisterClassEx(&mWindow);
		POINT center = CenterWindow(mScreenWidth, mScreenHeight);
		mWindowHandle = CreateWindow(mWindowClass.c_str(), mWindowTitle.c_str(), WS_OVERLAPPEDWINDOW, center.x, center.y, windowRectangle.right - windowRectangle.left, windowRectangle.bottom - windowRectangle.top, nullptr, nullptr, mInstance, nullptr);

		ShowWindow(mWindowHandle, mShowCommand);
		UpdateWindow(mWindowHandle);
	}


在Game::Initialization()函数中,通过调用RgisterClassEx()和CreateWindow()函数创建一个窗口,随后调用函数ShowWindow()显示窗口。通过WNDCLASSX结构体类型定义一个窗口,该结构体中包含了大量的成员用于自定义窗口的显示结果。通过AdjustWindowRect()函数可以在窗口里面创建一个指定大小的client area(窗口客户区),大小由类成员mScreenWidth和mScreenHeigth指定。Client area在窗口内部,不包括其他的窗口元素,如标题栏,状态栏或滚动条。
调用CenterWindow()函数可以把窗口放在屏幕的中心位置,但是该函数并不是一个Windows API函数。列表10.7列出了CenterWindow()函数的实现代码,以及WndProc()回调函数代码。
列表10.7 Implementations for Game::WndProc() and Game::CenterWindow()

	LRESULT WINAPI Game::WndProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam)
	{
		switch(message)
		{
			case WM_DESTROY:
				PostQuitMessage(0);
				return 0;
		}

		return DefWindowProc(windowHandle, message, wParam, lParam);
	}

	POINT Game::CenterWindow(int windowWidth, int windowHeight)
	{
		int screenWidth = GetSystemMetrics(SM_CXSCREEN);
		int screenHeight = GetSystemMetrics(SM_CYSCREEN);

		POINT center;
		center.x = (screenWidth - windowWidth) / 2;
		center.y = (screenHeight - windowHeight) / 2;

		return center;
	}	


在CenterWindow()函数中,通过调用Windows API GetSystemMetrics()函数获取屏幕的宽度和高度,并返回窗口位于屏幕中心的起点。所有的Windows应用程序都需要一个windows procedure。WndProc()函数的实现中,只是简单地把所有消息都传递给default windows procedure(默认的窗口消息处理函数DefWindowProc()),除了WM_DESTROY消息(销毁窗口的消息),用于在窗口退出时关闭程序。收到该消息时,调用PostQuitMessage()函数,向Windows的消息队列中发送一个WM_QUIT消息。当程序接收到WM_QUIT消息时,就会退出Windows消息循环,并结束运行。
根据这些代码,可以把Game::Run()函数更新为列表10.8所示的代码。
列表10.8 The Game::Run() Method

	void Game::Run()
	{
		InitializeWindow();
		Initialize();

		MSG message;
		ZeroMemory(&message, sizeof(message));
		
		mGameClock.Reset();		

		while(message.message != WM_QUIT)
		{
			if (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE))
			{
				TranslateMessage(&message);
				DispatchMessage(&message);
			}
			else
			{
				mGameClock.UpdateGameTime(mGameTime);
				Update(mGameTime);
				Draw(mGameTime);
			}
		}

		Shutdown();
	}


Game::Run()函数就是game loop,但是增加了window initialization以及Windows消息队列的处理。Windows API函数PeekMessage()从消息队列中获取消息,并通过调用TranslateMessage()和DispathMessage()函数派发消息。WndProc()回调函数用于处理接收到的消息。如果想到了解更多关于窗口的创建以及Windows消息循环相关的内容可以查看在线文档,但是目前的内容已经足够在屏幕上显示一个窗口了。编译并运行该应用程序,就可以看到如图10.10所示的图片。


图10.10 A blank window created through the demo framework.
这个窗口可能看起来有一点无趣,但是距离在屏幕上渲染一些内容已经很接近了。下一步是初始化Direct3D。

总结

本章完成了渲染引擎的基础。建立了Visual Studio projects,并配置编译选项,设计一个game loop并使用Windows API初始化了一个窗口。
下一章,将进一步扩展该程序并创建第一个Direct3D示例。

Exercises

1. From within the debugger, walk through the code used to initialize the game loop and create
a blank window. Visit the book’s companion website to find an associated demo application.

1.在调试模式下,一步步执行game loop中initialize部分的代码,并创建一个空白的窗口程序。访问本书的配套网站,找到对应的示例程序。

本章配套学习代码
需要注意的是,我使用了Visual Studio 2015创建了相关的工程,并加入了DirectXTK,Effects11的源码
该RenderEngine解决方案中,配置了工程所需的相关路径,与原书中有所不同,但是目录结构一致。此外,本书的配套网站上的代码,并不能完全按照书中所讲的那样配置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值