通過win32實現了簡單的遊戲引擎,通過模擬遊戲引擎的加載、圖形渲染以及遊戲退出的操作來理解遊戲引擎的基本原理。
文中分別使用了GDI以及Direct3D來實現,希望對你有所幫助。
// MyEngine.cpp : 定義應用程序的入口點。
//
#include "stdafx.h"
#include <stdio.h>
#include "MyEngine.h"
#include <time.h>
#include <d3d9.h>
#pragma comment(lib,"D3d9.lib")
#define MAX_LOADSTRING 100
// 全局變量:
HINSTANCE hInst; // 當前實例
WCHAR szTitle[] = L"遊戲引擎 - FPS:0"; // 標題欄文本
WCHAR szWindowClass[] = L"MyGameEngine"; // 主窗口類名
HWND g_hMainWnd;
TCHAR g_Key[256] = { 0 }; // 按鍵檢測
DWORD nWidth = 0; // 保存當前窗口的寬
DWORD nHeight = 0; // 保存當前窗口的高
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pDev = NULL;
LPDIRECT3DSURFACE9 g_pBackBuffer = NULL;
LPDIRECT3DSURFACE9 g_pSurface = NULL;
#define KEYDOWN(x) g_Key[x]
DWORD GetRand(DWORD dwBase); // 獲取隨機數
// 使用GDI繪圖測試
DWORD OnGameInit_GDI(); // 模擬遊戲引擎的加載
DWORD OnGameRender_GDI(); // 模擬遊戲進行畫面的渲染
DWORD OnGameOver_GDI(); // 模擬遊戲退出時的清理工作
// 使用Direct3D測試
bool OnGameInit_D3D(); // 模擬遊戲引擎的加載
bool OnGameRender_D3D(); // 模擬遊戲進行畫面的渲染
DWORD OnGameOver_D3D(); // 模擬遊戲退出時的清理工作
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYENGINE));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassExW(&wcex);
hInst = hInstance; // 將實例句柄存儲在全局變量中
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
600, 300, 800, 600, nullptr, nullptr, hInstance, nullptr);
if (!hWnd) return 0;
g_hMainWnd = hWnd;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
//OnGameInit_GDI();
if (!OnGameInit_D3D())return 0;
DWORD dwFPS = 0; // 幀率
DWORD dwLastTime = GetTickCount(); // 上一次渲染時間
DWORD dwNowTime = 0; // 當前渲染時間
DWORD dwLastFPSTime = dwLastTime; // 保存最後次計算FPS的時間
DWORD dwDelta = 0; // 每幀的運行時間
LARGE_INTEGER nLast; // 精確定時
LARGE_INTEGER nNow;
LARGE_INTEGER _animationInterval;
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
_animationInterval.QuadPart = freq.QuadPart*1.0 / 60;
QueryPerformanceCounter(&nLast);
MSG msg = { 0 };
while (TRUE)// 此處 PeekMessage 與 GetMessage
{
if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))// 無論消息隊列中是否有消息,都進行探測
{
if (WM_QUIT == msg.message)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
QueryPerformanceCounter(&nNow);
dwFPS++;
if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart)
{
//OnGameRender_GDI();
OnGameRender_D3D();
nLast.QuadPart = nNow.QuadPart - (nNow.QuadPart % _animationInterval.QuadPart);
if (dwFPS)
{
char szTitle[32];
sprintf(szTitle, "遊戲引擎 - FPS:%d", dwFPS);
SetWindowTextA(g_hMainWnd, szTitle);
dwFPS = 0;
}
}
Sleep(1);
// 使用GetTickCount控制幀率
// OnGameRender_GDI();
// OnGameRender_D3D();
// dwFPS++;
// dwNowTime = GetTickCount();
// dwDelta = dwNowTime - dwLastTime; // 計算本幀的運行時間
// // 控制每一幀在10ms左右
// if (dwDelta < 20)
// {
// Sleep(20 - dwDelta);
// }
//
// if ((dwNowTime - dwLastFPSTime) > 1000)
// {
// char szTitle[32];
// if (dwFPS)
// {
// sprintf(szTitle, "遊戲引擎 - FPS:%d", dwFPS);
// SetWindowTextA(g_hMainWnd, szTitle);
// dwLastFPSTime = dwNowTime;
// dwFPS = 0;
// }
// }
// dwLastTime = dwNowTime; // 保存上一次更新的時間
}
//OnGameOver_GDI();
OnGameOver_D3D();
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
g_Key[wParam] = 1;
break;
case WM_KEYUP:
g_Key[wParam] = 0;
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// 計算並獲取0~dwBase的隨機值
DWORD GetRand(DWORD dwBase)
{
DWORD result = 0;
__asm {
rdtsc // 取CPU執行的指令數
mov result,eax
}
return result % dwBase;
}
DWORD OnGameInit_GDI()
{
RECT rc;
GetWindowRect(g_hMainWnd, &rc);
nWidth = rc.right - rc.left;
nHeight = rc.bottom - rc.top;
return 0;
}
DWORD OnGameRender_GDI()
{
if (KEYDOWN(VK_ESCAPE))
{
SendMessage(g_hMainWnd, WM_CLOSE, 0, 0);
}
HDC hdc = GetDC(g_hMainWnd);
HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
SelectObject(hdc, hPen);
MoveToEx(hdc, GetRand(nWidth), GetRand(nHeight), NULL);
LineTo(hdc, GetRand(nWidth), GetRand(nHeight));
DeleteObject(hPen);
ReleaseDC(g_hMainWnd,hdc);
return 0;
}
DWORD OnGameOver_GDI()
{
return 0;
}
bool OnGameInit_D3D()
{
// 初始化Direct3D
g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (NULL == g_pD3D)
{
MessageBox(g_hMainWnd, _T("Init Direct3D failed!"), _T("Error"), MB_OK | MB_ICONERROR);
return false;
}
RECT rc;
GetWindowRect(g_hMainWnd, &rc);
nWidth = rc.right - rc.left;
nHeight = rc.bottom - rc.top;
D3DDISPLAYMODE displayMode;
if (FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))
return false;
// 設置Direct3D的屬性
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = displayMode.Format;
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferWidth = nWidth;
d3dpp.BackBufferHeight = nHeight;
d3dpp.hDeviceWindow = g_hMainWnd;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
// 按要求建立Direct3D設備
if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hMainWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pDev)))
{
int err1 = E_FAIL;
int err = GetLastError();
MessageBox(g_hMainWnd, _T("CreateDevice failed!"), _T("Error"), MB_OK | MB_ICONERROR);
return false;
}
//D3DERR_DEVICELOST D3DERR_INVALIDCALL D3DERR_NOTAVAILABLE D3DERR_OUTOFVIDEOMEMORY
srand(time(NULL));
// 將背景色設置爲黑色
g_pDev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
// 獲取後臺渲染指針
if (FAILED(g_pDev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &g_pBackBuffer)))
return false;
// 建立離屏表面 (類似GDI雙緩存)
if(FAILED(g_pDev->CreateOffscreenPlainSurface(nWidth,nHeight,displayMode.Format,
D3DPOOL_DEFAULT,&g_pSurface,NULL)))
return false;
return true;
}
bool OnGameRender_D3D()
{
if (KEYDOWN(VK_ESCAPE))
SendMessage(g_hMainWnd, WM_CLOSE, 0, 0);
// 確認Direct3D設備
if (!g_pDev) return false;
// 開始渲染
if (g_pDev->BeginScene())
{
// 將離屏幕表面填充隨機顏色
int r = rand() % 255;
int g = rand() % 255;
int b = rand() % 255;
g_pDev->ColorFill(g_pSurface, NULL, D3DCOLOR_XRGB(r, g, b));
// 畫離屏表面到後臺g_psurface的指定區域
RECT rc;
rc.left = rand() % nWidth / 2;
rc.right = rc.left + rand() % nWidth / 2;
rc.top = rand() % nHeight;
rc.bottom = rc.top + rand() % nHeight / 2;
g_pDev->StretchRect(g_pSurface, NULL, g_pBackBuffer, &rc, D3DTEXF_NONE);
// 定製渲染
g_pDev->EndScene();
// 完成後臺畫面與前臺的交互
g_pDev->Present(NULL, NULL, NULL, NULL);
}
return 0;
}
DWORD OnGameOver_D3D()
{
return 0;
}
源碼VS2015
本文難免有所錯誤,如有問題歡迎留言。