AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > VC编程

DirectX揭密

51自学网 2015-08-30 http://www.wanshiok.com

DirectX是一套为Windows程序提供对系统硬件更紧密控制的组件。(表1列出了DirectX 5.0
的组件及其作用)。那么,紧密控制是什么意思呢?

组件 用途
DirectDraw 高速2D图象
DirectSound 短响应时间声音输出
Direct3D 高速3D图象
DirectInput 面向游戏的对游戏杆和其它输入设备的访问
DirectSetup 方便的安装DirectX组件
DirectPlay 面向游戏的通信和网络支持
DirectShow 视频流支持
DirectAnimation 动画录放支持


DirectX提供的硬件控制常常被描述成底层控制,这会使人联想起位操作和其它讨厌的事
情。实际上,DirectX组件包含许多高层API,使得象复制位图和播放声音等复杂的工作变
得相当简单。用"为程序提供比过去更好的对硬件的控制"来形容DirectX更准确。这在
Windows中是一个显著的特性,因为在Windows中,资源是共享的,并由操作系统控制。

DirectX组件遵守称为COM的二进制对象的工业标准。

开始DirectX
下面从DirectX的安装开始讲起。大多数情况下,某个好玩的游戏就会为系统安装DirectX。
为得到最新的版本,应该从最新的Microsoft Platform SDK中将DirectX安装到系统中。
可以在http://www.microsoft.com/msdn站点或者MSDN光盘中找到platform SDK。缺省情
况下,Microsoft Platform SDK被安装到缺省驱动器根目录下的/MSSDK目录中。DirectX
的头文件安装在/MSSDK/INCLUDE目录中,Lib文件安装在/MSSDK/LIB目录中。

Platform SDK包含了一些非常好的DirectX例子和文档。早期发布的DirectX 文档非常粗
略而且有些是错误的,现在的版本已经极大地改正了这一问题。最好要熟悉这些文档。

现在已经为安装利用DirectX的程序做好了准备。所幸的是,不必一次就处理DirectX的全
部功能。DirectX是一套可以分别使用的组件。实际上,在编程概念中,DirectX的不同部
分互相没有联系。它们仅仅是具有相同的设计风格和目标:使Windows的游戏编程变得容易。

使用DirectX组件的程序有什么特殊的地方吗?根本没有。使用DirectX组件的程序是基于
Win32的程序,它们使用普通Win32 API集,并且可以访问所有可以获得的操作系统工具。
实际上,DirectX既可以用于GUI程序,也可以用于控制台程序。可以直接用Petzold-style
SDK编程开发程序,也可以用基本类库,如MFC。总的说,唯一的要求是大多数DirectX组
件在程序中需要HWND,所以至少要有一个窗口。

虽然DirectX组件是分离的,但是每个组件的实现风格和使用都是相同的。DirectInput是
学习DirectX的非常好的出发点,原因是DirectInput是最简单的组件之一。
用力
以后在游戏中要"用力",这是电影《星球大战》中的说法,因为DirectInput中加入了相
当令人陶醉的力反馈支持。DirectX 5.0以前,DirectInput支持从鼠标和键盘读取输入,
这是一个有用但却令人厌烦的特性。DirectX 5.0中,DirectInput被扩充到支持具有以物
理力的形式向用户传播反馈的能力的设备。

如果不能立即理解上面的内容,下面就用一个游戏进行解释。假设你刚启动了你最喜欢的超
现实3D越野赛车游戏,正手握力反馈游戏杆。在起跑线上,你可以听到赛车引擎的空转声,
同时也能够通过游戏杆感觉到赛车引擎的空转!比赛开始后,你可以感觉到引擎高速旋转的
嗡嗡震动。当行驶到赛程中崎岖的地段时,你将会不停的感觉到电子碰撞。赛车在整个赛场
上撞来撞去,你的游戏杆也会如此。赛车车轮卡在车辙中导致赛车被拉向左边,游戏杆也会
被拉向左边!整个过程中你可以感觉到每次颠簸、刮擦、撞击和撞毁。

剖析DirectInput
DirectInput由三个对象组成:DirectInput, DirectInputDevice, 和DirectInputEffect (见
表2)。DirectInput是一个高层的对象,通过DirectInput对象可以对相关的输入设备进行
基本的初始化和查找。DirectInput对象最终用来创建低层的DirectInputDevice对象。
DirectX中的每个主要组件都采用相同的方法,首先创建高层对象,如DirectInput或
DirectSound对象,然后创建低层对象与硬件进行实际的通信。

表2: DirectInput对象
对象 说明
DirectInput 封装高层DirectInput功能,列举设备并用来创建DirectInputDevice对象。
DirectInputDevice 与物理输入设备的接口,例如游戏杆,包括收集和设置设备状态信息的
接口,并且用来创建DirectInputEffect对象 (对于力反馈设备)。
DirectInputEffect 封装能够在力反馈设备上"播放"的简单效果,提供启动、停止和设置
力反馈效果等功能。


DirectInput对象是三个对象中最容易理解的。实际上,它在一个接口形式IDirectInput (见
表3)中只提供五个函数。这是DirectInput的一个非常重要的部分,因为这是出发点。

表3:IdirectInput接口:
成员函数 说明
CreateDevice 创建一个DirectInputDevice对象并返回一个指向其IdirectInputDevice接
口的指针。
EnumDevices 为找到的与给定标准匹配的每个设备调用一个回调函数,每个回调函数提供一
个GUID,可以用在CreateDevice中创建DirectInputDevice对象。
GetDeviceStatus 测试物理设备是否连接到系统。
Initialize 如果DirectInput对象是使用CoCreateInstance创建的,那么在使用前必须调
用Initialize成员。如果DirectInput对象是使用DirectInputCreate创建的,那么就已
经初始化过了。
RunControlPanel 为设备运行Windows Control Panel程序,让用户安装新设备或者更改已
有设备的配置。 游戏杆校准可以在此处做。
创建DirectInput对象
为了创建DirectInput对象并得到其IdirectInput接口指针,应该在程序初始化阶段使用
两种方法之一完成。

第一种方法相当简单。DirectX提供了一个助手函数DirectInputCreate来创建并初始化
DirectInput对象。它与所有DirectInput的函数、接口和宏定义都在头文件DINPUT.H中
声明。实际的函数体在DINPUT.LIB文件中。DirectInputCreate如下定义:

HRESULT WINAPI DirectInputCreate(
HINSTANCE hinst,
DWORD dwVersion,
LPDIRECTINPUT * lplpDirectInput,
LPUNKNOWN punkOuter
);

第一个参数是应用程序的实例。第二个参数是程序需要的DirectInput版本,通常使用
DIRECTINPUT_VERSION宏,定义为当前版本。第三个参数最重要,如果对COM非常陌生的化
就很难理解,它是指向IdirectInput接口的指针的地址。程序中应该定义一个LPDIRECTINPUT
类型的变量(可以是全局的)并将其地址作为第三个参数传递给DirectInputCreate。

最后一个参数叫作punkOuter,与COM技术中的聚合有关,可以用NULL安全的忽略。返回
值是一个HRESULT,是COM的标准返回类型,可以将返回值与可能的返回值比较,也可以使
用COM宏定义SUCCESS或FAILED来检查。

使用DirectInputCreate能够容易地创建高层对象并得到其主接口指针。这是DirectX的又
一个设计方法,每个DirectX组件都提供助手函数来创建高层对象,例如DirectInputCreate
或DirectDrawCreate。在程序中可以用这些助手函数创建DirectX对象,然而,这些函数
实际上创建的是COM对象。这个工作也可以用叫作CoCreateInstance的标准Win32 API函
数来完成。这就引出了创建DirectInput对象的第二中方法。

在Win32中用CoCreateInstance创建COM对象非常普遍。如果程序中已经使用
CoCreateInstance创建了其他COM对象,开发者可能就会希望也用它来创建DirectX对象。
因为COM对象在安装时就在系统中注册过,所以唯一需要知道的就是对象的GUID,用它来
创建一个实例。创建DirectX对象需要的全部GUID都在头文件中声明,并在库文件
DXGUID.LIB中定义。可以将一个预定义的GUID传递给CoCreateInstance,让Windows为你
创建对象。CoCreateInstance定义如下:

STDAPI CoCreateInstance(
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID * ppv
);


第一个参数是要创建对象的GUID,DirectX定义的GUID是叫作CLSID_DirectInput的GUID
结构变量。第二个参数是熟悉的pUnkOuter,同样可以用NULL忽略。第三个参数dwClsContext
定义COM对象在何处创建,DirectX只支持进程内服务器,所以必须使用
CLSCTX_INPROC_SERVER。

第四个参数是两种方法真正的不同之处。记住COM对象对外提供接口,与对象本身一样,接
口也用GUID识别。使用第一种方法,不能选择得到的接口,总是得到IdirectInput。使用
CoCreateInstance可以请求对象所支持的任何接口,方法是使用为接口预定义的GUID。但
是在DirectInput这是没有意义的,因为DirectInput对象的唯一有用的接口就是
IdirectInput。其它DirectX组件支持多个有用的接口。(例如,DirectDraw对象可以用
IdirectDraw或IDirectDraw2接口操作。)

最后一个参数是程序中接口指针变量的实际地址。

现在就拥有了对象和对象的一个接口。CoCreateInstance方法还需要另外一步:必须要首
先调用一个接口函数初始化对象。DirectInputCreate提供的是一个已经初始化过的
DirectInput对象,但CoCreateInstance没有特定于DirectInput的认识,因此必须调用
IdirectInput接口的初始化成员函数。 假设如下定义IdirectInput接口指针变量:

LPDIRECTINPUT g_lpDI

可以如下调用初始化函数:

g_lpDI->Initialize( hInstance, DIRECTINPUT_VERSION);

既然选择采取这种标准方法创建对象,就不得不注意COM需要的其他标准,例如需要调用
CoInitialize和CoUninitialize。
使用DirectInput对象
一旦拥有了DirectInput对象,就可以用它来创建DirectInputDevice对象,来管理系统中
特定的设备。创建DirectInputDevice对象要使用CreateDevice函数,它是作为
IdirectInput接口一部分的五个函数之一。CreateDevice需要所请求设备的GUID,返回新
DirectInputDevice对象的IdirectInputDevice接口指针。

HRESULT CreateDevice(
REFGUID rguid,
LPDIRECTINPUTDEVICE *lplpDirectInputDevice,
LPUNKNOWN pUnkOuter
);

这些内容看起来很熟悉,因为它与CoCreateInstance和DirectInputCreate类似。但是,
现在还没有完全准备好开始DirectInputDevice对象,原因是在创建DirectInputDevice对
象前需要该设备的GUID。

DirectInput库为创建DirectInputDevice对象预定义了两个GUID:GUID_SysKeyboard和
GUID_SysMouse。将两者之一直接传递给CreateDevice函数,就会得到相应设备的
DirectInputDevice对象。

注意,令人感到奇怪的是缺少对游戏杆的预定义GUID。在Windows中,通常都有系统键盘
和系统鼠标,另一方面,系统本身并不使用游戏杆。可以安装一个或者多个游戏杆,但系统
管理的范围只限于驱动程序级。系统并为这些设备指定特殊的系统状态,也不会在日常事务
中使用这些设备。因此,为游戏杆定义GUID对DirectInput来说是不合理的。

那么,如何才能找到与系统连接的游戏杆的GUID呢?要得到它们,必须要列举设备。列举
系统设备和性能在DirectX中相当普遍。要列举系统中的输入设备,需要使用EnumDevices
函数。EnumDevices是IdirectInput接口的一部分,如下定义:

HRESULT EnumDevices(
DWORD dwDevType,
LPDIENUMCALLBACK lpCallback,
LPVOID pvRef,
DWORD dwFlags
);

注意此函数与Windows中其它列举API相同,例如EnumWindows。第二个参数是一个回调函
数。第三个参数是程序中定义的32位值。第一个参数是想要列举的设备类型,对游戏杆来
说,是DIDEVTYPE_JOYSTICK(全部的设备类型列在表4中)。最后一个参数是详细描述想要
列举的设备的标志。现在支持的标志是DIEDFL_ATTACHEDONLY和DIEDFL_ALLDEVICES(这两
个标志是互斥独占的),此外还有DIEDFL_FORCEFEEDBACK,此标志表示力反馈设备,能够和
另两个标志位或操作。

图4:定义列举的输入设备:
以下定义的值可以传递给EnumDevices来选择列举哪种类型的输入设备。另外也支持子类
型,见SDK中DIDEVICEINSTANCE结构的文档。
值 说明
DIDEVTYPE_MOUSE 列举鼠标设备 (标准、轨迹球等)
DIDEVTYPE_KEYBOARD 列举键盘设备 (标准、键区等)
DIDEVTYPE_JOYSTICK 列举游戏杆设备 (操纵杆、操纵轮、方向舵等)
DIDEVTYPE_DEVICE 列举其它设备


当EnumDevices列举系统中的输入设备时,反复地调用回调函数。回调函数定义如下:

BOOL CALLBACK EnumProc(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) ;

因为回调函数是由用户程序定义并传递给EnumDevices的,所以是调用CreateDevice的最
合适地方,直到创建了满足需要的足够DirectInputDevice对象为止。但是回调函数并非一
定要如此实现,可以简单的将列举设备的所有GUID保存在一个表中,在以后的代码中使用。

回调函数接受两个参数。第二个参数是程序定义的传递给EnumDevices的32位值。更重要
的是,第一个参数传递指向一个结构的指针,该结构包含关于能够与列举标准匹配的单个设
备的许多信息。这是一个DIDEVICEINSTANCE结构。此结构中最重要的一条信息是设备的
GUID,保存在结构的guidInstance成员中。

当程序中完全完成DirectInput有关的工作后,就应该调用IdirectInput接口的Release
成员。这就告诉DirectInput对象可以释放自己了。在DirectX中,最好养成释放对象的习
惯,从低层对象开始,到高层对象结束。正常情况下程序会作为清除或者关闭的例行公事的
一部分调用Release。这是使用每个DirectX组件的必要步骤,也是使用每个COM组件的必
要步骤。

<

 

 

 
说明
:本教程来源互联网或网友上传或出版商,仅为学习研究或媒体推广,wanshiok.com不保证资料的完整性。
上一篇:用DirectDraw编写动画程序  下一篇:在VC++6.0下利用消息实现内部进程通讯(IPC)