通向DirectDraw之捷徑
發(fā)表時間:2023-08-22 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]作者:Johnny Watson 譯者:藍(lán)色feel From:www.x-temple.com 這篇文章描述了怎樣通過使用DirectX SDK中的通用庫文件來輕松地建立一個 Direct...
作者:Johnny Watson 譯者:藍(lán)色feel From:www.x-temple.com
這篇文章描述了怎樣通過使用DirectX SDK中的通用庫文件來輕松地建立一個 DirectDraw對象及其顯示表面(surface)。(這篇文章)對那些想要在不破壞原應(yīng)用程序架構(gòu)的情況下快速掌握它來做些事的人特別有幫助,請注意事實上這些類中抽象了相當(dāng)多且復(fù)雜的事物,因此我強烈地推薦你在掌握它們功能的同時,盡量關(guān)注一下其底層的實現(xiàn),這樣有助于你盡快掌握它們的工作方式。
1.DirectDraw的安裝
在本文中,我假定你擁有微軟公司的 Visual C++ , 和 DirectX 8.1 SDK。如果沒有,就快去準(zhǔn)備一份吧。 首先,啟動你的 Visual C++, 創(chuàng)建一個新的 Win32 應(yīng)用程序工程。 然后進(jìn)入 你 DirectX SDK 文件夾中的 multimedia\common\include 目錄 , 拷貝 dxutil.h 和 ddutil.h 至你新建工程的目錄下。 然后將你 DirectX SDK文件夾中的 \multimedia\common\src目錄下的dxutil.cpp 和 ddutil.cpp 也拷貝至你新建工程的目錄下。把四個文件加入你的工程, 然后連接上下列庫文件: dxguid.lib ,ddraw.lib,winmm.lib。現(xiàn)在,你創(chuàng)建一個新的 C++源文件文件, 而且也把它加入你的工程。 這些是整個教程中我們將會用到的工作文件。
2.DirectDraw程序代碼
既然我們已經(jīng)準(zhǔn)備好了, 讓我們開始寫一些代碼什么的實在的東西吧!
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "dxutil.h"
#include "ddutil.h"
這是標(biāo)準(zhǔn)的代碼。#define WIN32_LEAN_AND_MEAN,這句的目的是指示編譯器不要包含與MFC相關(guān)的操作。( 只是一個好的練習(xí)——如果你不在使用 MFC) 然后我們包括 dxutil.h 和 ddutil.h,這是兩個很有用的頭文件。 他們能夠使你以一種比通常的DirectX編程更輕松的方式來工作。
//globals
bool g_bActive = false;
CDisplay *g_pDisplay = NULL;
CSurface *g_pText = NULL;
//function prototypes
bool InitDD(HWND);
void CleanUp();
void GameLoop();
代碼自身說的很清楚,不是嗎?我們的第一個全局變量,g_bActive,是一個讓我們的應(yīng)用程序知道是否應(yīng)該運行游戲的標(biāo)志。 如果我們沒有這個全局變量, 我們的應(yīng)用程序可能在它自己被注銷之後還在嘗試著在我們已經(jīng)創(chuàng)建的 DirectDraw 的表面上畫點兒什么呢!雖然這通常是一個在程序結(jié)束的時候出現(xiàn)的一個不大的問題,但它會導(dǎo)致一個非法操作的錯誤,我們并不想那樣,不是嗎? g_pDisplay 是我們的顯示對象。CDisplay 在 ddutil.h 中是最主要的類。它控制著我們前后的緩沖區(qū),并提供一些針對該緩沖區(qū)的功能,如訪問及存儲緩沖區(qū),將表面寫入緩沖區(qū),創(chuàng)建一個表面,等等。 g_pText 是一個文本表面。 我們將會在這個表面 (你或許已經(jīng)有所理解 ) 之上寫文本, 然后將它傳送到我們的屏幕之上。 注意它們兩者都是指向?qū)ο蟮闹羔? 而且被初始化為NULL。
現(xiàn)在來看看函數(shù)的原型。 InitDD() 只是用來初始化 DirectDraw 。 多虧了 DirectDraw 的通用文件(common files),這還算是一個簡單的程序。( 但是晚些時候我們才會接觸到它們) CleanUp()調(diào)用了對象 g_pDisplay 的析構(gòu)函數(shù)來釋放 ( release)我們所創(chuàng)建的DirectDraw對象及其表面。很明顯, GameLoop() 則是將來用來放你的游戲的地方。
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
InitDD(hWnd);
g_bActive=true;
break;
case WM_CLOSE:
g_bActive=false;
CleanUp();
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_MOVE:
g_pDisplay->UpdateBounds();
break;
case WM_SIZE:
g_pDisplay->UpdateBounds();
break;
default:
return DefWindowProc(hWnd,uMsg,wParam,lParam);
break;
}
return 0;
}
這里是標(biāo)準(zhǔn)的 Windows 程序消息處理機制的代碼。 當(dāng)產(chǎn)生 WM_CREATE 消息時,我們初始化 DirectDraw, 并將我們的全局變量 g_bActive 設(shè)定為true,這樣就可以執(zhí)行游戲循環(huán) GameLoop() 了。當(dāng)消息 WM_CLOSE 被傳遞的時候,我們就把全局變量 g_bActive 設(shè)定為 false ( 這樣我們的應(yīng)用程序就不會在它自己被注銷之後還在嘗試著向我們已經(jīng)創(chuàng)建的 DirectDraw 的表面上寫數(shù)據(jù)了)。然后調(diào)用 CleanUp() 函數(shù),最終注銷我們的窗口程序。處理 WM_MOVE 和 WM_SIZE 事件非常重要,因為若處理不當(dāng),DirectDraw 將不顧窗口本身是否已經(jīng)被移動或重新設(shè)定其大小,仍然在屏幕上原來的位置上繼續(xù)作畫,從而造成錯誤。
bool InitDD(HWND hWnd)
{
//dd init code
g_pDisplay = new CDisplay();
if(FAILED(g_pDisplay->CreateWindowedDisplay(hWnd,640,480)))
{
MessageBox(NULL,"Failed to Initialize DirectDraw",
"DirectDraw Initialization
Failure",MB_OK MB_ICONERROR);
return false;
}
return true;
}
該死的 InitDD() 函數(shù)... 不過別急,它只不過幾行而已! 這兒就是通常那些個庫文件的用武之地。現(xiàn)在那些妨礙我們建立 DirectDraw 對象的冗長的玩意兒已被我們輕松搞定了,而你將會再次注意到它挺麻煩的,不是嗎?如果你真的不想弄明白那些惹人煩的玩意兒,那么至少你得知道個梗概吧!如果你得回去改變協(xié)作等級什么的,它也許幫得上忙。注意到這是一個返回 bool 值的函數(shù), 因此如果你不厭倦的話, 你應(yīng)當(dāng)做一下錯誤檢查。(基于你所可以理解的篇幅問題之原因,我決定在這篇文章中省略)
void CleanUp()
{
SAFE_DELETE(g_pDisplay);
}
真是夠簡單的! 這個函數(shù)調(diào)用了在 dxutil.h 中定義的宏 SAFE_DELETE 來刪除我們的顯示對象,同時調(diào)用析構(gòu)函數(shù)。
void MainLoop()
{
g_pDisplay->CreateSurfaceFromText(&g_pText,NULL,"DDraw using Common Files",
RGB(0,0,0),RGB(0,255,0));
g_pDisplay->Clear(0);
g_pDisplay->Blt(0,0,g_pText,0);
g_pDisplay->Present();
g_pText->Destroy();
}
這兒就是你將來放游戲的地方。 為了要給你一個表面對象(surface object)如何工作的例子,我們已經(jīng)做了一個簡單的文本表面而且寫了一些文本上去。注意我們在最後釋放(destroy)了全局指針變量 g_pText,否則每次循環(huán)它都將被重新創(chuàng)建一次,直到吃光最后一點兒內(nèi)存。
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int iShowCmd)
{
WNDCLASSEX wc;
HWND hWnd;
MSG lpMsg;
wc.cbClsExtra=0;
wc.cbSize=sizeof(WNDCLASSEX);
wc.cbWndExtra=0;
wc.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wc.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
wc.hInstance=hInstance;
wc.lpfnWndProc=WndProc;
wc.lpszClassName="wc";
wc.lpszMenuName=0;
wc.style=CS_HREDRAW CS_VREDRAW;
if(!RegisterClassEx(&wc))
{
MessageBox(NULL,"Couldn't Register Window Class",
"Window Class Registration
Failure",MB_OK MB_ICONERROR);
return 0;
}
hWnd = CreateWindowEx(NULL,"wc","DirectDraw Common Files in Action",
WS_POPUPWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,640,480,0,0,
hInstance,0);
if(hWnd == NULL)
{
MessageBox(NULL,"Failed to Create Window","Window Creation
Failure", MB_OK MB_ICONERROR);
return 0;
}
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
while(lpMsg.message != WM_QUIT)
{
if(PeekMessage(&lpMsg,0,0,0,PM_REMOVE))
{
TranslateMessage(&lpMsg);
DispatchMessage(&lpMsg);
}
else if(g_bActive)
{
MainLoop();
}
}
return lpMsg.wParam;
}
這就是我們的應(yīng)用程序中最長的函數(shù)——WinMain()。像往常一樣, 我們創(chuàng)建一個窗口類,然后再創(chuàng)建一個窗口,顯示及更新它, 并且進(jìn)入消息循環(huán)?瓷先ブ餮h(huán)不同於平常是因為我們不想要主游戲進(jìn)程被消息的處理所干擾。我們通過觀察全局指針變量 g_bActive 的狀態(tài) 來判斷該時刻調(diào)用游戲循環(huán)是否安全以及在循環(huán)中向屏幕上傳送(blit)圖象,而最后全部結(jié)束的時候我們返回一個長指針 lpMsg.wParam.( 至于為什么我實在是不敢肯定,可其它每個 Win32 應(yīng)用程序都是這樣做的,對,就是這樣)
實在是太簡單了,嗯!?我們只用了 135 行程序編碼就已經(jīng)將對象寫到了屏幕上。自由地去進(jìn)一步探究這些類的結(jié)構(gòu),做一些載入位圖至表面一類的實驗等等。這是使用 DDraw的一個很棒的捷徑。它能在不犧牲控制 (你總是能在你需要的時候回去編輯那些類) 和性能的情況下而使事情變得容易些。有一件事得要注意的是在計算機上如果我使用了這個結(jié)構(gòu)卻不繪制任何對象到屏幕上, 應(yīng)用程序?qū)i定。( 對,這就是我為什麼把文本輸出也包括在這里) 如果我說做游戲很大程度上就是把對象傳送( blit )到屏幕上,大概不會引起什么爭議(除非又有一些新的藝術(shù)風(fēng)格誕生了)。