荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Minatl (Minatl), 信区: Program
标  题: DirectDraw 游戏编程基础(3)
发信站: BBS 荔园晨风站 (Wed Jan 20 19:26:10 1999), 转信

DirectDraw 游戏编程基础(3)

游戏使计算机的发展超越了晶体管时代

      微软公司供稿
例一的扩展(DDEX2和DDEX3)
DDEX1包含了一个最基本的DirectDraw的实现方法。它生成了DirectDraw和
DirectDrawSurface对象,同时也生成了一个主表面(Surface)和与之相关的后台缓冲区,并
在后台缓冲区打印文本,并可以在表面(Surface)之间进行切换。
在DirectX 3 SDK(DDEX2)中的第二个DirectDraw 例程扩展了关于DDEX1应用程序。DDEX2包
括将一个位图文件载入到后台缓冲区的函数。
第三个DirectDraw
例程将这一函数进一步地扩展了。除了主表面(Surface)和后台缓冲区之外,DDEX3还生成了
两个隐屏表面(Surface),并且在每一个隐屏表面(Surface)之中都载入了一个位图文件。然
后,DDEX3使用IDirectDrawSurface::BltFast方法,将一个隐屏的内容复制到后台缓冲区中
。之后,弹出这
些缓冲区,并且将下一个隐屏表面(Surface)的内容复制到后台缓冲区。
以下的部分将更详细地检查这一新的函数。
在一个表面(Surface)上载入一个位图(Bitmap)
就与DDEX1一样,dolnit是DDEX2应用程序的初始化函数。虽然,在DDEX2中,DirectDraw的
初始化方式表面上与在 DDEX1中的DirectDraw的初始化方式不太一样,但它们的实质是一样
的。这一过程如下列的程序代码所示:
    LPddPal = DDLoadPalette(LpDD, szBackground);
    if (LpddPal == NULL)
     goto error;
      ddrval = LpDDSprimary->SetPalette(LpDDPal);
      if( ddral != DD_OK )
      goto error;
       // Load a bitmap into the back buffer。
     Ddrval = DDReLoadBitmap(LpDDSBack, szBackground);
     if( ddrval != DD_OK )
     goto error;
生成调色板
这个程序代码的第一行是:从DDLoadPalette函数返回一个值。如果你想知道在哪能找到
DDLoadPalette,你可以在\DXSDK|SAMPLES|MISC目录中的Ddutil.cpp文件中找到它。你会发
现,在DirectX 3
SDk的大部分DirectDraw例程中都使用了Ddutil.cpp文件。最为关键的是:该文件上包括了
能从文件中或是从资源中载入位图和调色板的函数。这些函数的代码并非一遍遍地重复出现
在例程文件中,而且被放置在能被重复使用的同一文件之中。
注意:如果你正在使用MS Developer Studio(微软开发工作室)编辑DDEX2和用DirectX 3
SDK提供的其它工具,你必须把Ddutil.cpp文件插入到DDEXx文件工作区的文件表中。重申一
遍:在工作区中必须包括Ddutil.cpp:
 1. 在插入(insert)菜单上,单击Files进入Projeects。
 2. 单击Browse.
 3. 单击DXSDK\SDK\SAMPLES\MISC\目录。
 4. 单击Ddutil.cpp
 5. 单击ADD
对于DDEX2来说,从Back.bmp文件中,DDLoadPalette创建了一个DirectDraew对象。
DDLoadPalette函数实际上是来检查用以产生调色板的一个文件或资源是否存在。如果不存
在的话,该函数就创建一个缺省的调色板,对于DDEX2
来说,DDLoadPalette函数从文件中提取调色板信息,并通过ape指针将其存储在一个指定的
结构中,然后它生成DirectDrawPalette 对象。如下面的代码所示:
          pdd->CreatePalette(DDPCAPS_8BIT, ape, &ddpal, NULL);
          return ddpal;

当IDirectDraw::Createpalette方法返回后,ddpal参数将指向DirectDrawPalette对象,其
中,对象DirectDrawPalette是从DDLoadPalettede的调用返回的。
Ape参数是一个指针,它可以包括2,4,16或256个入口,呈直线分布。这些入口的数目由
IDirctDraw::CreatePalette参数决定。在这种情况下,dwFLags参数被设置为
DDPCAPS_8BIt,它表示:在这个结构中有256个入口。每个入口包括4位(一位红通道,一位
绿通道,一位兰通道和一个标志?
?)。
设置调色板
在生成调色板之后,你要通过调用IDirectDrawSurface::SetPalette方法,将
DirectDrawPalette对象的指针转到主表面(Surface)上,如下列代码所示:
          ddrval = LpDDSPrimary->SetPalette(LpDDPal);
           if( ddrval != DD_Ok )
              // SetPalette failed
一旦你已经调用了IdirectDrawSurface::SetPalette,DirectDrawPalette对象就被嵌入
到DirectDrawSurface对象中了。不论何时你需要改变这一调色板,你要作的就是生成一个
新的调色板并重新设置该调色板。(这就如例程中所做的一样。然而,也有其它的改变调色
板的方式。我们可?
栽谄渌讨锌吹?)。
在后台缓冲区中载入一个位图文件
一旦DirectDrawPalette对象被嵌入到DirectDrawSurface对象之中,DDEX2就将Back.bmp
bitmap载入到后台缓冲区中。使用下例的程序代码可实现该过程:
           // Load a bitmap into the back buffer.
           Ddrval = DDReLoadBitmap(LpDDSBack, szBackground);
           if( ddrval != DD_Ok )
               // Load failed
DDReLoadBitmap是出现在Ddutil.cpp中的另一个函数。它从一个文件或资源中将一个位图文
件载入到一个已经存在的DirectDraw表面(Surface)之中。(就象在DDEX5中那样,你可以使
用DDLoadBitmap创造一个表面(Surface)并且将位图载入那个表面(Surface))。对于DDEX2来
说,DDReLoadBit
map把szBackground指向的Back.bmp载入到ipDDSBack指向的后台缓冲区,DDReLoadBitmap调
用DDCopyBitmap函数,将文件复制到后台缓冲区中,并且将缓冲区扩展到适当的。
DDCopyBitmap函数将位图复制到内存之中,然而利用GetObject函数得到位图的大小。
DDCopyBitmap然后使用下列的代码得到后台缓冲区的大小(它可以放置位图):
           //
           // get size of surface
           //
           ddsd.dwSize = sizeof(ddsd);
           ddsd.dwFlags = DDSD_HEIGHT DDSD_WIDTH;
           pdds->GetSurfaceDesc(&ddsd);
ddsd是指向DDSRFACEDESC结构的一个指针。该结构存储了DirectDraw表面(Surface)的当前
描述。在这种情况下,我们需要注意的是:DDSURFACEDESC的成员描述这个表面(Surface)的
高度和宽度,分别表示为:DDSD_HEIGHT和
DDSD_WIDTH。调用IDirectDrawSurface::GetSurfaceDesc方法,把适当的值来载入到这个结
构。对于DDEX2来说,这些值将是:高480和宽640。
DDCopyBitmap函数锁定表面(Surface)并将位图文件复制到后台缓冲区,使用StretchBit函
数延伸或压缩后台缓冲区到可适用的大小。表示如下:
    if ((hr = pdds->GetDC(&hdc)) == DD_OK)
          {
             StretchBlt(hdc, 0,0,ddsd.dwWidth, ddsd.dwHeight, hdcImage,x, y, dx,
 dy, SRCCOPY);
          pdds->ReleaseDC(hdc);
          }
弹出表面(Surface)
在DDEX2例程中的弹出表面(Surface)操作本质上与在DDEX1例程中的弹出操作是同样的过程
。但是在表面(Surface)丢失的情况下,你必须通过调用DDReLoadBitmap函数,在表面
(Surface)恢复之后,,将bitmap再次载入到后台缓冲区中。
从一个隐屏表面(Surface)按位隔行拷贝
DDEX2是在后台缓冲区中取出和放入位图的,然后在后台缓冲区和主缓冲区之间切换。这并
不是一个展示位图的很实际的方法。DDEX3扩展了DDEX2的功能,它包括了两个隐屏缓冲区,
且在其内部存放有两个位图(一个对应于偶行屏幕,另一个对应于奇行屏幕)。DDEX3把一个
屏幕按位隔行拷贝
到后台缓冲区中,再把另外一个屏幕按位隔行拷贝到另一个后台缓冲区中,然后弹出表面
(Surface)。
生成隐屏表面(Surface)
下列的代码在DDEX3 中加到dolnit 函数可生成两个隐屏 缓冲区:
          // Create an offscreen bitmap.
          Ddsd.dwFlags = DDSD_CAPS DDSD_HEIGHT DDSD_WIDTH;
          ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
          ddsd.dwHeight = 480;
          ddsd.dwWidth = 640;
          ddrval = lpDD->CreateSurface( &ddsd,&lpm DDSOne,NULL);
          if (ddrval != DD_OK)
          {
              return initFail(hwnd);
          }
          //Create another offscreen bitmap.
          Ddrval = lpDD->CreateSurface( &ddsd,&lpm DDSTwo,NULL);
          if (ddrval != DD_OK)
          {
              return initFail(hwnd);
          }
如代码中所示,dwFlags成员设定了应用程序将使用DDSAPS结构,并且设置缓冲区的高度和
宽度。表面(Surface)是一个平面式隐屏缓冲区,就如同通过设置在DDSCAPS结构中的
DDSCAPS_OFFSCREEN标志所表示的一样。在DDSURFACEDESc结构中,高度和宽度被分别设置为
480和640。通过使用IDi
rectDraw::CreateSurface方法,表面(Surface)就这样被生成了。
因为两个隐屏缓冲区有着同样的大小,故生成第二个缓冲区的唯一要求就是再运行
IDirectDraw::CreateSurface(当然,要用不同的指针名字)。
通过在DDSCAPS结构中,或是设置DDSCAPS_SYSTEMMEMORY,或是设置DDSCAP_VIDEOMEMORY的
容量,你可以将该:隐屏缓冲区或是放置在系统内存中或是显存中。通过将位图存盘在显存
中,你可以增加隐屏表面(Surface)和缓冲区之间切换的速度。当我们开始讨论位图动画时
,速度将变得更加?
匾5牵耸蹦阌Φ弊⒁猓喝绻憬鑫粱撼迩柚肈DScAPS_VIDEOMEMORY,而没有足
够的显存来保存整个位图文件,那么,当你试图创建表面(Surface)时,就会返回一个
DDERR_OUTOFVIDEOMEMORY的错误值。

将位图文件载入后台缓冲区
在两个隐屏表面(Surface)生成后,DDEX#使用INITSURFACES函数,从Frnt.bmp文件中将位图
文件载入到表面(Surface)中。InitSurfaces函数使用Ddutil.cpp中的DDCopyBitmap载入这
两个位图文件,如下列代码所示:
           // Load our bitmap resource.
           Hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL),szBitmap,
              IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);

           if (hbm == NULL)
               return FALSE;

           DDCopyBitmap(lpDDSone,hbm,0,0,640,480);
           DDCopyBitmap(lpDDSTwo,hbm,480,640,480);
           DeleteObject(hbm);
           return TRUE;
如果你在MS
Paint(微软画笔)或是另一个绘画程序中看到Frnt.bmp文件,你可以看到位图文件是由两
个屏幕组成的(其中一个在另一个的上部)。DDCopyBitmap函数在屏幕相汇点上将位图文件一
分为二,并将第一份位图文件载入第一个隐屏表面(Surface)(IPDDSOne)中,同时将第二份
位图载入第二个隐
屏表面(Surface)(IPDDSTwo)中。
将隐屏表面(Surface)按位隔行拷贝到后台缓冲区
WM TIMER包含了写表面(Surface)和弹出表面(Surface)的代码。在DDEX3的情况下,它包含
下列的代码,用来选择适当的隐屏表面(Surface),并将它按位隔行拷贝到后台缓冲区中。
           rcRECT.LEFT =0;
           RCRECT.TOP =0;
           RCRECT.right =640;
           rcRect.bottom =480;
           if(phase)
           {
              pdds = lpDDSTwo;
              phase = 0;
           }
           else
           {
              pdds = lpDDSOne;
              phase = 1;
           }
           while(1)
           {
              ddrval =lpDDSBack->BltFast(0,0,pdds,&rcRect,FALSE);
              if(ddrval == DD_OK)
              {
                 break;
              }

"phase"决定了将哪一个隐屏表面(Surface)按位隔行拷贝到后台缓冲区中。然后,
IDirectDrawSurface::BltFAst方法被调用,并将已经被选择好的隐屏表面(Surface)按位隔
行拷贝在后台缓冲区中,开始位置为(0,0),它位于屏幕的左上角。参数rcRect指向结构
Rect,它定义了隐屏表面(
Surface)的左上角和右下角。最后的参数被设置为FALSE(或0),这就表明了没有专门的转移
标志以备使用。
在这里,我很想补充说明的是:在何种情况下应该选择IDirectDrawSurface::Blt方法,在
何种情况下应该选择IDirectDrawSurface::BltFast方法。如果你正在从一个隐屏缓冲区中
进行一次按位隔行拷贝,你应当使用IDirectDrawSurface::BltFast。如果你的系统显存中
是使用硬件进行按?
桓粜锌奖矗闼淙徊换嵴嬲岣呖奖吹乃俣龋牵峤谑∠低衬D庥布奔洌佣拐
霭次桓粜锌奖词奔渌醵淘?10%。因此,我推荐读者使用IDirectDrawSurface::BltFast进
行所有的显示操作(从显存按位隔行拷贝到显存中)。如果你正在从系统内存中按位隔行拷贝
,或者要求专门?
挠布曛疚唬庋幕埃憔捅匦胧褂肐DirectDrawSurface::Blt。
一旦隐屏表面(Surface)被载入后台缓冲区中,后台缓冲区和主表面(Surface)就如同前边的
例程中所显示的一样被弹出。

--
※ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: 192.168.0.240]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店