这并非哗众取宠, 通常学习一种电脑技术有两种方法. 一种是自己摸索, 在错误的方向上一错再错, 屡战屡败, 不过最后得道成功. 另一种是有人 或好的材料指导, 因而事半功倍, 在正确的方向上走了速成的捷径. 就象KFC 的鸡一样. 第一种学法能学出电脑天才, 因为所谓电脑高手, 其实就是排错试错的高手. 而第二种则出电脑专才. 这个两小时(?)的学习, 不能使你深入的掌握DD, 不过可以给你编制DD的框架. 能给你 一个起始点, 这个教程就算成功了.
DirectDraw编程需要一些背景知识:
DirectDraw是为在 Windows95/NT 下实现高速图形显示所写的程式库.
高速图形显示的基本方法是用一种叫做 Page Flipping的技术. 关于什么是 Page Flipping, 参见古技介绍.如果你 不急的话, 看到下面, 你也会看到.
在 Windows95/NT下做 Page Flipping 分为全屏的和窗口的两种. 在全屏下Page Flipping 叫做Flip, 在窗口下叫做 Blit.
知道了这些背景知识, 我们可以开始写程式了.
写所有 DirectDraw的程式, 差不多都有以下几个步骤,
1. 初始化, 这是每个程式都需要的劳什子.
2. 设置显示模式.
3. 在内存里建立PageFlipping所需要的两个页, 前页和后页.
4. 给显示的区域加个画框以免画到外面来.
5. 在后页画图, 然后"刷"的一下子换到前页来.
步骤一: 初始化
DirectDraw 是一个面向对象的函数库. "面向对象"的意思并不是指面对著你的女朋友, "对象" 在这里, 你可以简单地想象成是一个模板, 比方说,"政府", 一旦你说:"我成立了一个政府". 别人 就会立即把你套入"政府模板", 自然而然地认为你有印钞票的功能. 在我们的程式里, 你一旦声明 一个变量(比如 myDD)是 DirectDraw对象 (DirectDraw对象的正式名为 LPDIRECTDRAW) , 这个myDD就有了 DirectDraw对象的所有的功能和特性. 定义 的语法是:
LPDIRECTDRAW pMyDD;
除了 DD的对象外, 还有几个重要的对象, "页面", "裁剪板" 和 "调色板". "页对象"用来定义"前页"和"后页". 定义如下:
LPDIRECTDRAWSURFACE pMyDDSFront;
LPDIRECTDRAWSURFACE pMyDDSBack;
一个"裁剪板对象", 在窗口模式下用来剪去画出窗口边界的部份.
LPDIRECTDRAWCLIPPER pMyClipper;
"调色板"设定屏幕的颜色表, 在读取256色的 Bitmap时要用到.
LPDIRECTDRAWPALETTE myDDPal;
最最重要的"对象"就是这些了. 当然 DirectX还有很多复杂晦涩的对象. 这是速成不起来的.
编制 Windows 程式有一大堆变量和对象是 Windows所要求的, 这也是我最烦 Microsoft的地方. Microsoft 似乎知道这点. 所以在 VC4.0后的版本有了 Wizard的功能帮你自动生成代码. 尽量地去用它的 Wizard使我们的生活变得容易.
由于我们的程式可能会占用一个窗口, 就给这个窗口一个 handle:
HWND myWnd
初始化的工作还没有完, 我们要把这些对象指向一个安全的地方 Null.
pMyDD = NULL;
pMyDDSFront = NULL;
pMyDDSBack = NULL;
pMyClipper = NULL;
pMyDDPal=NULL;
最后, 在 Windows系统为我们的 myDD对象开辟相应的区域:
DirectDrawCreate( NULL, //用当前的显示驱动
&pMyDD, NULL))
Okay, 烦人的初始化总算完了.
步骤二: 设置屏幕的显示方式.
DirectDraw 有自己的设置屏幕的方式, 而且它的屏幕模式分为"全屏"( exclusive mode)和"窗口"( normal mode). 各有各的设置方法. 设置的主要区别在于 SetCooperativeLeve的参数.
SetCooperativeLeve 在"窗口"模式下这样设置:
pMyDD->SetCooperativeLevel(AfxGetMainWnd()->GetSafeHwnd(),DDSCL_NORMAL);
而在"全屏"模式下这样设置:
pMyDD->SetCooperativeLevel( hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
如果它们的返回值为 DD_OK表示成功. 我们就可以把屏幕调节成我们想要的样子, 例如 640x480x8. 也就是256色. 究竟有那些屏幕模式可用取决于你的显示卡
pMyDD->SetDisplayMode( 640, 480, 8 );
现在, 我们已经有了一个屏幕, 不过还不能在上面画画, 我们需要步骤三来 替我们建立一个可供画画涂涂用的画板.
步骤三: 建立前后页(两块画板).
两块画板的好处是可以一边在一块上面画, 一边给别人看已经画好的另一块. 等这块画好了, 两块板就对调一下, 让别人看新画好的这块. 如果画的足够快, 换的足够快. 看的人就会看到动画了, 就象电影的效果一样. 我们把这叫做 Page Flipping.
这里先要介绍的是怎样在系统中建立两块画板( double buffering), 不过你也可以根据需要建立三块,四块画板.
DDSURFACEDESC ddsd; //这个结构描述"页"的特徵.
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;//指定我们用的是前页.
ddsd.dwSize = sizeof(ddsd); //尺寸
// 做前页:
HRESULT result;
result = pMyDD->CreateSurface(&ddsd, &pMyDDSFront, NULL);
当发生错误时, 要记得 Release对象.
if (result!=DD_OK)
{
pMyDD->Release();
pMyDD = NULL;
}
ddsd.dwWidth = scr_width; //设定后页的大小,
ddsd.dwHeight = scr_height;
//指定 我们要后页
ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
//做后页
result = pMyDD->CreateSurface(&ddsd, &pMyDDSBack, NULL);
}
步骤四: 给显示区加一个画框(裁剪板).
在窗口下. 为了防止 DirectDraw 画到窗口外面去. 需要加一个画框(裁剪板). 可以用 CreateClipper来 创建剪贴板.
result = pMyDD->CreateClipper(0, &pMyClipper, NULL);
创建后,把它套到窗口上去, 所以要知道是那一个窗口( Handle).
myWnd = AfxGetMainWnd()->GetSafeHwnd();// 从系统中拿到窗口的 Handle
result = pMyClipper->SetHWnd(0, myWnd);
// 把剪贴板加到窗口上去
result = pMyDDSFront->SetClipper(myClipper);
步骤五: 在后页画图, 前后页互换.
其实到这里才是真正开始写游戏的地方, 以前在 DOS下写游戏, 就是直接从 这个步骤开始的. 以上这些工作, 都是 Microsoft强加给我们的.
写游戏之前我们先来解决前后互换的问题.
// 如果前页的内存被 Windows"征用"了, 这里把它要回来. 这个检察常常会被忘记.
if (pMyDDSFront->IsLost() == DDERR_SURFACELOST)
pMyDDSFront->Restore();
DirectDraw 用来互换的语句有 Blt和 BltFast. BltFast据称比 Blt快10%.
result = pMyDDSFront->Blt(&rcTo, pMyDDSBack, &rcFrom, DDBLT_WAIT,NULL);
result = pMyDDSFront->BltFast( 0, 0, pMyDDSBack, &rcFrom, DDBLTFAST_SRCCOLORKEY);
如果程式工作在"全屏"模式下. 前后页互换容易得多, 只是一句:
result = pMyDDSFront->Flip( NULL, 0 );