C++制作游戏引擎之一 方向键控制地球上下左右乱跑

该博客介绍了如何在VS2015中创建一个C++游戏引擎,包括游戏引擎的头文件和实现文件的编写,以及如何调用游戏引擎使地球在屏幕上根据方向键移动。博客还涉及了位图类的创建,资源文件的使用,并提供了编译和运行后的效果。
摘要由CSDN通过智能技术生成

  1. 写一个游戏引擎的头文件(.h);
  2. 写一个游戏引擎的实现文件(.cpp);
  3. 在编程中调用游戏引擎,制作用方向键控制地球上下左右乱跑的简单动作。

一 、在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);

}

九、编译后运行画面:按 上 下 左 右键 试试,控制一下地球

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

watsonbu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值