大家對DirectX一定不陌生吧,因為在微軟剛剛推出WINDOWS視窗作業系統時,由於顯示介面採用統一的GDI,禁止程式員直接操作硬體,這使得在WIN 3.x系統上的遊戲程式速度奇慢,根本無法推廣。微軟為瞭解決這一問題,曾經又推出過WING圖形加速程式,但是由於WING缺乏廣大遊戲廠商的支援,也沒有普及開來。所以我們當時玩的大部分遊戲都是運行於DOS環境之下。
直到1995年,伴隨著WINDOWS 95的誕生,微軟公司正式公布了其新一代的遊戲開發系統DirectX。DirectX以其高效的效能,統一的程式介面,使得其一推出便受到了各大遊戲廠家的喜愛,並紛紛表示支援它,至此WINDOWS系統下的遊戲開發時代才真正開始。
DirectX編程,將涉及到 WIN 95 和 C++ 的一些基本知識,所以在此之前您最好有這方面的基礎,如果您不是很熟悉他們的話也不要緊,完全可以隨著本文介紹一起學習,可謂一箭雙鵰。
好了,閑話少說開始進入正題:
(一)DirectX由以下幾個部分組成:
1、DirectDraw:通過直接存取顯示記憶體和軟硬體加速技術,實現快速直接存取。
2、DirectSound:提供軟硬體聲音混合和錄音再生功能。
3、DirectPlay :提供多人遊戲的互動功能,讓您輕鬆實現網上互連。
4、Direct3D:互動三維圖形技術。
5、DirectInput:使你的程式能夠控制輸入裝置如滑鼠,鍵盤,和遊戲杆等。
6、DirectSetup:完成DirectX驅動程式的安裝。
7、AutoPlay:只要您把光碟一放入光碟機它便會自動運行。
最後補充一句,我們講到和用到的都是DirectX 5的東西,所以需要一套DirectX 5 SDK,你可以在時下一些光碟片中找找,它是免費的。
(二)正式編程之前的準備工作
要讓VC++ 5能正確的編譯、連結你的程式,你必須先在Microsoft Developer Studio中進行如下設定,以使得編譯器能夠找到需要的連結庫和包含檔案。
首先,開啟一個新的project workspace,在File菜單中,選擇New建立一個新的Win32 Application取名為‘MyDirectX1’,這時在workspace視窗中就會出現了一個新的檔案夾。工程建立好後,在Project菜單中選擇Add to Project/Files向新的工程中加入程式(這步在後面介紹)。
然後,設定編譯時間所需包含檔案的路徑。在Tools菜單中,選擇Options,彈出Options對話方塊,選中Directories ,在Show Directories For列表框中選擇Include files,雙擊列表框底部的空白行,輸入C:DX5SDKSDKINC 和 C:DX5SDKSDKSAMPLESMISC;接著再在Show Directories For列表框選擇Library files,雙擊其底部的空白行,輸入C:DX5SDKSDKLIB(我們假定DirectX 5 SDK安裝在C:DX5SDK )。
最後,設定連結時所需的庫檔案。開啟在Project菜單中Settings/Link,在Category下拉框中選擇General,然後在Object/Library模組列表框加入Ddraw.lib和Winmm.lib即可。
(三)我們的第一個DirectX程式
我們打算以DirectX SDK所帶的例子程式‘DDEX1’作為講解的基礎,因為這樣做起碼有以下幾個好處:(1)大家手裡都有正確的來源程式,當你辛辛苦苦輸完一段程式後,但在編譯時間不幸出現錯誤,你就不會不知所措了,可以對照來源程式。(2)我也不用再把所有的代碼都搬到紙上,這樣我們可以更詳細的介紹DirectX的重點內容。(3)可以培養你讀別人程式的能力,以後學習起來就會更輕鬆。
既然如此,還等什麼呢?還記得我們已經建立了一個新的工程‘MyDirectX1’嗎?現在開啟Project/Add to Project/Files,瀏覽目錄 DXSDKSDKSAMPLESDDEX1,並選擇該目錄下的所有檔案,點擊‘OK’就把它們加入到‘MyDirectX1’中了。好了,現在就開啟‘DDEX1.CPP’看看吧。
1、在使用 DirextDraw之前,首先需要建立一個對象DirectDraw的實體,該對象實體代表微機的顯示適配器。然後,才能使用介面所提供的方法來操作該對象實體,使之完成有關命令和任務。
所以程式的開頭先聲明了一個DirectDraw對象:
LPDIRECTDRAW lpDD;
然後如下建立該對象 :
HRESULT ddrval;
ddrval = DirectDrawCreate( NULL, &lpDD, NULL );
if( ddrval == DD_OK )
{
// DirectDraw 對象lpDD建立成功
}
else
{
// DirectDraw對象不能被建立
}
2、接下來設定協作層的運行方式。即DirectDraw程式的運行方式,指整屏模式、視窗模式、MODEX模式等。
HRESULT ddrval;
ddrval = lpDD->SetCooperativeLevel( hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
if( ddrval == DD_OK ){
// 全屏獨佔方式設定成功
}
else {
// 設定失敗
}
3、設定顯示模式,即螢幕解析度和色彩數。
HRESULT ddrval;
ddrval = lpDD->SetDisplayMode( 640, 480, 8 );
if( ddrval == DD_OK ) {
// 成功的把螢幕設定成了640X480X256色
}
else {
// 顯示模式不能改變
}
4、建立表面。我們可以把表面看成是一塊記憶體緩衝區,所有的操作都是對緩衝區進行的,操作完成後只須把整個緩衝區翻轉成主表面(顯存)即完成了快速寫屏。利用這種技術,就可以實現平滑無閃爍的動畫效果,下面講解如何建立表面。
建立可切換式表面(Surface)的第一步是:在DDSURFACEDESC結構中設定表面(Surface)的各項參數,請看下面的程式:
// Create the primary surface with 1 back buffer.
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
DDSCAPS_FLIP | DDSCAPS_COMPLEX;
ddsd,dwBackBufferCount = 1;
在以上幾行中,DDSURFACEDESC結構的大小被賦給dwSize成員。這樣做可防止你在調用DirectDraw的方法時返回一個無效值,且便於今後DDSURFACEDESC結構的擴充。
成員dwFlags用於標明DDSURFACEDESC結構中哪些地區填入的資訊有效,哪些地區的資訊無效。程式中,我們用dwFlags表明了你要使用結構DDSCAPS(DDSC_CAPS),以及你要建立一個背景緩衝區(DDSD_BACKBUFFERCOUNT)。常式中成員dwCaps包含了一些用在DDSCAPS結構中的標誌。這樣一來,成員dwCaps就定義了一個主表面(Surface)(DDSCAPS_PRIMARYSURFACE),一個彈出式表面(Surface)(DDS-CPAS_PRIMARYSURFACE),和一個複表面(Surface)(DDSCAPS_COMPLEX)。所謂複表面(Surface)是指,該表面(Surface)是由若干子表面(Surface)組成的。最後,上面的常式定義了一個背景緩衝區。這個背景緩衝區是我們真正進行操作的記憶體區。操作完成後,再把它們從背景緩衝區彈到主表面(Surface)上就行了。在這裡,背景緩衝區的個數被設為1。但你可以設定任意多的背景緩衝區,只要你的記憶體允許的話。
填完了DDSURFACEDESC結構,就可以使用該結構和lpDD建立表面的指標了:
ddrval = lpDD->CreateSurface( &ddsd, &lpDDSPrimary, NULL );
if( ddrval == DD_OK ) {
// lpDDSPrimary 指向主表面
}
else{
return FALSE;
}
如果調用成功,則IDirectDraw::CreateSurface函數返回指向主表面的指標lpDDSPrimary 。然後調用 IDirectDrawSurface::GetAttachedSurface 取得背景緩衝區的指標:
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
ddrval = lpDDSPrimary->GetAttachedSurface( &ddcaps, &lpDDSBack );
if( ddrval == DD_OK ) {
// lpDDSBack 指向背景緩衝區
}
else{
return FALSE;
}
如果IDirectDrawSurface::GetAttachedSurface調用成功的話,lpDDSBack 就指向背景緩衝區了。現在我們就可以對背景緩衝區進行各種的操作,把想顯示的內容寫進去。比如遊戲,我們就先調入背景圖象,然後再調入各種角色當前應顯示的圖象,最後是‘對白’、‘文字’等。全部完成之後,把背景緩衝區和主表面進行翻轉,就把你想要的東西顯示出來了。
5、翻轉表面
while( 1 ) {
HRESULT ddrval;
ddrval = lpDDSPrimary->Flip( NULL, 0 );
if( ddrval == DD_OK )
{
break; file://翻轉完成,退出迴圈
}
file://如果表面丟失了
if( ddrval == DDERR_SURFACELOST )
{
ddral = LpDDSPrimary->Restore(); file://恢複表面
if( ddral != DD_OK )
{
break;//恢複成功,退出迴圈
}
}
file://如果前一次表面翻轉還未發生
if( ddrval != DDERR_WASSTILLDRAWING )
{
break;
}
}
例中,lpDDSPrimary指明了主表面及其背景緩衝區。調用IDirectDrawSurface::Flip後,主表面和背景緩衝區交換。如果調用成功,返回DD_OK,程式終止While迴圈;如果返回DDERR_SURFACELOST,表明可能是表面丟失,需要用IDirectDrawSurface::Restore 恢複該表面,若恢複成功,就再一次調用IDirectDrawSurface::Flip方法;如果失敗,程式終止While迴圈並返回一個錯誤值。另外,即使IDirectDrawSurface::Flip調用成功,交換也不是立即完成,它將等到系統中在此之前的表面交換都完成後才進行。如果前一次的表面翻轉還未發生,IDirectDrawSurface::Flip 就返回DDERR_WASSTILLDRAWING。IDirectDrawSurface::Flip將繼續迴圈直到返回DD_OK。
6、結束程式之前別忘了用Release()釋放你建立的對象。
if( lpDD != NULL )
{
if( lpDDSPrimary != NULL ) {
lpDDSPrimary->Release();
lpDDSPrimary = NULL;
}
lpDD->Release();
lpDD = NULL;
OK!大功告成。運用以上的手段我們就可以作出基本的DirectX程式了,不過不要高興的太早,DirectX遠比你想象的要龐大的多!我們所涉及的不過是一點皮毛而已(僅包括了DirectDraw),要想全面掌握DirectX編程的技巧,你還須付出艱辛的努力。我的建議是多上機,多看書,多讀讀別人的程式,只有這樣才能快速提高自己的水平。
好了,今天我們就先聊到這裡,希望這能對你有所協助,