通过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
本文难免有所错误,如有问题欢迎留言。