Chinaunix首页 | 论坛 | 博客
  • 博客访问: 497061
  • 博文数量: 78
  • 博客积分: 5131
  • 博客等级: 大校
  • 技术积分: 1468
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-17 16:20
文章分类
文章存档

2012年(1)

2011年(29)

2010年(6)

2009年(24)

2008年(18)

我的朋友

分类: LINUX

2008-03-26 14:48:10

 我想关于这个主题的文章,不算少,但也不算太多。但大多是分别介绍 DirectDraw 与 DirectInput,而并没有将其结合起来,也许你会问:“分开与合并起来并没有本质区别啊!”。其实的确没有本质区别,但那样使那些最初对游戏编程报有极大热情的爱好者感到非常失望,因为这其中的一个并不能完全满足他们的要求,并且使其感到巨大的阻力,从而失去信心。所以本文将 DirectDraw 与 DirectInput结合起来去讲一个主题就是“游戏编程”,请注意是“游戏编程”,当然这只是一个简单的桌面游戏,但这已经与先前有很大的不同了,这已不是简单的 DirectDraw或 DirectInput编程。我想你现在应该能够体会出其中的区别了。
  声明:在这之前需要你具有一定的 WIN32 API 函数的知识,并且可以熟练使用。和 DirectDraw的知识,关于DirectDraw可以参见 中的 <<动画程序编写——DirectDraw之旅>> 1-3),或其它文章。最后是 c++ 语言,当然也要包括面向对象的那部分。在 Visual C++ .NET 编译环境下进行开发的。
  首先 ,我们还是先简要复习一下DirectDraw的概念吧!
  DirectDraw本质上是显存管理程序。它最重要的性能是允许程序员直接在显存里存储和操纵位图。它使你能够利用视频硬件bliter(位块传输器)在显存内部进行位图的blit(位块传输)。用视频硬件的blitter从显存向显存进行blit比从内存向显存更快。这在64位显卡向显存提供64位数据路径的今天显得尤其重要,硬件独立于促CPU进行位块传输操作,使得CPU得以继续工作。另外DirectDraw支持显卡的其他硬件加速特性,例如对精灵和z -buffering的硬件支持。
  DirectDraw的工作原理
  我们这里还是用图表方式展现给大家吧!
  
 

  细心的朋友可以很明显地注意到图示中的右上角的图解中说明,表面对象有两个宽度,一个是WIDTH,一个是PITCH。WIDTH就是创建表面时所给出的那个宽度,而PITCH是表面的实际宽度,是按字节算的。在许多显卡上,PITCH和WIDTH是相等的,比如在640x480的高彩模式下,PITCH为1280。而在某些显卡上,PITCH比WIDTH要大。比如在640x480的256色模式下,当WIDTH是640时,PITCH为1024而不是640,这些显卡这样做是为了更好地进行数据对齐来提高性能或达到其它目的。所以,我们在实际编程时,为了保证程序的兼容性,必须按PITCH处理。 但这些硬件的底层问题,我们不用太关心,只要稍有了解就可以了。
  下面我们再简要叙述一下,如何使用 DirectX 9.0 中提供的 DirectDraw 类库来创建对象并使用操作对象。
  宏定义在先,定义删除指针和释放对象的宏
  #define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
  #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
  先创建一个 CDisplay 的全局对象
  CDisplay就是ddutil.h中定义的类,用于处理表面之间的拷贝翻页等操作的类,再次定义一个全局变量,用于以后对指向的表面之间进行操作
  CDisplay* g_pDisplay = NULL;
  然后创建表面,当然可以创建很多的表面,这些表面都是离屏表面,在更新画面时,都可以用 CDisplay 类的对象中的方法,将其拷贝到后备缓冲区表面上。只要创建离屏表面,就要用到 CSurface 类。CSurface也是ddutil.h头文件中定义的类,用于对表面本身进行操作,如设置色彩键码,在此定义的图画指针。
  CSurface* g_pBackSurface = NULL;
  DirectX 中就一共用这两个类封装了 DirectDraw 对象的大部分操作,如果你觉得这还不能满足要求,那么你也可以在程序中用 DirectDraw API 函数编写程序,不过在本文中不再介绍。
  这之后,我们会用到 InitDirectDraw 函数。这个函数是我们自己创建的。在此函数中作所有的 DirectDraw 的对象初始化工作。
  HRESULT InitDirectDraw( HWND hWnd )
  {
  HRESULT hr; //接受返回值,其实是long型变量
  LPDIRECTDRAWPALETTE pDDPal = NULL; //定义程序中的调色板
  int iSprite; //定义与sprite个数有关的计数器
  g_pDisplay = new CDisplay(); //动态开辟一个CDisplay类
  if( FAILED( hr = g_pDisplay->CreateFullScreenDisplay( hWnd, SCREEN_WIDTH,
  SCREEN_HEIGHT, SCREEN_BPP ) ) ) /*设置程序为全屏,并且 g_pDisplay 就是动态开辟一个CDisplay类的指针,而在这个类的域中,有一个DirectDraw主表面指针,和一个后备缓冲区表面的指针。在从我建议你可以先去阅读一下 ddutil.h 和 ddutil.cpp 文件。*/
  {
  MessageBox( hWnd, TEXT("This display card does not support 1024x768x8. "),
  TEXT("DirectDraw Sample"), MB_ICONERROR | MB_OK );
  return hr;
  }
  if( FAILED( hr = g_pDisplay->CreatePaletteFromBitmap( &pDDPal, MAKEINTRESOURCE( IDB_DIRECTX ) ) ) ) //顾名思义,就是从bmp图片中获得调色板值,并赋值在pDDPal结构指针所指向的结构体中。
  return hr;
  if( FAILED( hr = g_pDisplay->SetPalette( pDDPal ) ) ) //用刚才从IDB_DIRECTX中获得的调色板制来设置程序调色板
  return hr;
  SAFE_RELEASE( pDDPal );//释放指针,在用过后,一定要释放,这是良好的编程习惯
  // 用IDB_WINXP图片创建一个表面,并用g_pBackSurface指向这个表面
  if( FAILED( hr = g_pDisplay->CreateSurfaceFromBitmap(    &g_pBackSurface, MAKEINTRESOURCE( IDB_WINXP ),
  SCREEN_WIDTH, SCREEN_HEIGHT ) ) )
  return hr;//设置色彩键码为黑色,0代表黑色,这样在表面的拷贝过程中黑色像素的点将不会被拷贝,这样可以产生镂空效果。当然你可以任意设置关键颜色,而颜色的表示法可以用 RGB 宏定义。例如 红色:RGB( 255,0,0 ), 黑色 RGB( 255,255,255 )
  if( FAILED( hr = g_pBackSurface->SetColorKey( RGB( 255,255,255 ) ) ) )
  return hr;
  return S_OK;
  }
  下面的函数是用于更新画面的。
  HRESULT DisplayFrame()
  {
  HRESULT hr;
  g_pDisplay->Clear( 0 ); //清空后备缓冲区表面
  //将g_pBackSurface所指向的图片拷贝到后备缓冲区表面
  g_pDisplay->Blt( 0, 0, g_pBackSurface, NULL );//最关键的地方在这里,请看下面的语句,只要我们一执行翻页操作,就可以将改动了的图像了显示在屏幕上了
  if( FAILED( hr = g_pDisplay->Present() /*翻页操作*/) )
  return hr;
  return S_OK;
  }
  下面的函数是用于在程序失去焦点时调用的。
  HRESULT RestoreSurfaces()
  {
  HRESULT hr;
  LPDIRECTDRAWPALETTE pDDPal = NULL; /*当程序失去焦点,要保存当前的画面,请注意这里,g_pDisplay->GetDirectDraw()函数返回的才是真正的 DirectDraw 对象 */
  if( FAILED( hr = g_pDisplay->GetDirectDraw()->RestoreAllSurfaces() ) )
  return hr;//在此我们还要重新创建调色板
  if( FAILED( hr = g_pDisplay->CreatePaletteFromBitmap( &pDDPal, MAKEINTRESOURCE( IDB_DIRECTX ) ) ) )
  return hr;//重新设置调色板
  if( FAILED( hr = g_pDisplay->SetPalette( pDDPal ) ) )
  return hr;
  SAFE_RELEASE( pDDPal );//重新画出图画
  if( FAILED( hr = g_pLogoSurface->DrawBitmap( MAKEINTRESOURCE( IDB_WINXP ),
  SPRITE_DIAMETER, SPRITE_DIAMETER ) ) )
  return hr;
  return S_OK;
  }
  下面这个函数是释放表面指针所用的。
  VOID FreeDirectDraw()
  {
  SAFE_DELETE( g_pBackSurface );
  SAFE_DELETE( g_pDisplay );
  }
  我们的回顾到此结束,下面我们开始本文要介绍的一个关键技术,DirectInput 的使用。
  游戏编程可不仅仅是图形程序的开发工作,实际上包含了许多方面,本文所要讲述的就是关于如何使用 DirectInput 来对键盘编程的问题。
  而我们为什么要选择用 DirectInput 来处理游戏中的输入问题呢?其实用 Win32 API 函数也完全可以处理这些工作,例如其中,有一个
  GetAsyncKeyState() 的函数可以返回一个指定键的当前状态是按下还是松开。这个函数还能返回该指定键在上次调用 GetAsyncKeyState() 函数以后,是否被按下过。虽然这个函数听上去很不错,但需要我们自己轮换查询每个键盘的状态。而在 DirectInput 中我们已经可以脱离这些烦琐的工作,只因它的功能更强大。
  由于本文重点在二者的结合,故在此只介绍 DirectInput 中比较简单的,而且最容易上手的立即模式的工作方式。
  而这里我们要用到 DirectInput 的 API 函数。有人会问,为什么在 DirectDraw 中用 DirectX 提供的类库编程,而对于 DirectInput 却直接使用要用其 API 函数呢,是因为没有提供 DirectInput 的类库吗?不是!而是因为使用类库并不很方便而且不灵活。
  OK,让我们开始我们游戏编程的第二部——DirectInput编程。
  前面讲 DirectDraw 时,并没有提到,微软是按 COM 来设计DirectX的,所以就有了一个 DIRECTINPUT 对象来表示输入设备,而某个具体的设备由 DIRECTINPUTDEVICE 对象来表示。也许会感到很无奈,怎么游戏编程需要这么多的知识啊,其实您也无需烦恼,只要知道一下就可以了,其实这并不;影响您的设计,而且就算您不知道,也同样可以驾驭DIRECTINPUT。
  实际的建立过程是先创建一个 DIRECTINPUT 对象,然后在通过此对象的 CreateDevice 方法来创建 DIRECTINPUTDEVICE 对象。
  #include
  #define DINPUT_BUFFERSIZE 16
  LPDIRECTINPUT lpDirectInput; // DirectInput 对象实际上是一个
阅读(1315) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~