C++ Builder是一個可視化的C++編程環境,它為編程人員提供了一種方便高效、簡便的C++語言開發工具,因此已為廣大C++程序員所青睐,DirectX開發工具包是微軟公司提供的一套Windows9X下開發高性能圖形、聲音、輸入輸出和網絡游戲的接口,其高效的直接硬件訪問、程序與硬件設備之間的相對獨立等特性,幾乎使得DirectX成為唯一可以在Windows操作系統下開發游戲程序的基本工具軟件。
雖然C++ Builder中直接包含了一套DirectX3的開發包,也提供了一些例程,但是這些例程沒有系統化的說明,同時也不適合DirectX5以上的程序開發。另一方面,幾乎所有關於DirectX開發的書籍和文獻均使用Visual C++語言描述,所以作者將自己在C++ Builder中的一些開發經驗介紹給讀者。
本文適合具備C++ Builder面向對象編程經驗的讀者,實例開發環境為中文Windows98、C++ Builder4、DirectX6或以上的開發工具包。
一.DirectX簡介
1_1.DirectX的特性
1_2.DirectX是一種Windows環境下標准的高性能游戲、多媒體開發工具包,使用DirectX開發的程序能夠與操作系統默契地配合成為“真正”的桌面應用程序;可以利用硬件廠商提供的驅動程序接口,充分最佳的設備性能;通過直接底層硬件操作,實現最快速、短延時、設備無關的底層接口。
1_3.DirectX采用了組件對象模型(COM)標准,因此對於不同對象的版本可以有不同的接口,這使得用DirectX開發的程序在未來將得到完全兼容和支持的保證。
1_4.DirectX的結構
DirectX需要以設備無關的方法提供設備相關的性能,所以DirectX的結構是由兩個驅動程序構成:硬件抽象層(HAL)和硬件模擬層(HEL),當Direct對象創建時,會同時建立一張“兼容表”,其中記錄了當前硬件系統支持的功能,當DirectX需要實現某個功能時就查詢該表,得到硬件對功能的支持信息,如果功能能夠得到硬件支持,則向HAL發出求,以得到硬件的支持,否則向HEL發出請求,以模擬方式實現功能。
1_5.DirectX的主要組成
(1)DirectDraw:直接訪問圖形硬件,管理用於顯示的內存(顯示內存和系統內存),提供高速圖形和頁面切換動畫;
(2)Direct3D:提供3D硬件接口;
(3)DirectInput:主要支持輸入服務,同時支持輸出設備;
(4)DirectSound:提供3D聲音效果,管理聲卡內存;
(5)DirectPlay:提供網絡多人游戲的通訊、組織功能;
(6)DirectSetup:自動安裝DirectX驅動程序。
本文將按照DirectX的上述組成,分別以實例介紹其在C++ Builder中的實現方法。
二.DirectDraw程序設計
2.1 DirectDraw實現的基礎—顯示方式的設置
設計圖形程序首先遇到的問題是將屏幕設置成一種合適的圖形顯示方式,然後把圖形繪畫到屏幕頁面上。如果您在DOS下開發圖形應用程序,這將是很簡單的事,只要調用DOS的中斷服務程序即可實現,但是使用用DirectDraw就比較復雜。由於DirectDraw的設計目標是提供設備無關的編程接口和高效、多功能的硬件訪問支持,所以DirectDraw需要考慮更多的問題。
DirectDraw在Windows環境下支持兩種圖形方式:全屏幕獨占方式和窗口方式。這裡我先以全屏幕獨占方式,介紹DirectDraw設置屏幕顯示方式步驟,函數具體使用格式和編程方法將在2.2中介紹:
2.1.1 選擇硬件設備
計算機系統不一定只有一個DirectDraw硬件抽象設備,例如,一台計算機可能有兩台或更多的顯示器,那麼DirectDraw對象與哪個HAL對應呢?我們可以使用DirectDrawEnumerate函數來枚舉系統所有已安裝的設備,以供選擇,並返回設備的唯一標識GUID。DirectDraw默認主顯示設備的GUID為NULL;
2.1.2 創建DirectDraw對象
由於DirectX是使用面向對象的程序設計技術,因此,使用DirectDraw編程就首先要創建DirectDraw對象。使用DirectDrawCreate函數及將第一步獲得的設備GUID作為參數可以創建基於所選設備的DirectDraw對象;
2.1.3 獲取DirectDraw更高版本的COM接口
如果您不打算使用DirectX5以上版本提供的功能則可以跳過本步驟,否則必須使用新創建DirectDraw對象的QueryIntrface方法來獲得IDirectDraw2或更高的COM接口。在2.2例中將介紹如何獲得DirectX5以上版本提供的IDirectDraw2接口;
2.1.4 設置協作級別
協作方式可以控制程序與系統其他應用程序之間的交互關系,典型的例子是:設置為全屏獨占方式還是窗口普通方式。設置協作級別可以用DirectDraw對象的SetCooperativeLevel方法;
2.1.5 枚舉設備支持的各種顯示方式,選擇並設置合適的顯示分辯率、色彩深度和刷新頻率等。
使用DirectDraw的EnumDisplayModes方法可以枚舉設備支持的所有圖形方式供用戶選擇,在某些已確定圖形顯示方式的應用程序中可以通過此枚舉功能來檢查系統設備是否支持指定的圖形顯示方式。
使用DirectDraw的SetDisplayMode方法可以設置所需要的圖形顯示方式。
2.2 用DirectDraw設置屏幕圖形顯示方式的實例
現在我們開始編寫第一個示例程序“設置全屏幕獨占圖形顯示方式的程序”dx1,首先我們將在這裡討論C++ Builder中進行DirectX編程的有關問題,然後再詳細介紹實現程序每一步驟的相關技術。
2.2.1 dx1程序運行過程介紹
dx1是根據DirectDraw設置屏幕圖形方式的過程設計的,運行界面如圖2.1所示。在窗口右邊有六個功能按鈕,它們按照屏幕圖形顯示方式設置實現的步驟自上而下排列,程序開始運行時,除第一個按鈕“顯示設備的枚舉”是可用的,其它按鈕均不可用。由於用戶必需按照固定的步驟操作,所以,當一個按鈕任務完成後,dx1程序會將下一步任務的
圖2.1 dx1 屏幕顯示方式設置程序運行界面
按鈕設為可用。
按下“顯示設備枚舉”按鈕後,窗口左上方“運行狀態”對應的文本框中將顯示任務完成情況,若成功則顯示“Enumerate devices OK!”,否則顯示“Enumerate devices failed!” ,同時在狀態組中的設備枚舉下拉框中可以看到枚舉的設備(一般系統只有一個”主顯示設備—Display);確定設備枚舉選擇為“主顯示設備”後,可以進行“創建DirectDraw對象”、“獲得COM的IDIRECT2接口”、“設置協作級別”,每個步驟的運行狀態都會顯示在“運行狀態”右邊的文本框中;在執行了“DDraw2顯示模式的枚舉”後,狀態組下方“顯示模式DDraw2”下拉框中將列出所有顯示設備支持的顯示方式,選擇需要的圖形顯示方式,再按“設置DDraw2”的顯示方式,屏幕就會立刻切換為指定的顯示方式。
2.2.2 dx1編程實現
啟動C++ Builder後在窗口Form1中設計如圖2.1的操作界面,各對象相關屬性設置如表2.1:
控件對象類型 控件對象名稱 相關屬性 屬性值
TForm Form1 Caption DirectX 練習程序1
TLabel Label1 Caption 運行狀態:
TLabel Label2 Caption 設備的枚舉
Tlabel Label3 Caption 顯示模式DDraw2
TEdit Edit1 Text (空)
ReadOnly true
TGroupBox GroupBox1 Caption 狀態
TCombBox ComboBox1 Text (空)
TCombBox ComboBox2 Text (空)
TGroupBox GroupBox2 Caption 協作級別
TCheckBox CheckBox1 Caption DDSCL_ALLOWMODEX
TCheckBox CheckBox2 Caption DDSCL_ALLOWREBOOT
TCheckBox CheckBox3 Caption DDSCL_EXCLUSIVE
Checked true
TCheckBox CheckBox4 Caption DDSCL_FULLSCREEN
Checked true
TCheckBox CheckBox5 Caption DDSCL_NORMAL
TCheckBox CheckBox6 Caption DDSCL_NOWINDOWCHANGES
Checked true
TButton Button1 Caption 設備的枚舉
TButton Button2 Caption 創建DirectDraw對象
Enabled false
TButton Button3 Caption 獲得COM的IDIRECT2接口
Enabled false
TButton Button4 Caption 設置協作級別
Enabled false
TButton Button5 Caption DDraw2顯示模式的枚舉
Enabled false
TButton Button6 Caption 設置DDraw2的顯示方式
Enabled false
表2.1 dx1控件對象屬性設置一覽表
確定已安裝了DirectX5以上的SDK,且在C++Builder中已經將Project/Options/中標簽頁“Directories/Conditionals”上的“Include Path”和“Library Path”添加了DirectX5或以上SDK的路徑。
在窗口模塊中包含 #include "ddraw.h" 頭文件。
現在可以開始編寫代碼了,我們按照按鈕的順序逐個實現每個步驟的任務。
2.2.2.1 設備的枚舉
DirectDraw提供了一個函數DirectDrawEnumerate 來實現設備枚舉功能,此函數的調用格式為:
HRESULT DirectDrawEnumerate (LPDDENUMCALLBACK lpcallback LPVOID lpContext)
(1)參數lpcallback是一個回調函數的地址指針。所謂回調函數是程序員自己編寫的函數,當枚舉函數每枚舉一個設備時就調用這個回調函數一次,並把當前枚舉的設備有關信息通過參數傳遞給回調函數處理。
在dx1程序中,回調函數命名為EnumDeviceCallBack,負責將每次枚舉出的設備的描述和名稱加入到ComboBox1的列表中去,並把設備標識地址指針保存到一個lpDevices數組中以便創建DirectDraw對象時使用。
(2)參數lpContext是一用戶定義的上下文變量,我們僅取值NULL就可以了。
(3)DirectDrawEnumerate為回調函數指針,該函數的格式為:
BOOL WINAPI EnumDeviceCallBack (GUID FAR *lpGUID,
LPSTR lpDevice,
LPSTR lpDeviceName,
LPVOID lpContex)
其中:參數lpGUID 為當前枚舉設備標識地址指針;參數lpDevice 為設備描述的地址指針;參數lpDeviceName 為設備名稱的地址指針;參數 lpContext 為上下文變量地址指針,這裡我們不使用它。在dx1程序中,此回調函數負責將所枚舉設備的名稱和描述顯示在ComboBox1中,並將設備標識地址保存到lpDevices數組中。為了簡化程序,這裡的lpDevices數組只采用了靜態數組,最多允許保存10個元素。後面在保存顯示模式枚舉信息時使用的DisplayModes數組也是為了簡化程序,在實際編程時可以考慮動態分配。 需要說明的是,回調函數應該是一個獨立的函數,不要把它們聲明為窗口類的成員函數(否則回調不能進行),而只要聲明為普通函數就可以了。
2.2.2.2 創建DirectDraw對象
在進行DirectDraw編程之前,必需首先用DirectDrawCreate函數創建DirectDraw對象,並獲得該對象的入口指針。該函數格式如下:
HRESULT DirectDrawCreate(GUID FAR *lpDD,
LPDIRECTDRAW FAR &lplpDD,
Iunknown FAR *p)
(1)參數lpDD為指定的設備標識指針(為NULL時是主設備),在dx示例程序中的第一個按鈕“設備的枚舉”采用靜態數組保存枚舉設備的標識指針,在第二個按鈕創建DirectDraw對象時,可根據用戶在ComboBox1中的選擇,提供一個設備標識指針,以便對該設備創建DirectDraw對象。
(2)參數 lplpDD 為對象創建成功後的獲得IditrctDraw接口指針。
(3)參數p未使用,直接為NULL。
2.2.2.3 獲得COM的IDIRECT2接口
由於DirectX采用了對象組件模型COM技術(這裡不再介紹),因此如果我們需要使用DirectX5或以上版本提供的功能,就需要獲得更高的IDirectDraw接口,例如:如果我們使用DirectX5開發包,就需要獲得IDirectDraw2接口。
可以利用HRESULT IdirectDraw::QueryInterface方法獲得高版本接口:
lpDD->QueryInterface(IID_IDirectDraw2,
(LPVOID *)LPDIRECTDRAW2 &lpDD2)
參數IID_IDirectDraw2是一個常量。
lpDD2是為獲得的IdDrectDraw2接口指針。
如果您需要使用更高版本的開發包,也可以用此方法獲得IDirectDraw3或更高的接口。高版本接口獲得後,就可以釋放低版本的接口了,方法是:lpDD->Release();
2.2.2.4 設置協作級別
玩過DirectX游戲的朋友可能都領略過“全屏獨占”和“窗口”兩種圖形模式,這就可以稱為不同的協作級別,它控制應用程序與系統及其它應用程序的交互程度,因此我們必需用HRESULT IDIRECTDRAW2::SetCooperateLevel方法設置應用程序的協作級別。
LpDD2->SetCooperateLevel(HWND handle,DWORD dwFlag)
(1)參數handle是當前應用程序窗口的句柄,在C++Builder中,TForm類的Handle屬性就是當前應用程序窗口的句柄。
(2)參數dwFlag是協作標志,可以為表2.2中定義之一或多個之和。
序號 標志 說明
(1) DDSCL_ALLOWMODEX 允許使用Mode X模式。必須與(3)、(4)組合使用
(2) DDSCL_ALLOWREBOOT 當使用(3)、(4)時允許用戶進行熱啟動
(3) DDSCL_EXCLUSIVE 使用獨占方式,與(4)一起使用
(4) DDSCL_FULLSCREEN 全屏方式,與(3)一起使用
(5) DDSCL_NORMAL 以普通應用程序窗口方式運行
(6) DDSCL_NOWINDOWCHANGES DirectDraw不能自動最小化或恢復窗口
表2.2 dwFlag標志定義
在我們的dx1示例程序中,默認設置為:
DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN|DDSCL_NOWINDOWCHANGES
2.2.2.5 DDraw2顯示模式的枚舉
利用HRESULT IDIRECTDRAW2::EnumDisplayModes方法及其相應的回調函數可以列出系統顯示設備所支持的顯示方式。您開發的應用程序可以判定當前運行的計算機是否支持所需的顯示方式,也可以提供用戶選擇顯示方式的功能。
lpDD2->EnumDisplayModes(DWORD dwFlag,
LPDDSURFACEDESC lpDDSurfaceDesc,
LPVOID lpContext,
LPDDENUMMODESCALLBACK EnumDisplayModesCallBack
(1)參數dwFlag是標志參數,可以為DDEDM_REFRESHRATES(枚舉不同刷新頻率的刷新模式)和DDEDM_STANDARDVGAMODES(枚舉模式中包含Mode13)兩者之一或之和,在dx1程序中采用了後者。
(2)參數lpDDSurfaceDesc是一個過濾顯示模式的結構,只要設置為NULL就可以獲得全部顯示模式的枚舉,否則只獲得滿足指定模式的枚舉。
LPDDSURFACEDESC是一個結構,其中包含了顯示模式有關信息,主要有:
屏幕點陣 DWORD dwWidth、DWORD dwHeight;
色彩深度 DWORD ddpfPixelFormat.dwRGBBitCount
屏幕刷新頻率 DWORD dwRefreshRate
(3)參數lpContex為用戶上下文變量,設為NULL就可以了。
(4)參數EnumDisplayModesCallBack為回調函數指針,該回調函數規定有如下參數格式:
BOOL WINAPI EnumDisplayModesCallBack(LPDDSURFACEDESC lpDDSurfaceDesc
LPVOID lpContext)
其中:lpDDSurfaceDesc為當前所枚舉顯示模式的信息,lpContext為用戶上下文變量。
在dx1程序中,此回調函數負責將每次回調的顯示模式信息顯示到ComboBox2中,並記錄到一個結構數組DisplayModes中。
2.2.2.6 設置DDraw2的顯示方式
每當用戶選擇了一個顯示模式並點擊“設置DDraw2的顯示方式”按鈕後,dx1將使用RESULT IdirectDraw2::SetDisplayMode方法改變顯示方式。
lpDD2->SetDisplayModes(DWORD dwWidth,
DWORD dwHeight,
DWORD dwRGBCount,
DWORD dwRefreshRate,
DWORD dwFlags)
(1)參數dwWidth和dwHeght為顯示指定方式的點陣。
(2)參數dwRGBCount為顏色深度,如:8(256色)、16(16位色)、24(真彩色)。
(3)參數dwRefreshRate為刷新頻率,不關心時可以設置為0。
(4)參數dwFlags為使用DDSDM_STANDARDVGAMODE來設置Mode13。在dx1程序中始終設為0。
需要注意的是在IdirectDraw接口中,設置顯示模式方法不支持dwRefreshRate和dwFlags這兩個參數,所以,在有些資料中(包括C++Builder4的示例)均只介紹了DirectX3支持的IDirectDraw::SetDisplayMode(dwWidth,dwHeight,dwRGBCount)方法,請讀者注意它們的區別。
2.2.2.7 退出dx1程序需要做的事
不要忘記釋放lpDD2接口。LpDD2->Release();