使游戏全屏显示很容易,但是需要更改程序的一些细节,并添加几行代码。在本节中,我们将介绍两件事:首先,我们将介绍如何全球化你的屏幕分辨率以及为什么要这样做;其次,我们将介绍如何使窗口进行全屏模式并再次返回的机制。
设置屏幕尺寸
在你的DirectX游戏编程中,你会遇到许多需要了解屏幕尺寸的函数和结构体。当你决定更改分辨率,尤其是当你决定在运行时更改分辨率时,这可能会很麻烦。现在,我们将介绍一种简单的方法来标准化程序中的屏幕大小。
首先,我们必须早程序顶部添加两个指令,它们代表屏幕的宽度和高度。
// 定义屏幕分辨率
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
下一步是遍历程序,指示窗口的宽度和高度。到本教程目前为止,你只有两个地方需要修改。对代码执行如下操作:
hWnd = CreateWindowEx(NULL,
L"WindowClass",
L"Our Direct3D Program",
WS_OVERLAPPEDWINDOW,
300, 300,
SCREEN_WIDTH, SCREEN_HEIGHT, // 将窗口设置为新的分辨率
NULL,
NULL,
hInstance,
NULL);
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = SCREEN_WIDTH; // 设置新的宽度
viewport.Height = SCREEN_HEIGHT; //设置新的高度
在下一节中,我们将介绍在运行时更改屏幕后如何在整个游戏中保持屏幕尺寸。大多数PC都有特定的分辨率,下表中可以找到最常见的分辨率。
更改为全屏模式
尽管几乎所有主要的游戏都以全屏模式播放,但许多游戏都具有在全屏和窗口模式之间切换的功能。虽然我们默认情况下希望全屏显示,但我们也希望用户能够在全屏和窗口之间轻松切换(通常使用Alt+Enter键完成)。当切换到全屏模式时,我们需要做以下几件事:1、修改窗口使其没有背景;2、将后台缓冲区设置为特定大小;3、将DirectX设置为在使用Alt-Enter时自动切换;4、修改CleanD3D()函数,使其在关闭时进行全屏关闭。
1、修改窗口使其没有背景
要删除窗口的背景,我们需要注释掉WINDOWCLASSEX结构的hbrBackground
成员,该成员用于设置背景wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
。这样做会使背景保持不变,这意味着在游戏开始前一两秒钟只显示背景而不会显示为窗口(这使得游戏看起来很专业)
2、将后台缓冲区设置为特定大小
接下来,我们必须告诉DirectX我们新的屏幕分辨率。为此,我们对上一节中构建的scd结构进行一些更改:
void initD3D(HWND hWnd)
{
DXGI_SWAP_CHAIN_DESC scd; // create a struct to hold various swap chain information
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC)); // clear out the struct for use
scd.BufferCount = 1; // one back buffer
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // use 32-bit color
scd.BufferDesc.Width = SCREEN_WIDTH; // 设置后缓冲区宽度
scd.BufferDesc.Height = SCREEN_HEIGHT; // 设置后缓冲区高度
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // how swap chain is to be used
scd.OutputWindow = hWnd; // the window to be used
scd.SampleDesc.Count = 1; // how many multisamples
scd.SampleDesc.Quality = 0; // multisample quality level
scd.Windowed = TRUE; // windowed/full-screen mode
// ...
3、将DirectX设置为在使用Alt-Enter时自动切换
这一步很简单,我们需要做的就是在scd结构中添加一个标志,并将该标志放入DXGI_SWAP_CHAIN_DESC
结构体的Flags
成员中。
void initD3D(HWND hWnd)
{
DXGI_SWAP_CHAIN_DESC scd; // create a struct to hold various swap chain information
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC)); // clear out the struct for use
scd.BufferCount = 1; // one back buffer
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // use 32-bit color
scd.BufferDesc.Width = SCREEN_WIDTH; // set the back buffer width
scd.BufferDesc.Height = SCREEN_HEIGHT; // set the back buffer height
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // how swap chain is to be used
scd.OutputWindow = hWnd; // the window to be used
scd.SampleDesc.Count = 1; // how many multisamples
scd.SampleDesc.Quality = 0; // multisample quality level
scd.Windowed = TRUE; // windowed/full-screen mode
scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // 允许全屏切换
// ...
4、修改CleanD3D()函数,使其在关闭时进行全屏关闭
在全屏模式下,Direct3D实际上无法关闭,这是由于在后台发生的某些线程问题,要正确关闭,我们必须确保我们处于窗口模式。我们可以使用SetFullscreenState()
函数来做到这一点。
// this is the function that cleans up Direct3D and COM
void CleanD3D(void)
{
swapchain->SetFullscreenState(FALSE, NULL); // 切换到窗口模式
// close and release all existing COM objects
swapchain->Release();
backbuffer->Release();
dev->Release();
devcon->Release();
}
SetFullscreenState()
函数的第一个参数是你希望切换到的转态,FALSE
表示窗口模式,而TRUE
表示全屏模式。第二个参数是一项功能,可以让你选择要使用的视频适配器,对于使用多个显示器的特殊场景,这将很有用。但是,对于几乎所有游戏,你都可以设置为NULL,它将自动选择正确的适配器。
最终程序
// include the basic windows header files and the Direct3D header files
#include <windows.h>
#include <windowsx.h>
#include <d3d11.h>
#include <d3dx11.h>
#include <d3dx10.h>
// include the Direct3D Library file
#pragma comment (lib, "d3d11.lib")
#pragma comment (lib, "d3dx11.lib")
#pragma comment (lib, "d3dx10.lib")
// define the screen resolution
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
// global declarations
IDXGISwapChain *swapchain; // the pointer to the swap chain interface
ID3D11Device *dev; // the pointer to our Direct3D device interface
ID3D11DeviceContext *devcon; // the pointer to our Direct3D device context
ID3D11RenderTargetView *backbuffer; // the pointer to our back buffer
// function prototypes
void InitD3D(HWND hWnd); // sets up and initializes Direct3D
void RenderFrame(void); // renders a single frame
void CleanD3D(void); // closes Direct3D and releases memory
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HWND hWnd;
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
// wc.hbrBackground = (HBRUSH)COLOR_WINDOW; // 不在需要
wc.lpszClassName = L"WindowClass";
RegisterClassEx(&wc);
RECT wr = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
hWnd = CreateWindowEx(NULL,
L"WindowClass",
L"Our First Direct3D Program",
WS_OVERLAPPEDWINDOW,
300,
300,
wr.right - wr.left,
wr.bottom - wr.top,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hWnd, nCmdShow);
// set up and initialize Direct3D
InitD3D(hWnd);
// enter the main loop:
MSG msg;
while(TRUE)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if(msg.message == WM_QUIT)
break;
}
RenderFrame();
}
// clean up DirectX and COM
CleanD3D();
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;
}
return DefWindowProc (hWnd, message, wParam, lParam);
}
// this function initializes and prepares Direct3D for use
void InitD3D(HWND hWnd)
{
// create a struct to hold information about the swap chain
DXGI_SWAP_CHAIN_DESC scd;
// clear out the struct for use
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
// fill the swap chain description struct
scd.BufferCount = 1; // one back buffer
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // use 32-bit color
scd.BufferDesc.Width = SCREEN_WIDTH; // set the back buffer width
scd.BufferDesc.Height = SCREEN_HEIGHT; // set the back buffer height
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // how swap chain is to be used
scd.OutputWindow = hWnd; // the window to be used
scd.SampleDesc.Count = 4; // how many multisamples
scd.Windowed = TRUE; // windowed/full-screen mode
scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // allow full-screen switching
// create a device, device context and swap chain using the information in the scd struct
D3D11CreateDeviceAndSwapChain(NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
NULL,
NULL,
NULL,
D3D11_SDK_VERSION,
&scd,
&swapchain,
&dev,
NULL,
&devcon);
// get the address of the back buffer
ID3D11Texture2D *pBackBuffer;
swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
// use the back buffer address to create the render target
dev->CreateRenderTargetView(pBackBuffer, NULL, &backbuffer);
pBackBuffer->Release();
// set the render target as the back buffer
devcon->OMSetRenderTargets(1, &backbuffer, NULL);
// Set the viewport
D3D11_VIEWPORT viewport;
ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = SCREEN_WIDTH;
viewport.Height = SCREEN_HEIGHT;
devcon->RSSetViewports(1, &viewport);
}
// this is the function used to render a single frame
void RenderFrame(void)
{
// clear the back buffer to a deep blue
devcon->ClearRenderTargetView(backbuffer, D3DXCOLOR(0.0f, 0.2f, 0.4f, 1.0f));
// do 3D rendering on the back buffer here
// switch the back buffer and the front buffer
swapchain->Present(0, 0);
}
// this is the function that cleans up Direct3D and COM
void CleanD3D(void)
{
swapchain->SetFullscreenState(FALSE, NULL); // switch to windowed mode
// close and release all existing COM objects
swapchain->Release();
backbuffer->Release();
dev->Release();
devcon->Release();
}
程序运行的结果就是一个带鼠标指针的蓝色矩形。