-------------------------------------------------------
游戲編輯器框架教程
無心柳
-------------------------------------------------------
一、前言:
我一直在尋找DX編程和MFC類庫的結(jié)合點(diǎn),前段時間曾經(jīng)在第二人生http//webking.com.cn/secondlife上放了一個從《計算機(jī)編程技巧與雜志》來的例子。應(yīng)該說,那個例子非常不錯,唯一的缺點(diǎn)就是它只研究了全屏幕顯示的情況,而對于地圖編輯器人員來說,迫切需要的是既能夠完全使用原有MFC的框架又能夠使用DX的功能的東西,這也是我一直在尋找的,今天(1999/12/4)總算找到了。
促使我找到這個辦法的是為MUD編寫的那個地圖編輯器,雖然功能上沒有什么欠缺,但誠如美工所言,界面太差了,按鈕按下去的時候沒有一個響應(yīng)動作,而且也不方便使用MFC的全部功能。在完成這個地圖編輯器以后,其他程序員的MFC編輯器也在趕制之中,而他們的圖形顯示部分是一個難題。如果要使用經(jīng)典編程方法去操作和顯示256色以上的圖片,在程序上是非常麻煩的。能否將 MFC與DDRAW的窗口模式完美地融合 ?在完成地圖編輯器以后,我已經(jīng)有了這樣的設(shè)想,就等著拿到機(jī)器上去測試了。結(jié)果一試之下,完全可行,而且它是如此地 簡單 !
二、第一個例子:
在這個例子中,我用VC的AppWizard生成了一個標(biāo)準(zhǔn)的單文檔窗口并保存。然后,在視類CGameEditView的頭文件中加入了四個DX成員,一個DDRAW指針,一個DDRAW4指針,一個前緩沖,一個后緩沖,具體可以看源碼。在頭文件中,另外加入了兩個函數(shù),一個是Msg函數(shù),用于顯示消息框,這是從別人的例子中直接復(fù)制過來的;另一個是InitDDrawSurface()函數(shù)。然后在CGameEditView.cpp里加入了這兩個函數(shù)的函數(shù)體。Msg沒什么說的,你直接復(fù)制上去用就行了。InitDDrawSurface你也可以直接復(fù)制,這個函數(shù)里創(chuàng)建了一個DDRAW對象,再利用它獲取了DDRAW4的接口。然后用DDRAW4接口創(chuàng)建一個DDSURFACE4主屏幕緩沖頁,它負(fù)責(zé)在屏幕上顯示內(nèi)容。最后創(chuàng)建一個DDSURFACE4的后臺緩沖頁,它負(fù)責(zé)在主緩沖在屏幕上顯示之前在后臺準(zhǔn)備一下畫面。僅此兩個函數(shù)而已。
程序中有兩個動作,一個是在預(yù)創(chuàng)建窗口時執(zhí)行InitDDrawSurface()對DDRAW頁面進(jìn)行初始化,如果不成功程序就退出。另一個動作就是在視類的OnDraw()中將一塊矩形區(qū)域從后臺緩沖復(fù)制到前緩沖(也就是在屏幕上顯示出來)。由于后臺緩沖沒有放入數(shù)據(jù),所以顯示的內(nèi)容將是隨機(jī)的,一般是黑色的。顯示的步驟可以看源碼上的程序注釋,我一般寫得比較細(xì)。
為什么不加入什么撞球等的變化動作?這是因?yàn)槲抑幌敫嬖V大家一個最簡單的DDRAW窗口頁面如何創(chuàng)建和使用,下面的教程中會慢慢加入如何讀入位圖塊和顯示,時間控制,窗口區(qū)域檢測,剪切塊,顏色鍵等內(nèi)容。當(dāng)教程結(jié)束的時候,用MFC和DDRAW做一個簡單游戲應(yīng)該沒有問題了。
下載源碼
GameEdit1.zip
三、第二個例子:
1、上個例子中有個明顯的漏洞,在InitDDrawSurface()中創(chuàng)建后臺緩沖的時候,顯式地指定了寬和高。在前緩沖lpMainSurface創(chuàng)建時,沒有指定大小,因?yàn)椴恍枰熬彌_指的就是整個屏幕的范圍,它使用的是顯示內(nèi)存。而后緩沖是不顯示的內(nèi)容,它實(shí)際是在系統(tǒng)內(nèi)存中(窗口模式不能在顯示內(nèi)存中再創(chuàng)建后臺頁面)創(chuàng)建的一個頁面,一般情況下,應(yīng)該設(shè)置得和前緩沖一樣大或者更大。1024*768的顯式指定對一般顯示器可能夠了,但萬一用戶使用更大的分辨率就可能導(dǎo)致錯誤。所以第二個例子中首先修改了InitDDrawSurface()中創(chuàng)建后臺緩沖的部分,用標(biāo)準(zhǔn)WINDOWS函數(shù)GetDeivceCaps獲取了當(dāng)前顯示分辯率的高和寬(當(dāng)然也可以獲得顏色位數(shù),源碼中有)。然后用獲取的高和寬來創(chuàng)建后臺緩沖,這樣就保證了與分辨率的大小相同。當(dāng)然這也不是絕對保險,萬一用戶運(yùn)行中修改了分辨率仍有可能導(dǎo)致錯誤,但一般我們不需要為那種情況負(fù)責(zé)了,可以不去考慮。
2、這次我們要讀入一個圖片,看起來挺恐怖,你好象需要去研究BITMAP的文件結(jié)構(gòu)。其實(shí)不需要那么復(fù)雜,對一般的游戲和編輯器來說,根本不用去了解那些細(xì)節(jié)的問題,在DX的例程中多次用到的兩個文件可以直接搬過來用,它們是ddutil.h和ddutil.cpp,你可以在大部分DX的例程中找到它們,然后復(fù)制到你程序的文件夾下,再在工程中加入這兩個文件,試試重編譯。直接重編譯是無法成功的,這是因?yàn)镸FC程序有點(diǎn)特殊性,要使它能被MFC所用,必須在上面提到的這兩個文件首部都追加一句#include "stdafx.h"。用過這個語句,MFC就認(rèn)這兩個文件,然后可以正常編譯通過。當(dāng)然,如果你想在VIEW類中使用里的函數(shù),還要在GameEditView.cpp的頭部加入#include "ddutil.h"。這是常識,說這些好象有點(diǎn)多余,在源碼中這幾個步驟都做了而且有說明。
3、通過第2步,已經(jīng)可以隨時讀入BMP文件了,具體的讀法是非常簡單的,因?yàn)槟阋呀?jīng)可以使用ddutil.cpp里的函數(shù)。如果需要,你也可以去修改這個文件的源碼或者制作修改版。作為例子,我先在GameEditView.h中增加了一個DDSURFACE4的頁面指針:
LPDIRECTDRAWSURFACE4 lpBitMapBuffer;//DirectDraw讀入的位圖頁面保存在這里
再在GameEditView.cpp的預(yù)創(chuàng)建窗口PreCreateWindow()函數(shù)中將一幅位圖讀入這個位圖頁面。讀入成功后,將這個臨時頁面的內(nèi)容復(fù)制到后臺緩沖。這樣,后臺緩沖就有了內(nèi)容。再編譯一次程序,會發(fā)現(xiàn)原來窗口左上角那個黑塊已經(jīng)變成這幅圖片(一幅UO中截下來的騎馬圖),代碼如下:
lpBitMapBuffer=DDLoadBitmap(lpDD4,"test.bmp",200,200);
if(NULL==lpBitMapBuffer)//如果沒有讀入成功,退出程序
{
Msg("無法裝入位圖,演示無法進(jìn)行");
exit(-2);
}
//讀入成功就復(fù)制到后臺緩沖去
lpBackBuffer->BltFast(0,0,lpBitMapBuffer,&(CRect(0,0,200,200)),FALSE);
你可以看到代碼中指定的位圖文件名是test.bmp,所以你也需要一個這樣文件名的位圖文件,在下面的源碼包里附有一個,如果你想換一個文件也可以,不過這個文件以后將用于演示動畫的例子。
第二個例子完,需要的話請下載源碼 GameEdit2.zip
三、與GDI結(jié)合:
上面的兩個例子的源碼中沒有包含資源文件,所以在使用的時候請先按照上面提到的方法建立自己的簡單MFC單文檔程序,再將源碼文件替換你自己的文件再編譯。
在第二個例子中,讀入了一幅位圖并顯示在窗口的左上角。這在程序運(yùn)行中沒有任何問題,而且速度還相當(dāng)快。但如果你同時運(yùn)行了其他程序,或者將窗口移動到屏幕的邊緣位置,會出現(xiàn)一些不正常現(xiàn)象。如果將窗口移動到屏幕邊緣,在從后臺到前臺進(jìn)行傳送的時候,會發(fā)生非常矩形錯誤,意思是指你試圖將頁面?zhèn)魉偷狡聊粎^(qū)域外,也就是前緩沖以外(前面說過,前緩沖與屏幕大小是一樣的)。其結(jié)果并不會使你的程序當(dāng)機(jī),只是這次傳送不會進(jìn)行。另外,當(dāng)你用其他程序的窗口蓋住本程序窗口并移動上面的窗口的時候,本窗口的刷新可能導(dǎo)致將位圖蓋在其他窗口區(qū)域內(nèi)。這是個嚴(yán)重的問題,在編寫全屏幕程序的時候不會發(fā)生這種情況,因?yàn)槠聊槐荒愕某绦颡?dú)占了,而在編寫窗口程序的時候,你的程序就會顯示非常差勁。
要解決上面的問題,一個辦法是當(dāng)程序失去焦點(diǎn)的時候,你就禁止將位圖從后緩沖傳送到前緩沖,當(dāng)重新獲得焦點(diǎn)的時候,再恢復(fù)位圖傳送。但有時候這樣不理想,比如你在進(jìn)行程序或者游戲的時候,同時發(fā)ICQ的信息,雖然ICQ占的屏幕面積不大,還是會導(dǎo)致你的程序停止更新畫面,而如果你在游戲中此時正在緊張而且隨時需要你干預(yù)的話,程序看起來就非常不友好。
另一個辦法是與GDI結(jié)合,與GID結(jié)合會導(dǎo)致在圖像速度上有微小的下降,但會使你的程序看來非常自然。其實(shí)方法也非常簡單,就是獲取后臺緩沖的HDC,然后用HDC的BitBlt方法將后臺緩沖復(fù)制給窗口CDC,可以將OnDraw中的BltFast的方法修改為:
HDC hdc;
lpBackBuffer->GetDC(&hdc); //獲得后臺緩沖的HDC
::BitBlt(pDC->m_hDC,0,0,100,100,hdc,0,0,SRCCOPY); //將后臺緩沖的圖片傳送到窗口DC
lpBackBuffer->ReleaseDC(hdc); //不要忘了用完后釋放HDC,否則會出錯
為了與HDC匹配,所以使用pDC的成員m_hDC。BitBlt的具體用法可以看一下幫助文件。使用這個辦法以后,無論你將窗口移動到什么位置,以及無論你用什么窗口蓋住程序的窗口,都不會有任何不正常的現(xiàn)象發(fā)生。它唯一的缺點(diǎn)就是比BltFast慢一點(diǎn)點(diǎn)。因?yàn)镈C的處理方法是分幾次進(jìn)行,第一次復(fù)制到一個DC自己的后臺緩沖,然后根據(jù)窗口的合法區(qū)域分一次或多次復(fù)制到屏幕。所以這個方法適用于制作編輯器以及對速度不是極度敏感的游戲。如果要達(dá)到最高速度,當(dāng)然是非全屏幕模式莫屬。
?
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=3405
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
