日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

优化你的手游:使用脏矩形技术

發布時間:2024/8/26 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 优化你的手游:使用脏矩形技术 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文基于2D表現的游戲,在當今3D大行其道的時代,說2D是否顯得格格不入?這個問題我不作討論,因為本人從事的一直都是2D游戲的開發,所以如果你認為討論2D技術是一個過時的東西就此打住。?

??? 優化一直是我在程序中追求的東西之一,想想讓自己的游戲在一個古董機器能流暢的運行或者說在當今的機器上,CPU占用率和內存占用率都很低的情況。(畢竟我非常討厭一個游戲獨占了我所有的CPU資源)。?

??? 如果從圖形接口上作優化,常用的就是使用3D加速和CPU的特殊指令(雖然說DirectDraw能夠使用2D硬件加速,但大部分機器支持的僅僅是簡單的加速,比如帶ColorKey的支持,連一些稍微高級一點的東西,比如Alpha混合,帶Alpha通道的紋理(表面)都不支持,需要自己寫,優化起來還是使用CPU的特殊指令)。雖然說使用3D加速非常簡單,但是它的缺點也非常明顯:對硬件有苛刻的要求,如果僅僅是做2D游戲不推薦使用(新手作為練習寫DEMO而使用倒還可以,我也這樣使用過,呵呵)。使用特殊的CPU指令最常見的就是使用MMX指令了,現在想找到一塊裝了Windows95以上但不支持MMX的CPU都有難度 ~自己花了大半年的時間用MMX高速實現了D3D對2D貼圖的各種特效(帶通道或者不帶通道的紋理,帶BlendColor, 帶縮放旋轉,做加減法的Alpha混合之類的)之后,雖然發現可以不使用D3D的東西,但是如果畫面的東西很多的話,在一些內存帶寬不高的機器上的速度還是不夠理想,所以還是需要更多的優化。這時候我想起了DirtyRect。
什么是臟矩形?簡單的說,就是游戲每次畫面的刷新只更新需要更新的那一塊區域。Windows本身就是最好的例子。或者說Flash控件,也正是利用了臟矩形技術,所以他的效率才如此的高。傳統的游戲循環如下:
while( 游戲沒有結束 )
{
if( 有Windows消息 )
{
處理Windows消息
}
else if( 需要渲染 )
{
清除游戲屏幕的緩沖區
把游戲中的物體畫到緩沖區里面
把緩沖區更新到游戲窗口上
鎖定游戲速度,Sleep一段時間,限制FPS
}
}?

??? 從上面的偽代碼可以看出,每次游戲都要做清除緩沖區-〉渲染游戲的物體-〉更新到窗口,而基本上我們寫游戲至少要保證最低每秒鐘要刷新24幀以上(一般都在30)。所以上面的代碼每秒鐘要至少24次以上,畫面東西越多,耗費的CPU越多。
不過我們也可以自然的想到,每次那么多東西不一定都需要更新的,比如一個動畫,一般都有一個延遲,比如間隔200毫秒更新一次,那么在這段時間是不需要重新畫的,只有更新了幀以后,表示這個動畫所在的范圍已經“臟”了,需要重新畫,這個時候才需要畫這個動畫。而這段時間之內我們可以節約大量的CPU時間,很自然,積少成多,總體下來這個數值是非常可觀的。再舉一個例子,一個靜止的游戲物體,(比如一棵樹)是永遠都不需要更新的,除非這個樹的位置或者他的屬性發生了變化。這樣下來我們首先想到的是,每次我們都省略清除后臺緩沖這個步驟,這個非常重要,因為上一次畫下來的東西都在這個緩沖區里面,如果清除之后就什么都沒有啦~~?

??? 搞明白了這個原理以后,下面來看看具體實現過程中遇到的問題:?

??? 游戲中的物體不會是相互沒有遮擋的,所以如果遇到遮擋的問題怎么辦??

??? 如果游戲中有100個物體,里面的物體相互遮擋關系總有一個順序,為了簡化問題,只考慮兩個物體遮擋的情況,多個物體的遮擋可以根據這個來衍生。

??? 考慮上圖,物體B遮擋了物體A, 也就是說渲染順序是先畫A再畫B,這個順序由各自定義,(我自己就喜歡用一棵渲染樹來排序,當然如果你用連表或者其他數據結構來實現也沒有問題。)如果物體A的整個區域都需要更新,那么對于B物體,需要更新的部分也就只有A與B的交集部分(圖中的藍色區域),在畫B的時候,我們設置目標裁減區域(也就是屏幕緩沖的裁減區域)為這個交集部分,則B在渲染的時候,相當于整個緩沖區大小就只有藍色區域那么大,那么裁減函數將會把B的數據區裁減到相應的位置(你實現的圖形函數中不會沒有做裁減的工作吧???如果沒有實現,你就不用看了,直接return算了,不然下面的東西你肯定不明白我說什么)。怎么樣,B物體相當于只畫了藍色區域這一部分的東西,比整個區域來說節約了不少時間吧??

??? 不知道上面說的你明白了沒有,如果沒有明白請多看幾遍,直到弄明白之后再往下看,不然千萬不要往下看。
上面的例子大家肯定會問一個問題,我如何控制B只畫藍色區域的部分呢?這個問題我暫時不說,等到把所有的遮擋情況說完了再說。繼續看另外的遮擋情況

??? 上面6個物體A,B,C,D,E,X。X是我們的游戲背景顏色,假設畫的順序是EADCB,如果E需要重新畫,那很顯然,A,B,C,D不需要做什么?

??? 如果A,D都需要重新畫,那顯然A,D只需要各畫一次。而B需要更新的,不是需要更新BD相交的區域,而是AB相交的大區域,也就是說小區域該忽略掉,如果B需要重新畫,A,D,C需要重新畫嗎?也許有人會說,B畫的次序是在最后的,所以前面的就不需要畫了,對么?答案是錯的,需要重新畫,因為背景緩沖區我們一般情況下不去清除它,所以談不上畫的順序了。也就是說,A與B相交的部分,A在下次畫的時候也需要更新,D也同樣(想通了嗎?再舉一個例子,如果B含有大量的透明色,如果B需要更新的話,那么B的區域首先要涂上X作為背景,不然B非透明色如果變成了透明色的話,那B在重新畫的時候,由于透明色不需要畫,那么B上一次留下來的顏色就殘留在X上面,看起來當然不對啦,同理對于A,D也一樣處理)。
上面的理論部分不知道聽明白了沒有,如果不明白的話自己花一點點時間去想象看。假如明白了的話,下面繼續更加深入的問題。?

??? 從上面的理論解說部分可以看出,臟矩形的選取和優化是關鍵。怎樣得到最優化的臟矩形表,就成為了這個技術優化的核心部分。?

??? 為了簡單起見,這里使用的是一個鏈表來管理所有的渲染物體。?

??? 為了實現我們所設計的手游賬號購買平臺,我設計了一個非常簡單的類:
class CRenderObject
{
public:
virtual ~CRenderObject(){}
virtual void OnRender( GraphicsDevice*pDevice ) = 0; //所有物體都在這里渲染
virtual void OnUpdate( float TimeStamp ) = 0;//物體更新,比如動畫幀更新拉之類的,在這里面可以設置DirtyRect標志之類的
virtual bool IsDirty( ) = 0;//是否有臟矩形
virtual bool GetBoundsRect(RECT*pRect) =0;//得到該物體的范圍
virtual int GetDirtyRects ( RECT*pRectBuffer ) = 0;//該物體的臟矩形個數,填充到pRectBuffer里面,返回填充了多少個
...其他函數
};?

??? 我們還需要一個簡單的能管理臟矩形和渲染物體的類
class CRenderObjectManager
{
pulibc:
void RemoveRenderObject( CRenderObject*pObject );//刪除一個渲染物體
void AddRenderObject( CRenderObject*pObject );//添加一個渲染物體
void Render( GraphicsDevice*pDevice );//渲染所有的物體
void Update( );//更新所有物體
.....其他函數
protected:
std::list< CRenderObject* > m_RenderObjects;
int m_nCurrentDirtyRectCount;//當前臟矩形數量
struct DirtyRect
{
RECT Range; //臟矩形范圍
int AreaSize; //臟矩形大小,用來排序
};
BOOL m_bHoleDirty;//是否全部臟了
DirtyRect m_DirtyRects[128];//屏幕上最多的臟矩形數量,如果大于這個數量則認為屏幕所有范圍都臟了
};
void CRenderObjectManager::Update()
{
m_bHoleDirty = false;
m_nCurrentDirtyRectCount = 0;
static RECT DirtyRectBuffer[128];
float TimeStamp = GetElapsedTime();
for(std::list< CRenderObject* >::iterator it = m_RenderObjects.begin();
it != m_RenderObjects.end(); it++)
{
CRenderObject*pObject = *it;
pObject->OnUpdate( TimeStamp );
if(m_bHoleDirty == false && pObject->IsDirty() )
{
int Count = pObject->GetDirtyRects(DirtyRectBuffer);
for( i =0; i<count;i++) <br="">{
對于該物體的每一個臟矩形DirtyRectBuffer[i]
如果DirtyRectBuffer[i] 沒有在任何一個已有的臟矩形范圍內
那么把這個臟矩形根據從大到小的順序添加到臟矩形范圍內,否則忽略這個臟矩形
   如果臟矩形數量已經大于設定的最大臟矩形范圍,設置所有所有屏幕都臟了的標志,
}
}
}
如果屏幕所有都臟了,填充背景顏色
否則為每一個臟矩形填充背景顏色
}
void CRenderObjectManager::Render( GraphicsDevice* pGraphics)
{
for(std::list< CRenderObject* >::iterator it = m_RenderObjects.begin();
it != m_RenderObjects.end(); it++)
{
CRenderObject*pObject = *it;
if(如果屏幕都臟了的標志已經設定)
{
RECT rcBoundsRect = { 0, 0, 0, 0 };
if( pObject->GetBoundsRect( rcBoundsRect ) )
{
//設置屏幕裁減區域
pGraphics->SetClipper( &rcBoundsRect );
}
pObject->OnRender( pGraphics );
}
else
{

RECT rcBoundsRect = { 0, 0, 0, 0 };
if( pObject->GetBoundsRect( rcBoundsRect ) )
{
//如果該物體的范圍與臟矩形緩沖區的任何一個臟矩形有交集的話
for( int i=0; i<m_ncurrentdirtyrectcount; i++="" )="" <br="">{
RECT rcIntersect;
if( ::IntersectRect( &rcIntersect, &m_DirtyRects[i].Range, &rcBoundsRect ) )
{
//只畫交集的部分
pGraphics-> SetClipper ( &m_DirtyRects[i].Range );
pObject->OnRender( pGraphics );
}
}
}
}

}?

??? 好了,核心代碼的偽代碼就在這里,不知道大家看明白沒有,當然我在這里上面實現的這種方法有一個缺陷,最壞情況下一個也許會導致重新畫很多次,如圖的情況:

??? 假設A是渲染物體,B,C,D,E是由大到小的臟矩形范圍,那么很顯然,重疊的部分就被反復畫。。。這是在分割臟矩形導致的問題,這樣畫下來,如果A物體是采用了疊加混合到背景的算法的話,問題就出來了,YY號重畫的部分會變得非常亮。所以臟矩形的分割就顯得非常重要,也就是說把這些臟矩形還要分割為互相獨立的互不相交的矩形,至于分割算法嘛,嘿嘿,各位還是動一下腦筋思考思考吧:)這個也是最值得優化的地方了,哈哈。實在想不出的話,最簡單的方法就是把彼此相交的臟矩形都做一個合并,得到更大的臟矩形,雖然沒有相交的區域了,但是也許這個臟矩形會變得比較大了哦:)
最后,大家一定關心的是我會不會提供源代碼,很抱歉的說,不能。我在我的引擎中實現的不是以簡單的鏈表去做的,用的是一棵比較復雜的渲染樹,牽扯到的東西就比較多了,所以不方便提供代碼,不過可以給一個演示吧:)再說大家如果真的明白了我所說的,那就可以自己動手寫一下嘛,不要怕失敗/。

??? 好啦,關于臟矩形的技術就介紹到這里啦,用好這個技術你會發現你的游戲會在配置極低的機器上也能運行如飛的:)這種技術如果能用在現在市面上的那么多的游戲中的話,就不必為一個小游戲就強占了您100%的CPU資源而煩惱拉:)
如果您有更好的方法或者指出其中的不完善的地方還請您不吝賜教,大家多多交流:)

關于測試的Demo?
??? 該Demo渲染部分由Kylinx花了近半年的時間,全部采用MMX寫成,已經成功實現d3d中對2d紋理的操作,速度非常快

關于Settings.ini
EnableDirtyRect = 1 //是否允許臟矩形技術,0=關閉,1=開啟
LockFPS = 1 //是否鎖定FPS,0=關閉,1=開啟
哈,這個Demo在不鎖定FPS,臟技術開啟的的情況下,我的Duron1.8G CPU,FPS達到 31500左右!(沒錯,是三萬一千五百)這個數字嚇人吧?如果臟技術未打開,只能在150左右,相差200倍阿!!!

如果LockFPS開啟,在我機器上(512M DDR)跑30個DEMO,CPU占用還是為0,哈哈!?

??? 關于該引擎:演示用的這個引擎(代號ShinyFairy:閃靈,基于前期開發的GFX3.0系列,該系列已經成功運行在某商業游戲公司的休閑游戲系列),采用Kylinx歷時2年多開發的具有自主知識產權的基于2D游戲的超級引擎,強大的數據加密,打包,圖形接口同時提供d3d8版本和mmx版本,該演示使用的是mmx版本,適用于各種休閑游戲平臺,或者大型2D RPG/MMORPG均可適用。

總結

以上是生活随笔為你收集整理的优化你的手游:使用脏矩形技术的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。