2d游戏引擎:对2D游戏引擎设计的一些研究

  前不久用模拟器玩了SFC上个经典SLG——圣龙战记后突然对它出色表现有了浓厚兴趣尤其是在那种硬件平台下仅仅3M游戏竟然能够有如此出色表现!不仅是画面表现得极致而且整个游戏系统情节相对当今大多数游戏来说实在是有过的而无不及!~实在是佩服万分~!

  确实,现在硬件条件都比以前好多了个游戏也越来越简单了(虽然我没有在DOS下写过但是经过两年多编程对各个方面都有些了解仍能体会到在DOS下写游戏痛苦:)) 现在各种各样游戏开发包也越来越多了不说别就直接用DirectX SDK吧个小游戏比如飞机类也不会花几天时间(以前我花了3天做过个^_^)开发简单了自然有些东西就不那么讲究了比如说现在商业游戏容量无论是什么都先比光盘多少3CD我来5CD除去里面"免费赠送"些"原声大蝶" 阿"官方资料"阿的类东西个游戏至少也有1个G(大概是现在硬盘在大家眼中不如何值钱了吧可能商家是这么认为也可能是大众心理:东西越多越好嘛:))真正有用数据有多少?估计也只有商家才清楚~先不说某些游戏连压缩都没压缩过就裸用大堆24Bit BMP( 没错就是标准位图)来做游戏中资源(仅仅做了个未压缩资源包很轻松就能提取出全部资源:))画面看起来效果好么?确实不过那没有什么反正就是美工表现嘛!还对机器要求至少有PIII 500、128M以上RAM~真是Fa

  赫赫也许大多数人不在乎上面提到东西~可是作为个游戏开发者个游戏设计者就要在能力范围内对游戏做尽可能优化(先不提商业制作些"无奈"原因阻碍)就比如说星际大概是我所见到PC上商业游戏中做最好个了:)

  好了,废话了大堆,下面来谈谈点正式
  如今2D PC游戏上最流行就是16位色显示方式(主要是从速度和内存消耗以及显示质量这些方面上来综合)16位色上基本上是565显示方式(我到现在还从来没有见到台555显示机器或块555显卡)所以我只讨论16bit下565 模式

  下面思路方法是由于'调色板'而来灵感~
  (先申明,这种思路方法绝对不适合主流技术,基于上面我所说游戏----圣龙战记可以做类似游戏~不适合基于象素游戏,对TILE类游戏比较实用)

  由于TILE类游戏用到TILE颜色相对都比较固定颜色种类比较少所以我们可以选取个固定调色板里面能容纳大部分TILE颜色这样所有TILE数据都可以用这个调色板索引来表示当然为了方便256种颜色最好不过这样每个点只占8位(也许有人会说这样不就干脆创建个8位色游戏不就行了?嘿嘿稍安勿躁马上解释)在内存消耗上就有了很大优势~如果再压缩嘿嘿........

  从速度上来说由于游戏里面需要大量特效比如最常用半透明效果色彩饱和效果、阴影效果、灰度化等等效果所以从这方面来考虑

  由于只用到了256色混合后颜色也在256种颜色内所以考虑用查表方式
  这256种颜色从16Bit 565模式共65536种颜色色彩空间中提取出来这样就算是32级Alpha混合也就只占用256*256*32*8bit=2M内存
  但是用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左右,呵呵够少吧!
  如下:
     unsigned char BDI_AlphaBlendTable[16][256][256]; //16级Alpha混和表
     unsigned char BDI_AdditiveTable[256][256];       //Additive表
     unsigned char BDI_SubTable[256][256];            //阴影表
     unsigned char BDI_GrayTable[256];                //灰度表

  那么哪256种颜色可以很好描述大部分图片颜色呢?
  尝试过几个区别调色板后最后发现下面这个调色板效果最好(并且还有附加优势!稍后看到)
  如下
    unsigned wPal[256];
    for( i=0;i<256;i)
    {
     wPal[i]=i|(i<<8);
    }

  也就是说这个调色板高8位和低8位是相同嘿嘿想到什么了?(赶快用10秒钟猜猜下面回答)

  当然这样来也就不能直接用DirectDraw里面Blt的类东西啦~另外由于我们数据保留是调色板索引所以不能直接Blt到BackSurface上自己分配个缓冲区大小和BackSurface样大不过用类型就够啦~

  自己写几个Blt吧:

  比如个Alpha混合操作:(代码取自我给出Demo)
    void GBDI::DrawToScreenAdditiveSrcColorKey(unsigned char*pBufDest, nDestWidth,unsigned char*pBufSour, nLine, nRow)
    {
        unsigned char*pDestAddr = pBufDest;
        unsigned char*pSourAddr = pBufSour;
        for(register i=0;i<nRow;i)
        {
            for(register j=0;j<nLine;j)
            {
                (*pSourAddr != m_byColorKeyIndex)
                {
                    *pDestAddr = GBDI::BDI_AdditiveTable[*pSourAddr][*pDestAddr];
                    //这个地方极大节省了大量数学运算
                }
                pDestAddr;
                pSourAddr;
            }
            pBufDest nDestWidth;
            pBufSour m_nWidth;
            pDestAddr = pBufDest;
            pSourAddr = pBufSour;
        }
}

上面操作是经过裁减过后显示裁减代码如下:

    RECT rtDest = {m_position.x,m_position.y,m_position.x+pScreen->GetWidth,m_position.y+pScreen->GetHeight};
    RECT rtSour = m_rtShowArea;
    (rtDest.top<0)
    {
        rtSour.top -= rtDest.top;
        rtDest.top = 0;
    }
    (rtDest.left<0)
    {
        rtSour.left -= rtDest.left;
        rtDest.left = 0;
    }
    (rtDest.left+rtSour.right-rtSour.left>pScreen->GetWidth)
    {
        rtSour.right = rtSour.left+pScreen->GetWidth-rtDest.left;
    }
    (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;//目标地址
    unsigned char*pBufSour = m_pData+m_nWidth*rtSour.top+rtSour.left;//源地址
     nLine = rtSour.right-rtSour.left;
     nRow = rtSour.bottom-rtSour.top;

  各种参数含义都比较明显了解E文并且写过代码应该都能看懂看不懂如果有兴趣自己去看完整源代码好了如何才能在屏幕上正确显示呢? 这个问题就很简单了当然最最直接思路方法就是:

  for(缓冲区上个点)
    BackSurface上个点 = 缓冲区上个点所代表调色板

嘿嘿,别忘记了,上面说过用到调色板是什么来?
低8位和高8位相同!
如果了解mmx话,就应该知道这条指令:punpcklbw
哈哈!如何?知道优化思路方法了吧?

下面是我Demo中代码:
    DDSURFACEDESC2 ddsd;
    ZeroMemory(&ddsd,(ddsd));
    ddsd.dwSize = (ddsd);
    hr = m_pDSBack->Lock(NULL,&ddsd,DDLOCK_WAIT,NULL);
    while(DD_OK!=hr)
    {
        (DDERR_SURFACELOSThr)
            RestoreSurface;
        
            ;
        hr=m_pDSBack->Lock(NULL,&ddsd,DDLOCK_WAIT,NULL);
    }
    unsigned char*pSourBuf = (unsigned char*)m_pBuffer;

    (m_bDefaultPal)//如果是采用了默认调色板(高8位低8位)
    {
        //由于初学mmx,还不会作mmx指令优化~代码见笑了~
        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;
            }
        }
    }
    
    {
        unsigned long dwResPitch = (ddsd.lPitch>>1)-m_nWidth;
        unsigned *pBuf = (unsigned *)ddsd.lpSurface;
        for(register i=0;i<m_nHeight;i)
        {
            for(register j=0;j<m_nWidth;j)
            {
                *pBuf = m_pPal[*pSourBuf];
                pBuf;
                pSourBuf;
            }
            pBuf dwResPitch;
        }
    }
    m_pDSBack->Unlock(NULL);

嘿嘿最后最最重要点就是:效果如何呢?
点我无权评论大家可以看看demo再说在我机器( CII 950 + 256M SDR)上FPS最高能到200左右

  附带这个Demo中我运用了类似模拟器上图层管理思路方法其思想就是分n个layer每个layer上图元都有个高度高度范围为m然后每个layer上图元全部由m个链表连接起来画图顺序为:最先画图层是0号图层最先画是0号链表直到n个layer和m个高度(这样就可以随意关闭或者打开第几个layer就像模拟器并且很容易实现流水线渲染具体情况看我demo代码)

  最后介绍说明现在游戏都使用是即时计算来进行渲染(包括我正在写个engine)并且使用3d加速来做特效上面这种方式虽然简单高效但是只是在TILE方式下~~有兴趣研究Tile方式游戏朋友们不妨try下~

  好了就到这里浪费大家宝贵时间了多有得罪~

Demo download
http://www.gameres.com/Articles/Program/Visual/2D/BlueDream.rar

有兴趣朋友欢迎和我探讨:
[email protected]
OICQ:30784290(难得糊涂)
http://www.gamepp.org/

2003/12/4 night 
Tags:  2d游戏设计 2d设计 2d引擎 2d游戏引擎

延伸阅读

最新评论

发表评论