问题描述
拥有如此简单的 DirectX 应用程序:
#include <d3dx9.h>
#include <tchar.h>
#include <chrono>
#include "Utils.h"
//#pragma comment (lib,"d3d9.lib")
//#pragma comment (lib,"d3dx9.lib")
using namespace std::chrono_literals;
using namespace std::chrono;
const wchar_t g_szClassName[] = _T("myWindowClass");
// we use a fixed timestep of 1 / (60 fps) = 16 milliseconds
//constexpr std::chrono::nanoseconds timestep(16ms);
constexpr std::chrono::nanoseconds timestep(1000ms);
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND,UINT,WParaM,LParaM);
wchar_t msgbuf[100];
int loop_cnt = 0;
bool handle_events() {
// poll for events
return false; // true if the user wants to quit the game
}
void update(long long lag) {
// update game logic here
swprintf_s(msgbuf,_T("update - loop_cnt: %d\n"),loop_cnt++);
OutputDebugString(msgbuf);
}
void render() {
// render stuff here
}
VOID OnPaint(HDC hdc) {
}
// ------------ D3D ------------
IDirect3D9* pD3D;
IDirect3DDevice9* pDev;
IDirect3DVertexBuffer9* pVB;
const DWORD OURVERT_FVF = D3DFVF_XYZRHW | D3DFVF_DIFFUSE;
struct OurVertex {
float x,y,z; // pozycja
float rhw; // komponent rhw
D3DCOLOR color; // kolor
};
// -----------------------------
int WINAPI WinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nCmdshow) {
using clock = std::chrono::high_resolution_clock;
WNDCLASSEX wc;
HWND hWnd;
MSG Msg = {0};
std::chrono::nanoseconds lag(0ns);
auto start_time = clock::Now();
bool quit_game = false;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground = (HBrush)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
MessageBox(NULL,_T("Window Registration Failed!"),_T("Error!"),MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hWnd = CreateWindowEx(
WS_EX_CLIENTEDGE,g_szClassName,_T("The title of my window"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,480,// initial x size
640,// initial y size
NULL,NULL,hInstance,NULL
);
OutputDebugString(_T("--> WinMain <-- \n"));
if (hWnd == NULL) {
MessageBox(NULL,_T("Window Creation Failed!"),MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hWnd,nCmdshow);
UpdateWindow(hWnd);
// ------------ D3D ------------
pD3D = Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_ParaMETERS d3dpp;
ZeroMemory(&d3dpp,sizeof(d3dpp));
d3dpp.SwapEffect = D3DSWAPEFFECT_disCARD;
d3dpp.Windowed = true;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
OurVertex verts[] = {
{ 20.0f,20.0f,0.5f,1.0f,0xffff0000,},{ 100.0f,0xff00ff00,{ 20.0f,100.0f,0xff00ff55,0xff0000ff},};
pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,D3DCREATE_HARDWARE_VERTEXPROCESSING,&d3dpp,&pDev);
HRESULT hr = pDev->CreateVertexBuffer(4*sizeof(OurVertex),D3DUSAGE_DYNAMIC,OURVERT_FVF,D3DPOOL_DEFAULT,&pVB,0);
check_HRESULT(_T("CreateVertexBuffer"),hr);
void* data;
pVB->Lock(0,4*sizeof(OurVertex),&data,D3DLOCK_disCARD);
memcpy(data,(void*)verts,sizeof(verts));
pVB->Unlock();
// -----------------------------
// Step 3: The Message Loop
while (TRUE) {
while (PeekMessage(&Msg,PM_REMOVE) > 0) {
TranslateMessage(&Msg);
dispatchMessage(&Msg);
}
if (Msg.message == WM_QUIT)
break;
// Run game logic
auto current_time = clock::Now();
auto frame_time = current_time - start_time;
start_time = current_time;
lag += duration_cast<nanoseconds>(frame_time);
// update game logic as lag permits
while (lag >= timestep) {
lag -= timestep;
pDev->Clear(0,D3DCLEAR_TARGET,0xff000000,1,0);
pDev->BeginScene();
pDev->SetFVF(OURVERT_FVF);
pDev->SetStreamSource(0,pVB,sizeof(OurVertex));
pDev->DrawPrimitive(D3DPT_TRIANGLESTRIP,2);
pDev->EndScene();
pDev->Present(0,0);
update(lag.count()); // update at a fixed rate each time
}
}
OutputDebugString(_T("--> AFTER MSG LOOP END <-- \n"));
return Msg.wParam;
}
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WParaM wParam,LParaM lParam) {
switch (msg) {
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}
当我运行调试(没有定义任何刹车点)我得到输出:
--> WinMain <--
update - loop_cnt: 0
update - loop_cnt: 1
update - loop_cnt: 2
update - loop_cnt: 3
update - loop_cnt: 4
update - loop_cnt: 5
...
程序按预期成功显示了一个彩色矩形。但是,这里出了点问题,因为当我单击应用程序窗口右上角的标准“X”关闭按钮时,窗口关闭但程序似乎没有正确结束。我仍然在 Visual Studio 的“输出”窗口中收到 update - loop_cnt: #
消息,并且没有 --> AFTER MSG LOOP END <--
消息建议正确终止程序。
更重要的是,当我再次尝试运行调试时,出现错误:
LINK : Fatal error LNK1168: cannot open ...Game_Loop_Windows_1.exe for writing
...
MSB6006: "link.exe" exited with code 1168.
1>Done building project "Game_Loop_Windows_1.vcxproj" -- Failed
代码有什么问题?
附注。我在 Windows 10 上使用 Microsoft Visual Studio Community 2019。
解决方法
消息循环可能永远不会终止。
while (PeekMessage(&Msg,NULL,PM_REMOVE) > 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
if (Msg.message == WM_QUIT)
break;
您正在处理所有消息,但只检查最后一个消息是否为 WM_QUIT
消息。相反,您需要检查所有消息并正确退出循环,如下所示:
bool running{true};
while (running) {
while (running && PeekMessage(&Msg,PM_REMOVE) > 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
running = Msg.message != WM_QUIT;
}
// Not strictly required; you can run another render loop and have
// the outermost loop exit when running is false
if (!running)
break;
链接错误仅表示进程仍在运行。无法写入映射到进程的可执行映像。这就是链接器所抱怨的。