游戏艺术工厂

首页 » 游戏制作交流 » 游戏程序 » 从头学习DirectDraw
xxb - 2007-8-14 9:30:00
在开始学习DirectDraw编程之前,有一些题外话要说明,以下内容均是个人的心得和体会,如果其中有什么谬误之处,敬请谅解,同时个人不对可能造成的后果负责。。

  以下几点是在编制DirectX应用程序时应该注意的:
   


DirectDraw是什么?

  DirectDraw是DirectX中的关于视频输入输出的基本部分,使用DirectDraw可以方便地编制出高效的视频处理程序,只要用户的硬件支持DirectDraw,就能保证你的代码可以处理它们。
  简单地说,一个DOS程序员可以方便地直接访问视频显存,从而高效地处理视频动画,而在Windows的32位环境中,使用DirectDraw可以做类似的工作(而且做得更好)。例如,在DOS环境中的320x200x256色图形模式中,你可以通过在地址A000:0000开始处的一片内存区进行直接读写的方式来处理视频操作,而在Windows环境中,DirectDraw也提供一片内存区供你直接读写,使你能更好地完成相似的操作。
  当然,DirectDraw提供的远不止这些,但一个游戏程序员可能更关心如何直接访问视频显存,以及如何高效地完成位块拷贝操作等等。


让我们从消息循环开始

  DirectX最初是为游戏开发而推出的,编制游戏的程序员都很贪婪,他们会尽量榨取系统资源,并试图让自己的程序永远具有最高的效率。但Windows是一个多任务的操作系统,当它发现所有的程序都处于空闲时,便会减少给这些程序的资源,其中之一就是开始清理交换文件,为了让自己的程序给Windows以始终繁忙的假象,不妨用一些新的代码来代替常规的方法。

这是常规的消息循环处理

while(GetMessage(&msg,NULL,NULL,NULL)){

    TranslateMessage(&msg);

    DispatchMessage(&msg);

}

return msg.wParam;



//---------------



这是改进的消息循环处理

for(;;){

    if(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE)){

        if(msg.message==WM_QUIT) break;

        TranslateMessage(&msg);

        DispatchMessage(&msg)

    }else{

        if(AppPaused) WaitMessage();

    else{

        // 这里进行任何不基于消息循环的处理

        // 例如动画制作

        }

    }

}

return msg.wParam;

  上例中,新的消息循环处理方式使得Windows认为程序始终繁忙,从而提高了程序的性能。注意,其中的AppPaused变量为真时表示程序未在前台运行,该变量的取值为WM_ACTIVATEAPP消息的wParam参数。


第一个DirectDraw程序

  现在咱们开始编制第一个DirectDraw的应用程序,来示例如何建立一个全屏幕独占方式的应用程序,并利用HDC来实现简单的文本输出。
  这个示例与微软的示例程序不同,它使用了DirectX 5.0中的新接口,在制作最终可执行程序时应确保为LINK程序指定ddraw.lib和dxguid.lib这两个文件。
  示例程序的可执行程序由VC 5.0生成,只在基于Intel奔腾芯片、Windows95环境的机器上才能运行。

  下面重点介绍一下建立和销毁DirectDraw基本对象的方法,以及如何请求新接口。
首先来说明一下以后要用到的术语:

   

  在示例程序一中,从WinMain函数开始,在登记了窗口类并建立了程序主窗口后,就开始建立DirectDraw的基本对象了:

ddrval = DirectDrawCreate(NULL, &_lpDD, NULL);

if (ddrval!=DD_OK){

    // 失败

}

  函数DirectDrawCreate在成功时返回DD_OK(零),若是失败则返回一个32位(HRESULT)的错误代码。不止是这个函数,所有DirectX对象的成员函数(方法)均采用这种方式来返回表示成功或失败的值。
  不必去管该函数的第一个和第三个参数,现在只需要把它们设为空值即可,而第二个参数是一个指针,这个指针指向了一个LPDIRECTDRAW类型的变量,而该变量实际上也是一个指针,如果函数成功则指向IDirectDraw对象实体--------是不是有一点绕来绕去的?
  当基本对象成功建立后,就需要将它“变”成较新的对象以增进性能,这也正是本示例程序为什么需要5.0版本的DirectX来支持的缘故。

ddrval=_lpDD->QueryInterface(IID_IDirectDraw2, (void **)&lpDD);

_lpDD->Release();

if (ddrval!=DD_OK){

    // 失败

}

  下一步是利用新建立的基本对象来建立与该基本对象相关联的主平面,这个主平面实际上就是可视的屏幕,所有对主平面的访问都直接反映在屏幕。在建立主平面之前我们首先将基本对象的属性设为全屏幕独占方式,这样才能改变显示模式,以及建立可“弹出”的平面集。具体的设置方法参见源程序。
  在设置了全屏幕独占方式并设置了适当的显示模式之后,程序建立了一个主平面,这个主平面附带一个后台缓冲区,程序员可以使用“设备相关把柄(HDC)”或直接访问的方式(以后再讲)来对后台缓冲区进行图形操作,所有对后台缓冲区的操作并不能反映在屏幕上,在图形操作完成后,可以利用“弹出”操作来将后台缓冲区“弹”至主平面,从而在屏幕上显示出来。
  本示例程序在进行图形操作示例时,首先获取后台缓冲区的HDC,然后用标准的GDI函数来绘制文本,一旦绘制完毕,就立刻释放刚获取的HDC,因为任何一个平面的HDC被获取后,该平面就被隐式地锁定,无法进行位块拷贝或弹出操作,只有当该HDC被释放后,一个隐式的Unlock才被调用,程序才能对该平面进行前述操作。
  完成后台缓冲区的图形绘制后,程序调用主平面的成员函数(方法)Flip来将后台缓冲区弹至主平面,你将看到屏幕内容已被改变。

  这是我个人搞的一个小封装,可用于非C++的编程语言(如Delphi)。使用此封装库可比较容易地编制DirectDraw应用程序。此封装为静态链接库,提供Visual C++和BorlandC++两种版本,其中包括:
    Visual C++版库文件
    Borland C++版库文件
    相关的头文件
    两个示例源程序
    说明文档(HLP文档格式)

1
查看完整版本: 从头学习DirectDraw