- 写一个游戏引擎的头文件(.h);
- 写一个游戏引擎的实现文件(.cpp);
- 在编程中调用游戏引擎,制作用方向键控制地球上下左右乱跑的简单动作。
一 、在VS2015中新建一个 Visual C++ 常规空项目ball。
首先,添加游戏引擎的头文件GameEngine.h和实现文件GameEngine.cpp。详细步骤如下:
??二 、游戏引擎的头文件 GameEngine.h 源代码:
#pragma once
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine,
int iCmdShow);
LRESULT CALLBACK WndProc(HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam);
BOOL GameInitialize(HINSTANCE hInstance);
void GameStart(HWND hWindow);
void GameEnd();
void GameActivate(HWND hWindow);
void GameDeactivate(HWND hWindow);
void GamePaint(HDC hDC);
void GameCycle();
void HandleKeys();
class GameEngine
{
protected:
static GameEngine* m_pGameEngine;
HINSTANCE m_hInstance;
HWND m_hWindow;
TCHAR m_szWindowClass[32];
TCHAR m_szTitle[32];
WORD m_wIcon, m_wSmallIcon;
int m_iWidth, m_iHeight;
int m_iFrameDelay;
BOOL m_bSleep;
public:
GameEngine(HINSTANCE hInstance,
LPTSTR szWindowClass,
LPTSTR szTitle,
WORD wIcon,
WORD wSmallIcon,
int iWidth = 640,
int iHeight = 480);
virtual ~GameEngine();
static GameEngine* GetEngine() { return m_pGameEngine; };
BOOL Initialize(int iCmdShow);
LRESULT HandleEvent(HWND hWindow,
UINT msg,
WPARAM wParam,
LPARAM lParam);
void ErrorQuit(LPTSTR szErrorMsg);
HINSTANCE GetInstance() { return m_hInstance; };
HWND GetWindow() { return m_hWindow; };
void SetWindow(HWND hWindow) { m_hWindow = hWindow; };
LPTSTR GetTitle() { return m_szTitle; };
WORD GetIcon() { return m_wIcon; };
WORD GetSmallIcon() { return m_wSmallIcon; };
int GetWidth() { return m_iWidth; };
int GetHeight() { return m_iHeight; };
int GetFrameDelay() { return m_iFrameDelay; };
void SetFrameRate(int iFrameRate) { m_iFrameDelay = 1000 /
iFrameRate; };
BOOL GetSleep() { return m_bSleep; };
void SetSleep(BOOL bSleep) { m_bSleep = bSleep; };
};
三 、游戏引擎实现文件 GameEngine.cpp?源代码:
?
#include "GameEngine.h"
GameEngine *GameEngine::m_pGameEngine = NULL;
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine,
int iCmdShow)
{
MSG msg;
static int iTickTrigger = 0;
int iTickCount;
if (GameInitialize(hInstance))
{
//初始化游戏引擎
if (!GameEngine::GetEngine()->Initialize(iCmdShow))
return FALSE;
//进入主消息循环
while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// 处理消息
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//确保游戏引擎不在休眠状态
if (!GameEngine::GetEngine()->GetSleep())
{
//查看是否已过游戏周期
iTickCount = GetTickCount();
if (iTickCount > iTickTrigger)
{
iTickTrigger = iTickCount +
GameEngine::GetEngine()->GetFrameDelay();
HandleKeys();
GameCycle();
}
}
}
}
return (int)msg.wParam;
}
// 结束游戏
GameEnd();
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
return GameEngine::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam);
}
GameEngine::GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass,
LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth, int iHeight)
{
//设置游戏引擎的成员变量
m_pGameEngine = this;
m_hInstance = hInstance;
m_hWindow = NULL;
if (lstrlen(szWindowClass) > 0)
lstrcpy(m_szWindowClass, szWindowClass);
if (lstrlen(szTitle) > 0)
lstrcpy(m_szTitle, szTitle);
m_wIcon = wIcon;
m_wSmallIcon = wSmallIcon;
m_iWidth = iWidth;
m_iHeight = iHeight;
m_iFrameDelay = 50; // 设定游戏周期, 缺省为20帧 / 秒
m_bSleep = TRUE;
}
GameEngine::~GameEngine()
{
}
BOOL GameEngine::Initialize(int iCmdShow)
{
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = m_hInstance;
wndclass.hIcon = LoadIcon(m_hInstance,
MAKEINTRESOURCE(GetIcon()));
wndclass.hIconSm = LoadIcon(m_hInstance,
MAKEINTRESOURCE(GetSmallIcon()));
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = m_szWindowClass;
if (!RegisterClassEx(&wndclass))
return FALSE;
int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CXFIXEDFRAME) * 2,
iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 +
GetSystemMetrics(SM_CYCAPTION);
if (wndclass.lpszMenuName != NULL)
iWindowHeight += GetSystemMetrics(SM_CYMENU);
int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2,
iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2;
m_hWindow = CreateWindow(m_szWindowClass,
m_szTitle,
WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX,
iXWindowPos,
iYWindowPos,
iWindowWidth,
iWindowHeight,
NULL,
NULL,
m_hInstance,
NULL);
if (!m_hWindow)
return FALSE;
ShowWindow(m_hWindow, iCmdShow);
UpdateWindow(m_hWindow);
return TRUE;
}
LRESULT GameEngine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
// 设置游戏窗口并开始游戏
SetWindow(hWindow);
GameStart(hWindow);
return 0;
case WM_SETFOCUS:
// 激活游戏并更新休眠状态
GameActivate(hWindow);
SetSleep(FALSE);
return 0;
case WM_KILLFOCUS:
// 停用游戏并更新休眠状态
GameDeactivate(hWindow);
SetSleep(TRUE);
return 0;
case WM_PAINT:
HDC hDC;
PAINTSTRUCT ps;
hDC = BeginPaint(hWindow, &ps);
//绘制游戏画面
GamePaint(hDC);
EndPaint(hWindow, &ps);
return 0;
case WM_DESTROY:
GameEnd();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWindow, msg, wParam, lParam);
}
void GameEngine::ErrorQuit(LPTSTR szErrorMsg)
{
MessageBox(GetWindow(), szErrorMsg, TEXT("Critical Error"), MB_OK | MB_ICONERROR);
PostQuitMessage(0);
}
?
四、添加一个头文件Bitmap.h和实现文件Bitmap.cpp,目的是使用后缀为?.bmp位图,代码如下:
//Bitmap.h
#pragma once
#include <windows.h>
class Bitmap
{
protected:
HBITMAP m_hBitmap;
int m_iWidth, m_iHeight;
void Free();
public:
Bitmap();
Bitmap(HDC hDC, LPTSTR szFileName);
Bitmap(HDC hDC, UINT uiResID, HINSTANCE hInstance);
Bitmap(HDC hDC, int iWidth, int iHeight, COLORREF crColor = RGB(0, 0, 0));
virtual ~Bitmap();
BOOL Create(HDC hDC, LPTSTR szFileName);
BOOL Create(HDC hDC, UINT uiResID, HINSTANCE hInstance);
BOOL Create(HDC hDC, int iWidth, int iHeight, COLORREF crColor);
void Draw(HDC hDC, int x, int y, BOOL bTrans = FALSE,
COLORREF crTransColor = RGB(255, 0, 255));
int GetWidth() { return m_iWidth; };
int GetHeight() { return m_iHeight; };
};
//Bitmap.cpp
?
#include "Bitmap.h"
Bitmap::Bitmap()
: m_hBitmap(NULL), m_iWidth(0), m_iHeight(0)
{
}
Bitmap::Bitmap(HDC hDC, LPTSTR szFileName)
: m_hBitmap(NULL), m_iWidth(0), m_iHeight(0)
{
Create(hDC, szFileName);
}
Bitmap::Bitmap(HDC hDC, UINT uiResID, HINSTANCE hInstance)
: m_hBitmap(NULL), m_iWidth(0), m_iHeight(0)
{
Create(hDC, uiResID, hInstance);
}
Bitmap::Bitmap(HDC hDC, int iWidth, int iHeight, COLORREF crColor)
: m_hBitmap(NULL), m_iWidth(0), m_iHeight(0)
{
Create(hDC, iWidth, iHeight, crColor);
}
Bitmap::~Bitmap()
{
Free();
}
void Bitmap::Free()
{
if (m_hBitmap != NULL)
{
DeleteObject(m_hBitmap);
m_hBitmap = NULL;
}
}
BOOL Bitmap::Create(HDC hDC, LPTSTR szFileName)
{
Free();
HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
BITMAPFILEHEADER bmfHeader;
DWORD dwBytesRead;
BOOL bOK = ReadFile(hFile, &bmfHeader, sizeof(BITMAPFILEHEADER),
&dwBytesRead, NULL);
if ((!bOK) || (dwBytesRead != sizeof(BITMAPFILEHEADER)) ||
(bmfHeader.bfType != 0x4D42))
{
CloseHandle(hFile);
return FALSE;
}
BITMAPINFO* pBitmapInfo = (new BITMAPINFO);
if (pBitmapInfo != NULL)
{
bOK = ReadFile(hFile, pBitmapInfo, sizeof(BITMAPINFOHEADER),
&dwBytesRead, NULL);
if ((!bOK) || (dwBytesRead != sizeof(BITMAPINFOHEADER)))
{
CloseHandle(hFile);
Free();
return FALSE;
}
m_iWidth = (int)pBitmapInfo->bmiHeader.biWidth;
m_iHeight = (int)pBitmapInfo->bmiHeader.biHeight;
PBYTE pBitmapBits;
m_hBitmap = CreateDIBSection(hDC, pBitmapInfo, DIB_RGB_COLORS,
(PVOID*)&pBitmapBits, NULL, 0);
if ((m_hBitmap != NULL) && (pBitmapBits != NULL))
{
SetFilePointer(hFile, bmfHeader.bfOffBits, NULL, FILE_BEGIN);
bOK = ReadFile(hFile, pBitmapBits, pBitmapInfo->bmiHeader.biSizeImage,
&dwBytesRead, NULL);
if (bOK)
return TRUE;
}
}
Free();
return FALSE;
}
BOOL Bitmap::Create(HDC hDC, UINT uiResID, HINSTANCE hInstance)
{
Free();
HRSRC hResInfo = FindResource(hInstance, MAKEINTRESOURCE(uiResID), RT_BITMAP);
if (hResInfo == NULL)
return FALSE;
HGLOBAL hMemBitmap = LoadResource(hInstance, hResInfo);
if (hMemBitmap == NULL)
return FALSE;
PBYTE pBitmapImage = (BYTE*)LockResource(hMemBitmap);
if (pBitmapImage == NULL)
{
FreeResource(hMemBitmap);
return FALSE;
}
BITMAPINFO* pBitmapInfo = (BITMAPINFO*)pBitmapImage;
m_iWidth = (int)pBitmapInfo->bmiHeader.biWidth;
m_iHeight = (int)pBitmapInfo->bmiHeader.biHeight;
PBYTE pBitmapBits;
m_hBitmap = CreateDIBSection(hDC, pBitmapInfo, DIB_RGB_COLORS,
(PVOID*)&pBitmapBits, NULL, 0);
if ((m_hBitmap != NULL) && (pBitmapBits != NULL))
{
const PBYTE pTempBits = pBitmapImage + pBitmapInfo->bmiHeader.biSize +
pBitmapInfo->bmiHeader.biClrUsed * sizeof(RGBQUAD);
CopyMemory(pBitmapBits, pTempBits, pBitmapInfo->bmiHeader.biSizeImage);
UnlockResource(hMemBitmap);
FreeResource(hMemBitmap);
return TRUE;
}
UnlockResource(hMemBitmap);
FreeResource(hMemBitmap);
Free();
return FALSE;
}
BOOL Bitmap::Create(HDC hDC, int iWidth, int iHeight, COLORREF crColor)
{
m_hBitmap = CreateCompatibleBitmap(hDC, iWidth, iHeight);
if (m_hBitmap == NULL)
return FALSE;
m_iWidth = iWidth;
m_iHeight = iHeight;
HDC hMemDC = CreateCompatibleDC(hDC);
HBRUSH hBrush = CreateSolidBrush(crColor);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, m_hBitmap);
RECT rcBitmap = { 0, 0, m_iWidth, m_iHeight };
FillRect(hMemDC, &rcBitmap, hBrush);
SelectObject(hMemDC, hOldBitmap);
DeleteDC(hMemDC);
DeleteObject(hBrush);
return TRUE;
}
void Bitmap::Draw(HDC hDC, int x, int y, BOOL bTrans, COLORREF crTransColor)
{
if (m_hBitmap != NULL)
{
HDC hMemDC = CreateCompatibleDC(hDC);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, m_hBitmap);
if (bTrans)
TransparentBlt(hDC, x, y, GetWidth(), GetHeight(), hMemDC, 0, 0,
GetWidth(), GetHeight(), crTransColor);
else
BitBlt(hDC, x, y, GetWidth(), GetHeight(), hMemDC, 0, 0, SRCCOPY);
SelectObject(hMemDC, hOldBitmap);
DeleteDC(hMemDC);
}
}
?
五、检查一下项目文件下的 resource.h 内容跟以下一致:
//resource.h? ? ? /*其实这里只留意一下前面得5,6,7,8行有没有重复就可以了,也可以不理*/
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 ball.rc 使用
//
#define IDB_BACKGROUND 101
#define IDB_SAUCER 102
#define IDI_BALL 103
#define IDI_BALL_SM 104
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 105
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
六、到如下地址下载图标文件等,解压缩到目录ball下:
https://pan.baidu.com/s/1PAHPiDaKvm3msOcbjsRpbg
提取码:xzv5?
备注:要保留文件夹 RES 在 ball 目录下?
七、在项目属性-->连接器-->输入-->附加依赖项:msimg32.lib
八 、调用游戏引擎,添加项目头文件ball.h和实现文件ball.cpp 源码如下:
//ball.h
#pragma once
#include <Windows.h>
#include "resource.h"
#include "GAMEENGINE.h"
#include "BITMAP.H"
HINSTANCE ? ? g_hInstance;
GameEngine * ?g_pGame;
const int ? ? g_iMAXSPEED = 8;
Bitmap ? ? * ?g_pSaucer;
Bitmap ? ? * ?g_pBackground;
int ? ? ? ? ? g_iSaucerX, g_iSaucerY;
int ? ? ? ? ? g_iSpeedX, g_iSpeedY;
//ball.cpp
#include "ball.h"
BOOL GameInitialize(HINSTANCE hInstance)
{
?? ?g_pGame = new GameEngine(hInstance,
TEXT("BALL"),
TEXT("BALL"),
IDI_BALL,
IDI_BALL_SM,
1000,
700);
?? ?if (g_pGame == NULL)
?? ??? ?return FALSE;
?? ?g_pGame->SetFrameRate(30);
?? ?g_hInstance = hInstance;
?? ?return TRUE;
}
void GameStart(HWND hWindow)
{
?? ?HDC hDC = GetDC(hWindow);
?? ?g_pBackground = new Bitmap(hDC, IDB_BACKGROUND, g_hInstance);
?? ?g_pSaucer = new Bitmap(hDC, IDB_SAUCER, g_hInstance);
?? ?g_iSaucerX = 250 - g_pSaucer->GetWidth() / 2;
?? ?g_iSaucerY = 200 - g_pSaucer->GetHeight() / 2;
?? ?g_iSpeedX = 0;
?? ?g_iSpeedY = 0;
}
void GameEnd()
{
?? ?delete g_pBackground;
?? ?delete g_pSaucer;
?? ?delete g_pGame;
}
void GameActivate(HWND hWindow)
{
}
void GameDeactivate(HWND hWindow)
{
}
void GamePaint(HDC hDC)
{
?? ?g_pBackground->Draw(hDC, 0, 0);
?? ?g_pSaucer->Draw(hDC, g_iSaucerX, g_iSaucerY, TRUE);
}
void GameCycle()
{
?? ?g_iSaucerX = min(1000-g_pSaucer->GetWidth(),max(0,g_iSaucerX + g_iSpeedX));
?? ?g_iSaucerY = min(300,max(0,g_iSaucerY + g_iSpeedY));
?? ?InvalidateRect(g_pGame->GetWindow(), NULL, FALSE);
}
void HandleKeys()
{
?? ?if (GetAsyncKeyState(VK_LEFT) < 0)
?? ??? ?g_iSpeedX = max(-g_iMAXSPEED,--g_iSpeedX);
?? ?else if (GetAsyncKeyState(VK_RIGHT) < 0)
?? ??? ?g_iSpeedX = min(g_iMAXSPEED, ++g_iSpeedX);
?? ?else if (GetAsyncKeyState(VK_UP) < 0)
?? ??? ?g_iSpeedY = max(-g_iMAXSPEED, --g_iSpeedY);
?? ?else if (GetAsyncKeyState(VK_DOWN) < 0)
?? ??? ?g_iSpeedY = min(g_iMAXSPEED, ++g_iSpeedY);
}
九、编译后运行画面:按 上 下 左 右键 试试,控制一下地球
|