前不久用模擬器玩了SFC上的一個經(jīng)典SLG——圣龍戰(zhàn)記后,突然對它出色的表現(xiàn)有了濃厚的興趣,尤其是在那種硬件平臺下,僅僅3M的游戲竟然能夠有如此出色的表現(xiàn)!不僅是畫面表現(xiàn)得極致,而且整個游戲的系統(tǒng),情節(jié)相對當(dāng)今的大多數(shù)游戲來說,實在是有過之而無不及!~實在是佩服萬分~!
確實,現(xiàn)在的硬件條件都比以前好多了,做一個游戲也越來越簡單了(雖然我沒有在DOS下寫過程序,但是經(jīng)過兩年多的編程,對各個方面都有些了解,仍能體會到在DOS下寫游戲的痛苦:)), 現(xiàn)在各種各樣的游戲開發(fā)包也越來越多了,不說別的,就直接用DirectX SDK吧,做一個小的游戲比如飛機類也不會花幾天時間(以前我花了3天做過一個^_^),開發(fā)簡單了,自然有些東西就不那么講究了,比如說現(xiàn)在的商業(yè)游戲的容量,無論是什么都先比光盤多少,你的3CD,我來5CD,除去里面"免費贈送"的一些"原聲大蝶" 阿,"官方資料"阿之類的東西,一個游戲至少也有1個G,(大概是現(xiàn)在的硬盤在大家的眼中不怎么值錢了吧,可能商家是這么認(rèn)為的,也可能是大眾心理:東西越多越好嘛:)),真正有用的數(shù)據(jù)有多少?估計也只有商家才清楚~先不說某些游戲連壓縮都沒壓縮過就裸用一大堆的24Bit BMP( 沒錯,就是標(biāo)準(zhǔn)的位圖)來做游戲中的資源(僅僅做了一個未壓縮的資源包,很輕松就能提取出全部資源:)),畫面看起來效果好么?確實,不過那沒有什么,反正就是美工的表現(xiàn)嘛!還對機器要求至少有PIII 500、128M以上的RAM~真是Faint!
赫赫,也許大多數(shù)人不在乎上面提到的東西~可是作為一個游戲開發(fā)者,一個游戲程序設(shè)計者,就要在能力范圍內(nèi)對游戲程序做盡可能的優(yōu)化(先不提商業(yè)制作的一些"無奈"的原因的阻礙),就比如說星際,大概是我所見到的PC上的商業(yè)游戲中做的最好的一個了:)
好了,廢話了一大堆,下面來談?wù)匋c正式的。 如今2D PC游戲上,最流行的就是16位色的顯示方式(主要是從速度和內(nèi)存消耗以及顯示質(zhì)量這些方面上來綜合),16位色上基本上是565的顯示方式(我到現(xiàn)在還從來沒有見到一臺555顯示的機器或一塊555的顯卡),所以我只討論16bit下565 模式。
下面的方法是由于'調(diào)色板'而來的靈感~ (先申明,這種方法絕對不適合主流技術(shù),基于上面我所說的游戲----圣龍戰(zhàn)記,可以做類似的游戲~不適合基于象素的游戲,對TILE類游戲比較實用)
由于TILE類游戲用到的TILE顏色相對都比較固定,顏色種類比較少,所以我們可以選取一個固定的調(diào)色板,里面能容納大部分的TILE顏色,這樣所有TILE的數(shù)據(jù)都可以用這個調(diào)色板的索引來表示,當(dāng)然為了方便,256種顏色最好不過,這樣每個點只占8位(也許有人會說,這樣不就干脆創(chuàng)建一個8位色的游戲不就行了?嘿嘿,稍安勿躁,馬上解釋),在內(nèi)存消耗上就有了很大的優(yōu)勢~如果再壓縮一下,嘿嘿........
從速度上來說,由于游戲里面需要大量的特效,比如最常用的半透明效果,色彩飽和效果、陰影效果、灰度化等等效果,所以從這方面來考慮。
由于只用到了256色,混合后的顏色也在256種顏色內(nèi),所以考慮用查表方式, 這256種顏色從16Bit 565模式共65536種顏色的色彩空間中提取出來,這樣就算是32級的Alpha混合也就只占用256*256*32*8bit=2M的內(nèi)存。 但是用16級或者12級我就覺得夠了,這樣就有 256*256*16*8Bit = 1M 或者 256*256*12*8Bit = 768K 色彩飽和表就只需要 256*256*8Bit = 64K
陰影表也就只要 256*256*8Bit = 64K 灰度表只要 256*8Bit = 0.256K 一共加起來也就1M左右,呵呵夠少吧! 如下: static unsigned char BDI_AlphaBlendTable[16][256][256]; //16級Alpha混和表 static unsigned char BDI_AdditiveTable[256][256]; //Additive表 static unsigned char BDI_SubTable[256][256]; //陰影表 static unsigned char BDI_GrayTable[256]; //灰度表
那么,哪256種顏色可以很好的描述大部分圖片的顏色呢? 嘗試過幾個不同的調(diào)色板后,最后發(fā)現(xiàn)下面這個調(diào)色板效果最好(并且還有附加的優(yōu)勢!稍后看到) 如下, unsigned short wPal[256]; for(int i=0;i<256;i++) { wPal[i]=i|(i<<8); }
也就是說這個調(diào)色板的高8位和低8位是相同的,嘿嘿,想到什么了?(趕快用10秒鐘猜猜,下面回答)
當(dāng)然這樣一來也就不能直接用DirectDraw里面的Blt之類的東西啦~,另外,由于我們的數(shù)據(jù)保留的是調(diào)色板的索引,所以,不能直接Blt到BackSurface上,自己分配一個緩沖區(qū),大小和BackSurface一樣大,不過用byte類型就夠啦~
自己寫幾個Blt吧:
比如一個Alpha混合的操作:(代碼取自我給出的Demo) void GBDI::DrawToScreenAdditiveSrcColorKey(unsigned char*pBufDest,int nDestWidth,unsigned char*pBufSour,int nLine,int nRow) { unsigned char*pDestAddr = pBufDest; unsigned char*pSourAddr = pBufSour; for(register int i=0;i<nRow;i++) { for(register int j=0;j<nLine;j++) { if(*pSourAddr != m_byColorKeyIndex) { *pDestAddr = GBDI::BDI_AdditiveTable[*pSourAddr][*pDestAddr]; //這個地方極大的節(jié)省了大量的數(shù)學(xué)運算 } pDestAddr++; pSourAddr++; } pBufDest += nDestWidth; pBufSour += m_nWidth; pDestAddr = pBufDest; pSourAddr = pBufSour; } }
上面的操作是經(jīng)過裁減過后的顯示,裁減代碼如下:
RECT rtDest = {m_position.x,m_position.y,m_position.x+pScreen->GetWidth(),m_position.y+pScreen->GetHeight()}; RECT rtSour = m_rtShowArea; if(rtDest.top<0) { rtSour.top -= rtDest.top; rtDest.top = 0; } if(rtDest.left<0) { rtSour.left -= rtDest.left; rtDest.left = 0; } if(rtDest.left+rtSour.right-rtSour.left>pScreen->GetWidth()) { rtSour.right = rtSour.left+pScreen->GetWidth()-rtDest.left; } if(rtDest.top+rtSour.bottom-rtSour.top>pScreen->GetHeight()) { rtSour.bottom = rtSour.top+pScreen->GetHeight()-rtDest.top; }
unsigned char*pBufDest = pScreen->GetBuffer()+pScreen->GetWidth()*rtDest.top+rtDest.left;//目標(biāo)地址 unsigned char*pBufSour = m_pData+m_nWidth*rtSour.top+rtSour.left;//源地址 int nLine = rtSour.right-rtSour.left; int nRow = rtSour.bottom-rtSour.top;
各種參數(shù)的含義都比較明顯,了解E文的并且寫過代碼的應(yīng)該都能看懂,看不懂的如果有興趣的話,自己去看完整源代碼,好了,如何才能在屏幕上正確的顯示呢? 這個問題就很簡單了,當(dāng)然最最直接的方法就是:
for(緩沖區(qū)上的每一個點) BackSurface上的每一個點 = 緩沖區(qū)上的每一個點所代表的調(diào)色板的值
嘿嘿,別忘記了,上面說過用到的調(diào)色板是什么來的? 低8位和高8位相同! 如果了解mmx的話,就應(yīng)該知道這一條指令:punpcklbw 哈哈!如何?知道優(yōu)化的方法了吧?
下面是我的Demo中的代碼: DDSURFACEDESC2 ddsd; ZeroMemory(&ddsd,sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); hr = m_pDSBack->Lock(NULL,&ddsd,DDLOCK_WAIT,NULL); while(DD_OK!=hr) { if(DDERR_SURFACELOST==hr) RestoreSurface(); else return; hr=m_pDSBack->Lock(NULL,&ddsd,DDLOCK_WAIT,NULL); } unsigned char*pSourBuf = (unsigned char*)m_pBuffer;
if(m_bDefaultPal)//如果是采用了默認(rèn)的調(diào)色板(高8位==低8位) { //由于初學(xué)mmx,還不會作mmx指令的優(yōu)化~代碼見笑了~ unsigned long dwResPitch = ddsd.lPitch-(m_nWidth<<1); unsigned char*pBuf = (unsigned char*)ddsd.lpSurface; unsigned long dwHeight = m_nHeight; unsigned long loopTime = m_nWidth>>5; //一次處理32個索引點 { _asm { mov esi,pSourBuf; mov edi,pBuf; mov edx,dwHeight; rowLoop: cmp edx,0; je end; mov ecx,loopTime; mmxdraw: movq mm0,[esi]; //8個索引點 movq mm2,[esi+8]; //后8個索引點 movq mm4,[esi+16]; movq mm6,[esi+24]; movq mm1,mm0; movq mm3,mm2; movq mm5,mm4; movq mm7,mm6; punpcklbw mm0,mm0; //0-3個索引的值 punpckhbw mm1,mm1; //4-7 punpcklbw mm2,mm2; //8-11 punpckhbw mm3,mm3; //12-15 punpcklbw mm4,mm4; punpckhbw mm5,mm5; punpcklbw mm6,mm6; punpckhbw mm7,mm7;
movq [edi],mm0; movq [edi+8],mm1; movq [edi+16],mm2; movq [edi+24],mm3; movq [edi+32],mm4; movq [edi+40],mm5; movq [edi+48],mm6; movq [edi+56],mm7;
add esi,32; add edi,64; loop mmxdraw; dec edx; add edi,dwResPitch; jmp rowLoop; end: emms; } } } else { unsigned long dwResPitch = (ddsd.lPitch>>1)-m_nWidth; unsigned short*pBuf = (unsigned short*)ddsd.lpSurface; for(register int i=0;i<m_nHeight;i++) { for(register int j=0;j<m_nWidth;j++) { *pBuf = m_pPal[*pSourBuf]; pBuf++; pSourBuf++; } pBuf += dwResPitch; } } m_pDSBack->Unlock(NULL);
嘿嘿,最后最最重要的一點就是:效果如何呢? 這一點我無權(quán)評論,大家可以看看demo再說在我的機器( CII 950 + 256M SDR)上FPS最高能到200左右。
附帶一點,這個Demo中,我運用了類似模擬器上的圖層管理的方法,其思想就是分n個layer,每個layer上的圖元都有一個高度,高度范圍為m,然后每一個layer上的圖元全部由m個鏈表連接起來,畫圖順序為:最先畫的圖層是0號圖層,最先畫的是0號鏈表,直到n個layer和m個高度(這樣就可以隨意關(guān)閉或者打開第幾個layer,就像模擬器一樣,并且很容易的實現(xiàn)流水線渲染具體情況看我的demo代碼)。
最后說明,現(xiàn)在的游戲都使用的是即時計算來進(jìn)行渲染(包括我正在寫的一個engine),并且使用3d加速來做特效上面這種方式雖然簡單高效,但是只是在TILE方式下~~有興趣研究Tile方式的游戲的朋友們不妨try一下~
好了,就到這里,浪費大家的寶貴時間了,多有得罪~
Demo download http://www.gameres.com/Articles/Program/Visual/2D/BlueDream.rar
有興趣的朋友歡迎和我探討: game-diy@163.com OICQ:30784290(難得糊涂) http://www.gamepp.org/
2003/12/4 night
|