您的位置:首页 > 编程语言

2D游戏编程笔记-3

2010-04-10 11:54 190 查看
本人同意他人对我的文章引用,但请在引用时注明出处,谢谢.作者:蒋志强

在上一次的笔记中,我们定义了一个全局变量LPDIRECTDRAW7 p_Ddraw ,其中LPDIRECTDRAW是该变量的类型,在VC编程序中会遇到很多的变量类型,它们都是在头文件中通过宏进行定义的。我们不可能都能够将其名字记忆准确。为了帮助我们在编程时正确的拼写变量名字,我们要使用一个名为Visual Assist的插件程序。事实上,在写用VC做开发时,程序员们几乎都无一例外的使用Visual Assist来帮助提高效率。其实在新版本的VS中(包括VS2003,VS2005),microsoft都为其C#的开发添加了一种叫做"智能感应"的技术,实现和Visual Assist相同的功能(我个人感觉做得比Visual Assist更好),但对于C/C++却没有实现该技术,这让我很是困惑不解,所以我们甚至在使用VC.NET 2005时,仍需要安装Visual Assist插件。

我们可以从网上找到Visual Assist X V10的版本,安装界面如下图所示

一路点Next/OK安装完成后,重新打开VC6会发现左边多了一个VA View的标签,这个标签就是Visual Assist插件所加上去的,同时工具栏也增加了相应的按钮.如下图所示:

OK,有了Visual Assist的帮助,现在我们可以进行我们真正的工作了.我们要做的事情可以分为以下几步:

1.创建DirectDraw对象;

2.设置控制级别和显示模式;

3.创建页面;

4.装载图片到页面;

5.与GDI一起工作;

6.换页处理;

我们开始写程序的时候,经常会遇到很多问题,这个时候你应该去查询DX的 Document或者是MSDN,你甚至可以在http://resource.gameres.com/惊喜的发现DDraw的中文文档(当然这是热心的网友们翻译的)。

创建DirectDraw对象是通过

HRESULT WINAPI DirectDrawCreate(

GUID FAR *lpGUID,

LPDIRECTDRAW FAR *lplpDD,

IUnknown FAR *pUnkOuter );

函数完成的。第一个参数是显卡的GUID,如果你的机器只有一块显卡(大多数机器都是这种情况),你直接输入NULL作为实参就可以了。第二个参数是指向IDirectDraw接口的指针的地址(就是指针的指针),你首先要定义一个IDirectDraw接口类型的指针 LPDIRECTDRAW7 p_DDraw; 将其地址&p_DDraw作为参数传递,要注意的是你必须要将&p_DDraw进行强制类型转换,转换为(void**)类型。第三个参数为以后扩展所保留的,现在没有任何用,我们直接传递NULL就可以了。创建DirectDraw对象还可以使用另外一个函数

WINAPI DirectDrawCreateEx(

GUID FAR * lpGuid,

LPVOID *lplpDD,

REFIID iid,

IUnknown FAR *pUnkOuter );

,该函数比老的DirectDrawCreat多一个REFIID iid参数,该参数用于指定创建的DirectDraw对象的版本,我们是要创建DDraw7的版本,所以应该传递IID_IDirectDraw7作为参数。

现在我们可以使用下面的代码完成DirectDraw对象的产生:

LPDIRECTDRAW7 p_DDraw;

if(DirectDrawCreate(NULL,&p_DDraw,NULL) != DD_OK)

return FALSE;

产生了DirectDraw对象以后,我们就应该对它的控制级别和显示模式进行设置了。进行协作级别设置和显示模式设置分别使用IDirectDraw接口中的SetCooperativeLevel和SetDisplayMode这两个方法。

DirectDraw程序进行图像处理有两种方式,全屏和窗口。在全屏模式下,显示设备被我们的应用程序所独占,在窗口模式下,我们的程序运行在窗口状态下,其他的应用程序还是可以在显示设备上显示。因为窗口模式需要考虑其它的程序,所以不能对显示设备“为所欲为”的 操作,比全屏模式要复杂。我们先从较简单的入手,使用全屏模式进行编程。函数

HRESULT SetCooperativeLevel(

HWND hWnd,

DWORD dwFlags

);

进行控制级别的设置,第一参数是进行DDraw操作的窗口的句柄。第二个参数指定协作级别,我们DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN作为参数。DDSCL_EXCLUSIVE表示程序独占显示设备,DDSCL_FULLSCREEN表示程序全屏显示,使用或操作|表示程序独占设备而且全屏显示(写过SDK程序的朋友对这种或运算连接几个参数的方式应该是很熟悉的)。为什么我知道是传递这样的参数呢?因为我查阅了DX的Document,我再次强调查阅文档非常非常重要,一定要学会查阅文档来解决问题(learn how to learn)。我到现在为止都讲得很详细,随着笔记得继续,我将加快速度,过于细节的东西大家要到文档去查找。

函数

HRESULT SetDisplayMode(

DWORD dwWidth,

DWORD dwHeight,

DWORD dwBPP,

DWORD dwRefreshRate,

DWORD dwFlags

);

进行显示模式的设置。前两个参数分别指定显示的水平和垂直方向的分辨率,第三个参数是色深,第四个是刷新率,如果传递0为参数就使用当前显示设备的刷新率,第五个参数我们用不到,直接传递0就可以了。水平/垂直分辨率和色深应该是显卡能够支持的显示模式,如果显卡不支持,则函数的返回值就不会是DD_OK。

现在我们可以使用下面代码对DirectDraw对象的控制级别和显示模式进行设置:

if(p_DDraw->SetCooperativeLevel(mainWindow,DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN) != DD_OK)

return FALSE;

if(p_DDraw->SetDisplayMode(800,600,32,0,0) != DD_OK)

return FALSE;

在这里我们选择了800*600 32bit的显示模式,因为该模式可以被绝大多数显卡支持。完成了DDraw的创建和初始化以后,我们就应该创建页面(也就是Surface)了。页面这个概念,我们可以理解为一张纸,我们可以在上面作绘图操作。我们要创建的页面的类型会有3种:主页面,它代表了当前的屏幕显示;后台页面,是当前暂时不可见的页面,在进行换页操作后,主页面变为后台页面,后台页面链中的一个页面变为当前可见的主页面;离屏页面,是一种始终都不可见的页面,主要是用来读取图片资源,然后blit到后台页面上。

我们先来创建主页面,这是必不可少的代表显示屏幕的页面。为了要描述所创建的页面的各种属性,我们要创建一个DDSURFACEDESC2类型的结构体变量,下面的语句完成了该结构体的填充。

DDSURFACEDESC2 frontSurfaceDes;

ZeroMemory(&frontSurfaceDes,sizeof(frontSurfaceDes));

frontSurfaceDes.dwSize = sizeof(frontSurfaceDes);

frontSurfaceDes.dwFlags = DDSD_CAPS|DDSD_BACKBUFFERCOUNT;

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

frontSurfaceDes.dwBackBufferCount = 1;

第一句新建了一个名为frontSurfaceDes的DDSURFACEDESC2 类型的结构体变量。第二句的ZeroMemory函数将该结构体变量的成员全部清0(因为新建的变量的内部数据是随机的值,不清0的话有可能会产生莫名其妙的问题)。该结构体的dwSize 字段要填写该结构体的大小;dwFlags 字段指明该结构体的哪些字段将要被填写(除了dwSize 和dwFlags 字段以外)。DDSURFACEDESC2 结构体中的ddsCaps字段本身也是一个结构体(见ddraw.h中的定义):

typedef struct _DDSCAPS2

{

DWORD dwCaps; // capabilities of surface wanted

DWORD dwCaps2;

DWORD dwCaps3;

union

{

DWORD dwCaps4;

DWORD dwVolumeDepth;

} DUMMYUNIONNAMEN(1);

} DDSCAPS2;

我们需要填充该结构体的dwCaps成员,描述需要创建的页面的属性。因为我们创建的页面是主页面,提供翻页支持,并且允许由多个页面复合组成页面链,所以我们填充为DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX 。我们在换页链中的后台页面只要一个,所以将dwBackBufferCount 设置为1就可以了。完成了这个比较麻烦的结构体的填充后,我们就可以创建所指定特性的页面了。但我们要首先通过LPDIRECTDRAWSURFACE7 p_FrontSurface;语句定义一个可以指向页面的指针。然后我们使用下面的代码来创建我们的主页面:

if(p_DDraw->CreateSurface(&frontSurfaceDes,&p_FrontSurface,NULL) != DD_OK)

return FALSE;

一个DirectDraw对象只能有一个主页面,我们使用DirectDraw对象的IDirectDraw接口中的CreateSurface方法来创建页面。该方法的第一个参数是我们刚才填充的那个结构体的地址 &frontSurfaceDes ;第二个参数是页面指针的地址(即指向指针的指针)&p_FrontSurface ;如果创建页面对象成功,则该方法将让该页面指针指向所创建的页面对象。最后一个参数是设计来用于以后扩展使用的,现在没有任何作用,我们使用NULL就可以了。OK,我们现在已经成功创建了主页面。但我们在主页面中声明了,我们将有一个后台页面与主页面组成换页链。下面我们来创建后台页面。

创建后台页面业需要填充结构体来说明所创建的页面特性,用于后台页面属性说明的是DDSCAPS2结构体。在创建主页面时候,我们提到了该结构体, DDSURFACEDESC2 结构体的一个成员就是 DDSCAPS2 结构体。我们可以使用下面的代码来创建后台页面:

DDSCAPS2 backSurfaceDes;

ZeroMemory(&backSurfaceDes,sizeof(frontSurfaceDes));

backSurfaceDes.dwCaps = DDSCAPS_BACKBUFFER;

if(p_FrontSurface->GetAttachedSurface(&backSurfaceDes,&p_BackSurface) != DD_OK)

return FALSE;

第一条语句生成了一个DDSCAPS2 类型的结构体变量第二条语句将该变量内部的数据全部清0。我们需要填写的只是DDSCAPS2 结构体变量中的dwCaps字段,因为我们要创建的是后台页面所以我们用DDSCAPS_BACKBUFFER设置该字段。因为后台页面是隶属于主页面的,所以可以用IDirectDrawSurface接口中的GetAttachedSurface方法来创建后台页面,并与主页面组成换页链。该方法的第一个参数是我们刚刚填写的这个结构体的地址,第二个参数是所创建的后台页面指针的地址(又是指向指针的指针,这次大家应该都很清楚了吧)。后台页面指针的产生与主页面指针产生是完全一样的,语句LPDIRECTDRAWSURFACE7 p_BackSurface将产生这样的指针。如果该方法执行成功,生成了后台页面,则将我设置该后台指针p_BackSurface指针指向该页面对象。

到现在为止,我们已经完成了DirectDraw组件初始化工作。下面我们要做的是将使用GDI在后台页面进行绘图操作,使用GDI绘图必须要得到一个HDC,我们可以使用IDirectDrawSurface接口中提供的GetDC方法来得到HDC,然后使用该HDC进行绘图。如下面的代码所示:

if(p_BackSurface->GetDC(&hdc) != DD_OK)

return FALSE;

得到了HDC后,我们就可以使用GDI绘图了,我们可以像是在传统的窗口上绘图一样来使用该HDC绘图,不同的是现在所进行的绘图操作结果不直接显示在屏幕上,而是作用在当前暂时不可见的后台页面上。想看到绘图的结果,必须进行换页操作,将后台页面换为主页面时,绘制的结果才可以见到。我们使用红色画刷将后台页面涂为红色,具体的代码如下:

RECT clientRect2;

GetClientRect(mainWindow,&clientRect2);

HBRUSH myBrush2 = CreateSolidBrush(RGB(0,255,0));

FillRect(hdc,&clientRect2,myBrush2);

p_BackSurface->ReleaseDC(hdc);

我们注意到最后一条语句调用了IDirectDrawSurface4接口提供的ReleaseDC方法来释放所获得的HDC,我们在得到HDC完成绘图工作以后要记得使用该方法来进行释放(上了厕所后,要记得冲水哦^_^)。为了让我们看到绘图产生得结果,我们可以设置一个定时器(如果你不清楚定时器的使用赶紧去看看《Windows程序设计》吧),每隔一定时间(比如500ms),产生一个WM_TIMER消息,在定时器消息的处理部分进行换页操作。因为主页面没有进行绘图,所以将是初始的全白色的状态。每次进行换页操作将使主页面和后台页面交换,这样我们将会看到屏幕周期性的全白和全红。下面的语句实现了换页:

p_FrontSurface->Flip(NULL,DDFLIP_WAIT);

该方法的第一个参数是指定将哪个后台页面换到前台,如果使用NULL,则表示按照后台页面创建的先后顺序从换页链中取出最前面的后台页面与主页面进行换页。换页以后,以前的主页面,将自动成为后台页面,并排在换页链的最后面。第二个参数指定进行换页操作时的参数,我们一般使用DDFLIP_WAIT,表示如果换页操作没有成果则将继续重复尝试进行该换页操作。

虽然在说法上叫做换页,事实上页面的位置并没有任何的改变,DirectDraw所进行操作仅仅是改变了页面指针的指向(而没有实际上去移动页面),当主页面指针指向某个页面时,这个页面就是主页面,显示器上将显示该页面的内容,所以换页操作的效率是非常高的。换页这种说法只是逻辑上的描述,这种逻辑关系如下图所示:

哇,这次笔记的内容终于完成了。没想到居然写了这么长,我的老师给了我不少资料要看,还真有得忙的。但笔记当然会继续的啥,下次的笔记内容将会更有趣的。

篮球世竞赛的半决赛,美国输给了希腊被淘汰掉了,欧洲的篮球水平真是提高很快呀,NBA的国际化也为欧洲培养了很多优秀球员,我想美国人对此也很矛盾吧。NBA集中全球最优秀的篮球运动员的同时,也在培养自己的敌人^_^!。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gamer_gerald/archive/2006/08/19/1097902.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: