夜深人静做游戏---之二

第二章 将游戏进行到底

从这一章开始我们正式进入游戏编程的部分,正确的说法是开始一起研究和学习Direct编程,有了上面win32编程的基础上,我们实际上就已经建立了容纳我们这个游戏的空间,这个空间里面我们就可以实现Direct编程,通过调用Direct里面微软给我们封装好的API函数,我就可以达到对硬件的操作,从而制作出自己的游戏世界.

2.1开天辟地

有了框架,我们就可以在里面动手画游戏了, DirectDrawDirectX的重要组成部分,它就像一支画笔,主要负责各种把各种图像显示在屏幕上,对Windows环境中的游戏非常重要。

首先我们来定义这只笔,说得专业一点就是初始化DirectDraw.

我们先写一个初始化函数(在我的demo里面你不能找到下面这个函数,那是因为我把初始化写在了地图初始化里面,具体就是void map::init(),其实你仔细看他们是完成同样的功能).

LPDIRECTDRAW7 lpDD; // DirectDraw对象的指针

LPDIRECTDRAWSURFACE7 lpDDSPrimary; // DirectDraw主页面的指针

LPDIRECTDRAWSURFACE7 lpDDSBuffer; // DirectDraw后台缓存的指针

LPDIRECTDRAWSURFACE7 lpDDSBack; // 存放背景图的页面的指针 

BOOL InitDDraw( )

{

     DDSURFACEDESC2 ddsd; // DirectDraw的页面描述

     if ( DirectDrawCreateEx (NULL, (void **)&lpDD, IID_IDirectDraw7, NULL) != DD_OK )

     return FALSE; //创建DirectDraw对象

    //这里使用了 if ( xxx = DD_OK) 的方法进行错误检测,这是最常用的方法

if(lpDD->SetCooperativeLevel(hwnd,DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN) != DD_OK )

return FALSE; //设置DirectDraw控制级

if(lpDD->SetDisplayMode(640,480,32, 0, DDSDM_STANDARDVGAMODE ) != DD_OK )

return FALSE; //设置显示模式

  //开始创建主页面,先清空页面描述

  memset(&ddsd, 0, sizeof(DDSURFACEDESC2));

  //填充页面描述

ddsd.dwSize = sizeof( ddsd );

  ddsd.dwFlags = DDSD_CAPS|DDSD_BACKBUFFERCOUNT; //有后台缓存

ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX;

    ddsd.dwBackBufferCount = 1; //一个后台缓存

     if ( lpDD->CreateSurface( &ddsd, &lpDDSPrimary, NULL ) != DD_OK )

return FALSE; //创建主页面

    ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; //这是后台缓存

if ( DD_OK != lpDDSPrimary->GetAttachedSurface( &ddsd.ddsCaps, &lpDDSBuffer ) )

       return FALSE; //创建后台缓存

    ddsd.dwSize = sizeof( ddsd );

    ddsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT;

    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; //这是离屏页面

    ddsd.dwHeight=480; //

    ddsd.dwWidth=640; //

    if ( DD_OK != lpDD->CreateSurface( &ddsd, &lpDDSBack, NULL ) )

       return FALSE; //创建放背景图的页面

    //如还有别的页面可在此处继续创建

    return TRUE;

}

呵呵,看不懂了吧,看不懂就对了,我还没有讲你怎么能看得懂呢,那下面我一个一个函数的讲讲吧.

在这个初始化函数里面我们做了很多工作,包括申明各种指针,创建directdraw对象,设置控制级和显示模式,创建页面(主页面和副页面),具体解释一下这些东东。

DirectDraw对象

如果要使用DirectDraw,必须创建一个DirectDraw对象,它是DirectDraw接口的核心。用DirectDrawCreateEx( )函数可以创建DirectDraw对象,DirectDrawCreateEx( )函数是在ddraw.h中定义的,它的原型如下: 

HRESULT WINAPI DirectDrawCreateEx(

  GUID FAR *lpGUID,

  LPVOID *lplpDD,

  REFIID iid,

  IUnknown FAR *pUnkOuter

);  

第一个参数lpGUID:指向DirectDraw接口的全局唯一标志符(Global Unique IDentify)的指针。在这里,我们给它NULL,表示我们将使用当前的DirectDraw接口。

第二个参数lplpDD:这个参数是用来接受初始化的DirectDraw对象的地址。在这里,我们给它用强制类型转换为void**类型的&lpdd(传递指针的指针,这样这个函数才能改变指针的指向)。

第三个参数iid:给它IID_IDirectDraw7吧,表示我们要创建IDirectDraw7对象。

第四个参数pUnkOuter:目前必须是NULL

所有的DirectDraw函数的返回值都是HRESULT类型,它是一个32位的值。函数调用成功用 "DD_OK"表示,所有的错误值标志开头都为"DDERR",如:

DDERR_DIRECTDRAWALREADYCREATED

DDERR_OUTOFMEMORY

我们一般用"if ( DirectDrawCreateEx (NULL, (void **)&lpDD, IID_IDirectDraw7, NULL) != DD_OK ) return FALSE;"来创建DirectDraw对象,这样当创建不成功时就会退出函数并返回FALSE

设置控制级和显示模式

DirectDrawCreate函数调用成功后,lpDD已经指向了一个DirectDraw对象,它是整个DirectDraw接口的最高层领导,以后的步骤都是在它的控制之下。

我们用IDirectDraw7::SetCooperativeLevel( )来设置DirectDraw程序对系统的控制级。它的原型如下:

HRESULT SetCooperativeLevel (HWND hWnd, DWORD dwFlags )

第一个参数是窗口句柄,我们给它hWnd,使DirectDraw对象与主窗口联系上。

第二个参数是控制级标志。这里使用DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN,表示我们期望DirectDraw以独占和全屏方式工作。

控制级描述了DirectDraw是怎样与显示设备及系统作用的。DirectDraw控制级一般被用来决定应用程序是运行于全屏模式(必须与独占模式同时使用),还是运行于窗口模式。但DirectDraw的控制级还可设置如下两项:

(1)允许按Ctrl + Alt + Del重新启动(仅用于独占模式,为DDSCL_ALLOWREBOOT)。

(2)不允许对DirectDraw应用程序最小化或还原 (DDSCL_NOWINDOWCHANGES)

普通的控制级(DDSCL_NORMAL)表明我们的DirectDraw应用程序将以窗口的形式运行。在这种控制级下,我们将不能改变显示器分辨率,或进行换页操作(这是一个重要的操作,在4.2节会介绍)。除此之外,我们也不能够调用那些会对显存产生激烈反应的函数,如第五章要讲的Lock( )

当应用程序为全屏并且独占的控制级时,我们就可以充分的利用硬件资源了。此时其它应用程序仍可创建页面、使用DirectDrawGDI的函数,只是无法改变显示模式。

下一步我们使用IDirectDraw7::SetDisplayMode( )来设置显示模式,其原形为:

HRESULT SetDisplayMode(DWORD dwWidth,

DWORD dwHeight,

DWORD dwBPP, 

DWORD dwRefreshRate,

DWORD dwFlags

);

dwWidth and dwHeight用来设置显示模式的宽度和高度。

dwBPP用来设置显示模式的颜色位数。

dwRefreshRate设置屏幕的刷新率,0为使用默认值。

dwFlags现在唯一有效的值是DDSDM_STANDARDVGAMODE

DWORD dwFlags

);

dwWidth and dwHeight用来设置显示模式的宽度和高度。

dwBPP用来设置显示模式的颜色位数。

dwRefreshRate设置屏幕的刷新率,0为使用默认值。

dwFlags现在唯一有效的值是DDSDM_STANDARDVGAMODE

DWORD dwFlags

);

dwWidth and dwHeight用来设置显示模式的宽度和高度。

dwBPP用来设置显示模式的颜色位数。

dwRefreshRate设置屏幕的刷新率,0为使用默认值。

dwFlags现在唯一有效的值是DDSDM_STANDARDVGAMODE

DWORD dwFlags

);

dwWidth and dwHeight用来设置显示模式的宽度和高度。

dwBPP用来设置显示模式的颜色位数。

dwRefreshRate设置屏幕的刷新率,0为使用默认值。

dwFlags现在唯一有效的值是DDSDM_STANDARDVGAMODE
创建页面

    下一步是创建一个DirectDrawSurface对象。

DirectDrawSurface对象代表了一个页面。你可以把页面想象为一张张可供DirectDraw描绘的画布。页面可以有很多种表现形式,它既可以是可见的,称为主页面(Primary Surface);也可以是作换页用的不可见页面,称为后台缓存(Back Buffer),在换页后,它成为可见(换页在4.2节会讲);还有一种始终不可见的,称为离屏页面(Off-screen Surface),它的作用是存储图像。其中,最重要的页面是主页面,每个DirectDraw应用程序都必须创建至少一个主页面,一般来说它就代表着我们的屏幕。

创建一个页面之前,首先需要填充一个DDSURFACEDESC2结构,它是DirectDraw Surface Description的缩写,意思是DirectDraw的页面描述。它的结构非常庞大,这里只能作一个最简单的介绍。要注意的是在填充此结构前一定要将其清空!下面是一个典型的主页面的页面描述:

   ddsd.dwSize = sizeof( ddsd ); //dwSize页面描述的大小

   ddsd.dwFlags = DDSD_CAPS|DDSD_BACKBUFFERCOUNT; //有后台缓存

ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX; //为主页面,有后台缓存,有换页链

   ddsd.dwBackBufferCount = 1; //一个后台缓存

 

 

    再看看一个普通表面的页面描述:

   ddsd.dwSize = sizeof( ddsd );

   ddsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT; //高、宽由我们指定

   ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; //这是离屏页面

   ddsd.dwHeight=480; //

   ddsd.dwWidth=640; //

 

 

    页面描述填充完毕后,把它传递给IDirectDraw7::CreateSurface( )方法即可创造页面。CreateSurface( )的原形是:

 

 

HRESULT CreateSurface(

    LPDDSURFACEDESC2 lpDDSurfaceDesc,        

    LPDIRECTDRAWSURFACE FAR *lplpDDSurface, 

    IUnknown FAR *pUnkOuter

);

 

 

CreateSurface( )函数的第一个参数是被填充了页面信息的DDSURFACEDESC2结构的地址,此处为&ddsd;第二个参数是接收主页面指针的地址,此处为&lpDDSPrimary;第三个参数现在必须为NULL,为该函数所保留。

如果函数调用成功,lpDDSPrimary将成为一个合法的主页面对象。由于在前面已经设置了该程序的工作模式为独占和全屏,所以,此时主页面所代表的实际上是我们的整个显示屏幕。在主页面上所绘制的图形将立即反映到我们的显示屏幕上。

DirectDraw初始化函数最后创造了一个离屏页面,如果我们想创造更多的页面,由于页面描述已被填充好,只需接着它像下面这样先设置高度和宽度再创建页面即可:

ddsd.dwHeight=XXX;

ddsd.dwWidth=XXX;

if ( DD_OK != lpDD->CreateSurface( &ddsd, &lpDDSABC, NULL) )

   return FALSE;

       ok, 初始化完成以后,一个 Directdraw 对象我们就建立起来了,然后我们可以更具上面的各个参数的不同定义修改其中的内容,达到不同的我们想要的显示效果。如果你觉得上面讲的太繁琐 , 建议你看着我的源代码里面的地图初始化 void map::init() 部分 , 比较着来看上面的内容你就明白是怎么回事了 .
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值