1. 程式人生 > >遊戲程式設計入門(4):繪製圖形影象

遊戲程式設計入門(4):繪製圖形影象

除了經典的向量圖形遊戲之外,遊戲開發人員都使用圖形影象來在視覺上展示遊戲的圖形部分。本文將介紹如何載入和顯示圖形影象。

本章內容包括:

  • 點陣圖影象的基礎知識以及為什麼它們在遊戲程式設計中如此重要
  • 點陣圖影象的內部工作方式
  • 如何開發在遊戲中使用的通用點陣圖類
  • 如何在一個幻燈片放映程式中使用點陣圖類來表示幻燈片影象

點陣圖影象的基礎知識

Windows遊戲中的影象是用點陣圖(bitmap)表示的,點陣圖是一些矩形圖形物件,包含一些小方塊(稱為畫素)構成的行和列。

點陣圖的得名是因為行和列決定了怎樣將畫素對映(map)到螢幕上,而畫素本身是由位(bit)構成的。

點陣圖中的每一個畫素都是一種純色,因此可以將點陣圖看成是小彩色方塊的一種矩形排列形式。

在Windows中支援兩種點陣圖,裝置相關點陣圖和裝置無關點陣圖。裝置相關點陣圖是以某種特定裝置決定的方式儲存的,而裝置無關點陣圖的儲存方式使得能夠在任何裝置上顯示它。本文及後使用的點陣圖都是裝置無關點陣圖(DIB:device-independent bitmap)。

在Windows程式中使用點陣圖

要想在Windows程式中使用點陣圖,有兩種選擇:

  • 直接從檔案中讀取點陣圖
  • 將點陣圖儲存為一個應用程式資源並從記憶體中讀取它

直接從檔案中讀取點陣圖

Windows程式開啟磁碟中的點陣圖檔案並讀入影象資料,然後使用點陣圖影象資料來建立一個GDI 點陣圖圖形物件,可以在裝置環境上繪製這個圖形物件。

將點陣圖儲存為一個應用程式資源並從記憶體中讀取它

將點陣圖儲存為可執行程式內部的一個資源,意味著一旦編譯了程式,就不需要在程式中包括點陣圖檔案了。這種做法的優點是能夠將遊戲釋出為一個單獨的程式檔案。

深入學習點陣圖

要想在遊戲中使用點陣圖,必須對它們的結構有一個基本的瞭解。

點陣圖的結構

點陣圖的結構如圖所示:

這裡寫圖片描述

這幅圖解釋了每一個位圖都包含3個基本部分:

  • 頭部
  • 顏色表
  • 影象資料

頭部

頭部包含屬於點陣圖的整體結構的資訊,例如其寬度、高度、每畫素的位數等。

顏色表

顏色表包含點陣圖的調色盤,它是整個影象中使用的顏色列表。

顏色表對於8位點陣圖極其重要,因為它最多描述了影象中的畫素使用的256種顏色。相反,24影象的顏色完全可以由畫素本身進行描述,所以它們不需要顏色表。更具體的來說,24點陣圖像中的24位被分為3個8位值,分別對應每一個顏色部分—-紅色、綠色和藍色。

影象資料

影象資料是儲存點陣圖的實際畫素的地方。

例如,如果一個位圖是10*12,那麼橫向是10個畫素,縱向是12個畫素,一共是120個畫素。如果它是一個8點陣圖像,那麼每個畫素都需要8位(1個位元組)來描述其顏色。而對於24點陣圖像,每個畫素需要24位。

因此,對於10*12點陣圖的8位版本,影象資料包含120個位元組,而24位版本佔用360個位元組。

當然,所有這些影象資料都是在GDI 函式載入影象並開始使用它之後自動處理的。換句話說,只有第一次從檔案或資源中載入點陣圖時才需要關注點陣圖的內部工作方式。

一旦完成了載入,就可以使用一個位圖控制代碼來將其繪製到裝置環境中。

開發點陣圖類 Bitmap

建立一個類,包含載入和繪製點陣圖所需的全部程式碼,然後使用這個類來建立點陣圖物件。

點陣圖類 Bitmap 的工作原理

Bitmap 類的思路是:提供一種從檔案或者資源載入點陣圖並將點陣圖繪製到一個裝置環境的方法。通過將這些功能結合到一個類中,我們就能在遊戲中創建極易使用的Bitmap 物件,並且可以隱藏處理點陣圖的各種煩雜工作。

Bitmap 類有以下需求

  • 從檔案中載入點陣圖
  • 從資源中載入點陣圖
  • 建立純色的空白點陣圖
  • 將點陣圖繪製到裝置環境中
  • 獲得點陣圖的寬度和高度

Bitmap 類 原始碼

Bitmap類的設計和之前的遊戲引擎GameEngine設計類似,分為Bitmap.h 和 Bitmap.cpp。

Bitmap.h

#pragma once

//-----------------------------------------------------------------
// 包含的檔案
//-----------------------------------------------------------------
#include <windows.h>

//-----------------------------------------------------------------
// Bitmap 類
//-----------------------------------------------------------------
class Bitmap
{
protected:
  // 成員變數
  HBITMAP m_hBitmap;           //點陣圖控制代碼
  int     m_iWidth, m_iHeight; //點陣圖的寬和高

  // 幫助器方法,用來釋放與點陣圖有關的記憶體並清除點陣圖控制代碼
  void Free();

public:
  // 建構函式和解構函式  3個建構函式分別對應一種建立點陣圖的不同方法
  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();

  // 常規方法 create()用來處理載入點陣圖資料並將其建立為一個GDI 物件,3個Create分別對應3個建構函式
  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);

  //Getter方法 獲得點陣圖的寬和高
  int  GetWidth() 
  {
      return m_iWidth; 
  };
  int  GetHeight() 
  {
      return m_iHeight; 
  };
};

Bitmap.cpp

//-----------------------------------------------------------------
// 包含的檔案
//-----------------------------------------------------------------
#include "Bitmap.h"

//-----------------------------------------------------------------
// Bitmap 的建構函式和解構函式
//-----------------------------------------------------------------

// 預設的建構函式初始化成員變數
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();
}

//-----------------------------------------------------------------
// Bitmap 幫助器方法,用來釋放與點陣圖有關的記憶體並清除點陣圖控制代碼
//-----------------------------------------------------------------
void Bitmap::Free()
{
  // 若點陣圖控制代碼有效(即存在)
  if (m_hBitmap != NULL)
  {
    //刪除GDI 點陣圖影象並清除控制代碼
    DeleteObject(m_hBitmap);
    m_hBitmap = NULL;
  }
}

//-----------------------------------------------------------------
// Bitmap 常規方法,3個Create()和Draw()
//-----------------------------------------------------------------

//從一個檔案中載入點陣圖並將其建立為一個GDI 物件
BOOL Bitmap::Create(HDC hDC, LPTSTR szFileName)
{
  // 清空以前的任何點陣圖資訊(使用於對不同的點陣圖重複使用同一個Bitmap物件的情況)
  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;


    /* 計算biSizeImage填充回去,是增加的程式碼
  (因為無壓縮BMP檔案的pBitmapInfo->bmiHeader.biSizeImage 裡面的值不一定是影象的真實大小,
    可能是0或者隨意的值。所以需要重新計算
    */
    pBitmapInfo->bmiHeader.biSizeImage =
    m_iHeight*m_iWidth*((int)pBitmapInfo->bmiHeader.biBitCount)/8;

    // 複製影象資料,呼叫CreateDIBSection()以從原始點陣圖資料中獲得一個GDI 點陣圖物件的控制代碼
    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;
}

//從一個資源中載入點陣圖並將其建立為一個GDI 物件
BOOL Bitmap::Create(HDC hDC, UINT uiResID, HINSTANCE hInstance)
{
  // Free any previous DIB info
  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;

 /* 計算biSizeImage填充回去,是增加的程式碼
  (因為無壓縮BMP檔案的pBitmapInfo->bmiHeader.biSizeImage 裡面的值不一定是影象的真實大小,
    可能是0或者隨意的值。所以需要重新計算
 */
 pBitmapInfo->bmiHeader.biSizeImage =
   m_iHeight*m_iWidth*((int)pBitmapInfo->bmiHeader.biBitCount)/8;

  // 複製影象資料,並以此為基礎使用CreateDIBSection獲得一個位圖控制代碼
  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;
}

//建立純色的空白點陣圖並將其建立為一個GDI 物件
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)
{
    //確保點陣圖控制代碼有效
  if (m_hBitmap != NULL)
  {
    // 建立一個相容的裝置環境來臨時儲存點陣圖
    HDC hMemDC = CreateCompatibleDC(hDC);

    // 將點陣圖選入裝置環境中
    HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, m_hBitmap);

    // 將源裝置環境的一個影象繪製到目的裝置環境上的指定位置
    BitBlt(hDC, x, y, GetWidth(), GetHeight(), hMemDC, 0, 0, SRCCOPY);

    // 清理臨時裝置環境
    SelectObject(hMemDC, hOldBitmap);
    DeleteDC(hMemDC);
  }
}

Bitmap 類 程式碼解析

類定義

Win32 定義了幾個與點陣圖有關的資料結構,因此我們在Bitmap 類程式碼中經常會看到幾個不同的資料結構,其中一個結構是BITMAPINFOHEADER,它儲存了與點陣圖有關的頭部資訊。在從檔案或資源中讀取點陣圖時,就將其頭部儲存在一個BITMAPINFOHEADER 結構中。

需要注意的是,我們的Bitmap 類只是設計用來載入BMP檔案格式的24位未壓縮點陣圖影象。

建構函式和解構函式

所有Bitmap( )建構函式都非常簡單,只是呼叫相應的Create( )函式,執行根據一個檔案、一個資源或一種純色來建立點陣圖的具體工作。

建構函式的思路是,直接呼叫Create( )方法,利用資料載入Bitmap物件。

解構函式呼叫Free( )方法,釋放與點陣圖有關的記憶體並清除點陣圖控制代碼。

Free( )方法首先檢視點陣圖控制代碼m_hBitmap是否有效,在這種情況下,它刪除GDI 點陣圖影象並清除控制代碼。這是釋放與點陣圖有關的記憶體所需的全部操作。

建立點陣圖的三種方法

第一個Create( )方法負責從一個檔案中載入點陣圖。

這個方法首先呼叫Free( ),以確保清除了以前的任何點陣圖資料,這適用於對不同的點陣圖重複使用同一個Bitmap 物件的情況。然後開啟檔案,檢查得到的檔案控制代碼以確保順利打開了檔案而沒有出現錯誤。然後從檔案中讀取點陣圖的檔案頭,執行一些檢查以確保正確讀取而沒有出現錯誤。檔案頭包含點陣圖檔案本身的資訊,而資訊頭部包含點陣圖的資訊。接下來讀取資訊頭部,並執行另一個錯誤檢查。

從檔案中正確讀取頭部之後,Create( )方法就可以開始讀取點陣圖影象資料的實際工作了。需要說明的是CreateDIBSection( ) Win32函式的用法。

CreateDIBSection()
功能:從原始點陣圖資料中獲得一個GDI 點陣圖物件的控制代碼。

函式原型

HBITMAP CreateDIBSection(HDC hdc,CONST BITMAPINFO *pbmi,UINT iUsage,VOID** ppvBits,HANDLE hSection,DWORD dwOffset);

函式引數

hdc:裝置環境控制代碼。如果iUsage的值是DIB_PAL_COLORS,那麼函式使用該裝置環境的邏輯調色盤對與裝置無關點陣圖的顏色進行初始化。

pbmi:指向BITMAPINFO結構的指標,該結構指定了與裝置無關點陣圖的各種屬性,其中包括點陣圖的維數和顏色。

iUsage:指定由pbmi引數指定的BITMAPINFO結構中的成員bmiColors陣列包含的資料型別(要麼是邏輯調色盤索引值,要麼是原文的RGB值)。下列值是系統定義的,其含義為:

DIB_PAL_COLORS:表示成員bmiColors是hdc指定的裝置環境的邏輯調色盤,使用的是16位索引值陣列。

DIB_RGB_COLORS:表示結構BITMAPINFO中包含了RGB值的陣列。

ppvBits:指向一個變數的指標,該變數接收一個指向DIB位資料值的指標。

hSection:檔案對映物件的控制代碼。函式將使用該物件來建立DIB(與裝置無關點陣圖)。該引數可以是NULL。

如果hSection不是NULL,那麼它一定是檔案對映物件的控制代碼。該物件是通過呼叫帶有PAGE_READWRITE或PAGE_WRITECOPY標誌的CreateFileMapping函式建立的。不支援只讀的DIB型別。通過其他方法建立的控制代碼將會引起CreateDIBSection函式執行失敗。

如果hSection不是NULL,那麼函式CreateDIBSection將在hSection引用的檔案對映物件中偏移量為dwOffset處獲取點陣圖的位資料值。應用程式可以在以後通過呼叫GetObject函式來檢索hSection控制代碼,其中GetObject函式使用了GreateDIBSection函式返回的GBITMAP型別的物件。

如果hSection為NULL,那麼系統將為與裝置無關點陣圖(DIB)分配記憶體。在這種情況下,函式CreateDIBSection將忽略引數dwOffset,應用程式無法在以後獲取指向記憶體的控制代碼。通過呼叫GetObject函式來填充的DIBSECTION結構成員dshSection也將成為NULL。

DwOffset:指定從hSection引用的檔案對映物件開始處算起的偏移量,超過這個偏移量的地方就是點陣圖的位資料值開始存放的地方。在hSection為NULL時忽略該值。點陣圖的位資料值是以DWORD為單位計算的。

返回值:如果函式執行成功,那麼返回值是一個指向剛剛建立的與裝置無關點陣圖的控制代碼,並且*ppvBits指向該點陣圖的位資料值;如果函式執行失敗,那麼返回值為NULL,並且*ppvBit也為NULL,若想獲得更多錯誤資訊,請呼叫GetLastError函式。

第二個Create( )方法用來從一個資源中載入點陣圖

這個方法大致遵循與第一個Create( )方法相同的模式,只是在這裡,點陣圖資訊時從記憶體中的一個資源中檢索的,而不是從檔案中讀取的。

第一步找到點陣圖資源,將資源載入到記憶體中,然後鎖定資源,以便訪問其原始資料。接下來儲存點陣圖的寬度和高度,複製影象資料,並以此為基礎使用CreateDIBSection( )獲得一個位圖控制代碼。最後,在發生錯誤時執行一些清理工作。

最後一個Create( )方法建立純色的空白點陣圖。

這個方法與另外兩個Create( )方法有很大的不同,因為它不涉及任何現有的點陣圖資料,而使用一個名為CreateCompatibleBitmap( )的Win32 函式,根據提供的裝置環境建立一個全新的點陣圖。

這個Create( ) 方法中的大多數工作都涉及使用一種純色來填充點陣圖,首先建立一個相容的裝置環境以包含要繪製的點陣圖,然後建立一個指定顏色的純色畫刷。接下來,將點陣圖選入裝置環境並使用純色畫刷進行填充。之後清理圖形物件。

繪製點陣圖

Draw( )方法也涉及繪製點陣圖,因此它實際上與第三個Create( )方法相似。不過在這裡,是將點陣圖繪製到一個外部的裝置環境中,而不是在點陣圖自身上進行繪製。

Draw( )方法接受一個裝置環境和一個xy座標作為引數。繪製點陣圖的第一步是確保點陣圖控制代碼有效。如果控制代碼檢查正常,則建立一個相容的裝置環境來臨時儲存點陣圖,將點陣圖選入裝置環境。因為繪製圖像總是從一個裝置環境到另一個裝置環境。

點陣圖實際上是使用Win32 BitBlt( )函式繪製的,這個函式將源裝置環境中的一個影象繪製到目標裝置環境上的指定位置

繪製點陣圖影象有時候稱為 位塊傳輸( blitting ),在繪製圖像時就是在塊傳輸影象位。

開發Slideshow 幻燈片 示例

Slideshow 目錄結構和效果圖

Slideshow 目錄結構:

這裡寫圖片描述

Slideshow 效果圖:

這裡寫圖片描述

這裡寫圖片描述

Slideshow 原始碼

Resource.h

//-----------------------------------------------------------------
// Slideshow Resource Identifiers
// C++ Header - Resource.h
//-----------------------------------------------------------------

//-----------------------------------------------------------------
// 圖示                    Range : 1000 - 1999
//-----------------------------------------------------------------
#define IDI_SLIDESHOW       1000
#define IDI_SLIDESHOW_SM    1001

//-----------------------------------------------------------------
// 點陣圖                  Range : 2000 - 2999
//-----------------------------------------------------------------
#define IDB_IMAGE1          2000
#define IDB_IMAGE2          2001
#define IDB_IMAGE3          2002
#define IDB_IMAGE4          2003
#define IDB_IMAGE5          2004

Slideshow.h

#pragma once

//-----------------------------------------------------------------
// 包含的檔案
//-----------------------------------------------------------------
#include <windows.h>
#include "Resource.h"
#include "GameEngine.h"
#include "Bitmap.h"

//-----------------------------------------------------------------
// 全域性變數
//-----------------------------------------------------------------
HINSTANCE   g_hInstance;       //程式例項控制代碼
GameEngine* g_pGame;           //遊戲引擎指標
const int   g_iNUMSLIDES = 6;  //更改幻燈片的張數
Bitmap*     g_pSlides[g_iNUMSLIDES]; //儲存Bitmap物件的指標
int         g_iCurSlide;       //當前幻燈片在陣列中的索引

Slideshow.cpp

//-----------------------------------------------------------------
// 包含的檔案
//-----------------------------------------------------------------
#include "Slideshow.h"

//-----------------------------------------------------------------
// 遊戲事件函式
//-----------------------------------------------------------------

//初始化遊戲
BOOL GameInitialize(HINSTANCE hInstance)
{
  // 建立遊戲引擎
  g_pGame = new GameEngine(hInstance, TEXT("Slideshow"),
    TEXT("Slideshow"), IDI_SLIDESHOW, IDI_SLIDESHOW_SM);
  if (g_pGame == NULL)
    return FALSE;

  // 設定幀率
  g_pGame->SetFrameRate(1);

  // 設定例項控制代碼
  g_hInstance = hInstance;

  return TRUE;
}

//開始遊戲
void GameStart(HWND hWindow)
{
  // 建立並載入幻燈片點陣圖  這段程式碼闡明建立點陣圖的三種方法
  HDC hDC = GetDC(hWindow);
  g_pSlides[0] = new Bitmap(hDC, TEXT("Image1.bmp"));
  g_pSlides[1] = new Bitmap(hDC, TEXT("Image2.bmp"));
  g_pSlides[2] = new Bitmap(hDC, TEXT("Image3.bmp"));
  g_pSlides[3] = new Bitmap(hDC, IDB_IMAGE4, g_hInstance);
  g_pSlides[4] = new Bitmap(hDC, IDB_IMAGE5, g_hInstance);
  g_pSlides[5] = new Bitmap(hDC, 640, 480, RGB(0, 0, 255));

  // 設定第一個幻燈片
  g_iCurSlide = 0;
}

//遊戲結束
void GameEnd()
{
  // 清理幻燈片點陣圖
  for (int i = 0; i < g_iNUMSLIDES; i++)
    delete g_pSlides[i];

  // 清除遊戲引擎
  delete g_pGame;
}

void GameActivate(HWND hWindow)
{
}

void GameDeactivate(HWND hWindow)
{
}

//繪製當前的幻燈片點陣圖
void GamePaint(HDC hDC)
{
  // 繪製當前幻燈片點陣圖
  g_pSlides[g_iCurSlide]->Draw(hDC, 0, 0);
}

//遊戲迴圈 逐個顯示幻燈片,幾秒一個
void GameCycle()
{
  static int iDelay = 0;

  // 設定轉到下一個幻燈片之前的3秒延遲
  if (++iDelay > 3)
  {
    // 恢復延遲計數器
    iDelay = 0;

    // 顯示下一張幻燈片
    if (++g_iCurSlide == g_iNUMSLIDES)
      g_iCurSlide = 0;

    // 強制重新繪製,以便繪製下一個幻燈片
    InvalidateRect(g_pGame->GetWindow(), NULL, FALSE);
  }
}

Slideshow 原始碼解析

GameCycle( ) 函式

GameCycle( )函式負責在一次短暫的停頓之後移動到下一張幻燈片。

因為遊戲引擎限制了最小幀頻為1幀/秒,所以必須在GameCycle( )函式中進一步實現延遲,從而放慢幻燈片的放映。

對於觀看每一張幻燈片來說,3秒鐘是一個合理的延遲,因此GameCycle( )函式使用了一個靜態變數iDelay來延遲各張幻燈片,在進入下一張幻燈片之前等待3秒鐘。能夠這樣做是因為幀頻設定為每秒1個週期,可以在第3個週期(即每3秒)才更新幻燈片。

為了移動到下一張幻燈片,需要增加iCurSlide。如果增加iCorSlide時超過了最後一張幻燈片,將從第一張幻燈片重新開始放映幻燈片。

原始碼 下載