DirectInput简介
1. DirectInput的得名是因为它直接与设备驱动器通讯,对鼠标和键盘也就意味着立即响应硬件中断,而不是等待Windows发送消息证明已发生输入事件
2. DirectInput概念
(1) 设备
1. 键盘
2. 鼠标(触摸板、跟踪球以及相应的按键)
3. 游戏杆
设备可以指物理对象(如其驱动器)也可以指DirectInput创建的DirectInputDecive对象,当创建一个设备时,我们指获得一个指向IDirectInputDevice接口的指针
(2) 按钮和轴
3. 设置DirectInput:简单地调用DirectInputCreate函数就可以初始化DirectInput系统,此函数将返回一个指向IDirectInput接口的指针,然后就可以用此接口的方法列举设备并创建设备对象,这些设备对象是DirectInput的工作部分
4. 列举设备:一般不需要列举鼠标和键盘,游戏则属于另一种情况(EnumDevices方法)
5. 设置设备
(1) 创建设备:CreateDevice方法
选定一种输入设备或由用户选出一种后,接下来可以为其获取一个接口,继续工作之前需要设备的实例GUID,如果没有在列举时从DirectInput传给回调函数DIDEVICEINSTANCE结构的guidInstance成员中获取这一GUID,则需要使用预定义变量GUID_SysKeyboard或GUID_SysMouse
获得IDirectInput Device接口后,通常应立即查询IDirectInputDevice2结构并代替IDirectInput接口(对力反馈编程非常重要)
(2) 设备数据格式:从设备获取数据前,必须先设置其数据格式,这只是一个在设备的状态改变后获取数据包并与已知结构进行匹配的问题:SetDataFormat方法
设置自定义数据格式是相当复杂的,幸运的是,DirectInput开发者提供了四种预定义数据格式,可用于任何标准输入设备
(3) 获取设备信息(多种方法可以得到设备的各种信息)
列举设备物:EnumObjects方法
标识设备物:
1. 由偏移量标识:给设备设定数据格式后,每一对象都关联到此设备数据格式的特定位置处,此位置的字节偏移量是一种标识对应设备物的方法
2. 由实例号即ID标识:标识设备物的另一种方法是通过实例号(或ID)进行,它只是分配给各设备物一个序号,但不是GUID
3. 多数情况下,选择偏移量还是实例号来识别设备物是无关紧要的,但要获取输入数据,则应用偏移量来标识设备物
(4)设置和获取设备属性:SetProperty和GetProperty
(5)协作级别
(6)获得设备:取得设备的使用权并通知DirectInput程序想按设定的格式接受数据
6. 取得输入数据
(1)回顾在此之前应进行的步骤
1.创建设备
2. 设置协作级别
3. 设置数据格式
4. 设置设备属性
5. 获得设备
(2)两类数据
1. 缓冲区数据:当设备的缓冲区大小属性被设为大于0的值以后,这一系统开始运作,每当有输入事件发生时,都会有一个数据包被创建,但这些包被放在一个私有缓冲区中,而不是转换成消息的格式,在任何时刻都可以用GetDeviveData方法取出数据包
2. 立即数据:(整个设备或单个键的当前状态的“快照”),它存在于数据包中,数据包的格式即是设备设置的格式,调用GetDeviceState方法可以取得这些数据
(3)事件通知:DirectInput提供一种机制以通知程序设备上是否发生某事或程序是否失去了设备,用Win32 CreatEvent函数创建一个事件,然后通过把其句柄传递给IDirectInputDevice::SetEventNotifition方法将其关联到某个设备。(事件通知对实时游戏并不十分有用,一般采用的方法是在每次循环时检测设备状态或输入数据缓冲区的内容)
(4)轮询以获取数据
当设备状态改变时DirectInput是如何知道的呢?某些情况下,它通过硬件中断实现,否则就必须查询驱动器以获得当前设备状态并用一些小把戏来模拟事件
DirectInput不会自动轮询设备,只有在调用了Poll方法时才这么做
6. 总结
l 对DirectInput而言,设备是由驱动器代表的硬件
l 设备拥有按钮、轴和视点帽
l 设置DirectInput的第一步是取得IDirectInput接口,完成之后,可以列举有效设备
l 创建DirectInput设备对象时需要一个实例GUID,这可通过列举获得,对鼠标和键盘也可以使用预定义的值
l 设备被创建后,可以用几种方法获取它的有关信息,也可以列举它的对象或“物”并获取它们的信息
l 每个设备物一般是通过它在设备数据格式中的偏移量标识的
l 从设备取得数据前,需要设置其属性和协作级别,然后还要获得它
l 缓冲区数据是事件的记录,立即数据是设备当前状态的快照
l 可以让DirectInput在设备状态发生变化时,通知应用程序
l 有些设备如果不被轮询就不提供数据
l DirectInput快速测试是DirectX SDK中非常好的工具
鼠标输入
7. 由于DirectInput直接与鼠标驱动器打交道,因此用户在Windows控制面板中做的任何按键的设置都将被忽略,但是,可以通过调用GetsystemMetrics(SM_SWAPBUTTON)来检验用户参数设置,然后在程序中做相应的调整。尽管DirectInput忽略在控制面板中任何对鼠标的设置,可是它不能忽略由鼠标驱动器本身进行的重新设置
8. 鼠标轴
(1)相对轴模式:初始化鼠标设备时,DirectInput为每条轴设置一个特定值,在返回轴数据时,返回的数据是相对上次所汇报的位置而言的,如果想得到绝对形式的数据,则要设置一个或全部轴的模式属性
(2)轮:即鼠标Z轴()它可以自由地旋转而没有停止点
9. 以独占模式使用鼠标(实际上是把它从Windows中抢走)
这将产生以下现象:
1. 不再有任何鼠标消息产生
2. Windows光标消失
3. 用户不能用鼠标访问菜单或其他窗口(有时想让用户用鼠标访问菜单,这样只有提供一些释放鼠标的方法,使Windows能够使用它)
10. 鼠标缓冲区数据(立即数据是设备的当前状态、缓冲区数据是设备状态变化的记录)
(1)准备缓冲区
调用SetProperty方法来创建缓冲区(GetDeviceData方式得到缓冲区数据)
(2)数据包:每一个缓冲区数据元素都被包含在一个DIDEVICEOBJECTDATA结构中
取得数据:GetDeviveData方法
(3)序列号:数据通常以时间先后顺序从缓冲区中取出,并且每一项都以时间为标志,那序列号还有什么用?
1.与从多个设备来的数据有关
不同设备的事件不会得到相同的序列号,即使它们的时间标志相同也没有用,这对多游戏者实时游戏显然很重要
2.与从单个设备来的数据有关
源于同一设备的事件有可能被分配给相同的序列号,但它们必须确实是同时发生的。
(4) 用时间标志得到双击
DirectInput完全忽略了Windows的输入服务,包括把主鼠标键在用定义的时间间隔内的两次单击变为双击的转换,所以,如果想在程序中使用双击,必须自己控制
11. 鼠标立即数据:调用GetDeviceState时得到的按键数据表明的是此键的真实状态而不是按下或松开
12. 总结
l DirectInput忽略控制面板中对鼠标的设置,但由驱动器解释的击键情况在DirectInput处理前生效
l 鼠标轴,包括轮,在缺省时汇报的是相对值
l 程序可以以独占模式使用鼠标,但必须能把鼠标还给Windows以进行菜单对话框选择等
l 取得缓冲区数据前必须先用SetProperty方法建立缓冲区,从缓冲区中取数据时使用GetDeviceData;必须随时准备处理失去设备的情况
l 缓冲区数据的每一数据包中都含有一个序列号和一个时间标志,序列号可用于确定来自不同设备的事件的优先级以及合并轴的运动,时间标志在响应双击时非常有用
l 鼠标的立即数据说明的是按键的当前状态和轴在上一次调用后的累计运动
游戏杆输入
13. DirectInput以它对多达8条轴,128个按钮,以及力反馈的支持为游戏杆提供了足够的支持
14. 为有效设备编写代码
(1)识别设备:要寻找一种特定模型控制器,可以检测设备的DIDEVICEINSTANCE结构。可以在列举设备时得到这个结构也可以调用GetDev9ceInFo方法得到
(2)获取性能(找出设备能做什么):GetCapabilitese方法(DIDEVCAPS结构)
检测设备上是否有特定的设备时:GetObjectInfo方法(返回DI_OK表示存在)
15. 游戏杆轴(对DirectInput而言,游戏杆轴的缺省模式是绝对轴)
(1)侧向轴:对杆本身的侧向运动而言,X轴由左向右增加,Y轴由远及近增加,对有Z轴的设备而言,Z轴由高到低增加
(2)其他轴:滑块(近—远增大)和拨号盘(顺时针增加)、旋转轴(Rz轴(Rx轴、Ry轴)或舵控制):HID标准要求旋转轴遵守右手定则,即从侧向轴的负端看去旋转是顺时针方向的
(3)游戏杆轴算法(游戏杆要求必须指定如何将物理运动转换为数字)
1. 范围
2. 盲区(小范围不做响应)
3. 饱和度(饱和区里是补偿杆在不同位置得到极值的微小差异的有效方法)
4. 游戏板(只能拥有中心位置和极值)
16. 视点帽
视点控制器,由于其通常位于游戏杆的顶部而被称为帽,它既不是按钮也不是轴,视点帽数据中含有其极坐标形式的位置
17. 游戏杆按钮(返回数据的方式与鼠标按钮或键盘键完全一样)
18. 获取立即游戏杆数据(程序员感兴趣的是游戏杆轴的当前状态而不是每个运动的动作)
设置数据格式,对SetDataFormat的调用完成两种功能:使用按钮和轴的数据格式中的偏移量生成一个按钮和轴的识别系统;告知DirectInput立即数据使用什么样的结构
GetDeviceState方法
19. 总结
(1) DIDEVICEINSTANCE结构给出设备的相关信息,包括产品和实例的GUID以及昵称,在DIDEVCAPS结构中有更多的信息,此结构是通过调用GetCapabilities获得的
(2)缺省情况下,游戏杆返回绝对的值
(3)在采集轴数据之前,要告知DirectInput如何解释原始数据,至少要设置范围,有可能的话也要设置盲区和饱和度
(4)游戏板和游戏杆类似,但只返回X轴和Y轴的极端位置
(5)视点帽返回有限数量的罗盘方向
(6)典型情况下立即数据取到DIJOYSTATE中许多的游戏杆需要被轮询
键盘输入
20. 对于DirectInput来说,键盘是作为一个高效的游戏控制器来处理的,在独占模式中,我们是不能获取键盘数据的,我们所能获得的是WM_KEYDOEM、WM_KEYUP和WM_CHAR消息以及由GetDeviceState和GetDeviceData所返回的输入值(DirectInput中关心的仅有两件事情:某个键是否被按下和这个键的代码是什么)
21. 直接的键盘数据:只需要申明一个256字节的数组然后调用GetDeviceState,而且不需要在调用GetDeviceState之前Poll(轮询),因为键盘数据总是由中断产生的
22. 基于缓冲区的键盘数据:DirectInput是用基于缓冲区数据的方法来处理键盘输入的
数据流的打断:
(1) 设备被强制性地失去获取状态(例如前端协作级别的应用程序被移到后端)
(2) 缓冲区溢出
处理数据的丢失:立即调用GetDeviceState函数以重新获取设备
23. 总结
(1)DirectInput把键盘当作一组按钮来处理,它并不关心键的组合状态,如果想处理字符与控制键的组合状态,那么可以通过响应WM_CHAR消息来实现
(2)键盘上的键都已经用DIK_*定义,这些数值分别与用于返回立即数值的一个256字节的缓冲区相对应
(3)从键盘设备返回缓冲区数据与从鼠标和游戏杆设备中返回缓冲区数据完全一样
(4)在返回基于缓冲区的数据时,应用程序必须随时准备恢复意外丢失的键释放事件
力反馈
24. 力反馈是指把动力和阻力应用到输入设备的轴上,力反馈的单个实例称为效果,效果可由虚拟世界中的事件或状况触发,也可以由输入设备本身触发(如按下按钮等)
(1)动力和状况:动力是对杆的有效推动,可以是一个方向,也可以交替反方向进行;状况的应用是为了响应杆的运动和位置(弹性状况、阻尼、摩擦及惯性等)
(2)大小(动力有大小)、系数(状况没有大小,但有系数)和增益(调整)
(3)设置绝对的大小:查询每一轴的最大物理作用力,然后相应缩放在轴上产生效果,DIDEVICEOBJECTINSANCE结构中的dwFFMaxForce成员可以确定某轴的最大作用力,此结构可在列举设备对象时或后来调用GetObjectInfo方法获得
25. 力反馈的设备方法
(1)为使用力反馈,要先获得IDirectInputDevice接口,然后查询IDirectInputDevice2接口
(2)CreateEffect:创建一种效果并获得对其方法的访问许可、EnumCreatedEffectObjects:列举已创建的效果、EnumEffects:列举设备所支持的效果、GetEffectInfo:获取设备支持的效果的信息、GetForceFeedbackState:取得设备机械状态的多种信息和效果是在执行、被暂停还是被终止的信息、SendForceFeedbaceCommand:打开或关闭激励器,停止、暂停或继续执行所有效果;重新设置力反馈状态
26. 基本效果参数:所有效果都有保存在DIEFFECT结构的一组参数中,此结构在创建效果时和创建后用于修改效果
(1)对DIEFFECT参数的支持:GetEffectInfo方法获得DIEFFECTINFO结构的成员确定
(2)持续时间
(3)采样周期
(4)方向
(5)触发按钮(将效果关联到游戏杆的按钮上可以让驱动器响应按钮的按压事件以启动或停止这种效果)
27. 效果的种类(6种效果和多种子类型)
(1)状况:弹性、摩擦、阻尼(与速度成正比的阻力)、惯性(附加与其加速度成正比的阻力)、状况中的方向
(2)常力:常力是状况中最简单的一种,它只是在某一方向上的力。(原力(常力的一种):早已存在且时刻起作用的力,只不过其大小经常是0)
(3)斜坡力:一定时间内以固定的斜率增加或减小的力(持续时间不能为无限)
(4)周期效果:周期效果是一种波动的效果,其中的作用力大小和方向都在变化,它的特定类型参数在DEPERIODEC结构中
(5)特定设备效果:由硬件制造商设计并封装进驱动器中,可分为两类:一类有可调整的特定类型参数,另一类没有
(6)自定义的作用力:自定义作用力实际上是具有固定变化率并顺序执行的常力数组,所有元素都有相同的方向(可以通过设置负的大小值反转其方向),由于驱动器的原因,对自定义的作用力可能不能应用封套,当然,通过在数组起、止处设置适当的大小值,可以模拟起始和消失,自定义作用力的特定类型结构:DICUSTOMFORCE结构
28. 用封套对效果整形:DirectInput封套可理解为一种调整效果开端与结束的作用
29.在运转的效果
(1)创建一种效果需要的
1. 一个含有基本参数的DIEFFECT结构,其中包含了指向本列表后面各项的指针
2.轴标识数组,通常为DIJOFS_X和DIJOFS_Y
3.方向数组(如果用极坐标),其中包含一个以度的100倍表示的方向和一个0
4.DIENVELOPE结构为效果定义封套
5.一个含有设备特定类型参数的DICONDITION、DICONSTATFORCE、DIRAMPFORCE、DECUSTOMFORCE或为特别硬件定义的结构
6.要创建自定义的作用力,还需一个DICUSTOMFORCE结构指出的常力大小数组
(2)CreateEffect方法:组成一个效果对象的方法
(3)IDirectInputEffect:获得效果的接口以后,接着可以应用它:IDirectInputEffect方法
(4)下载某种效果
创建效果只是简单地展示出一个接口,到目前为止与驱动器的通信只是要确保各式各样的变量都是正确的
下一步是将效果装入内存,也就是说驱动器要保存效果的参数,调用Download方法可以将效果下载,该方法不需要任何参数,其返的是一个HRESULT
(5) 启动某种效果:如果某种效果没有被关联到触发按钮上,那么在运行时应调用Start方法
(6) 调整效果:SetParameters方法——此方法允许改变任何CreateEffect方法初始设置的参数
(7) 停止某种效果:效果持续时间用完后该效果自动停止,如果向提前停止某种效果,或停止一种无限持续时间的效果,使用Stop方法
(8) 卸载某种效果:Unload函数
30. 清除:在执行释放设备和释放DirectInput对象的清除任务之前必须要先释放所有的效果,通过列举可以卸载所有效果,先调用EnumCreatedEffectObjects方法,然后可以在回调函数中释放每一效果
清除时的步骤:释放效果、失去设备、释放设备、释放DirectInput
31. 小结
(1)力反馈的一个实例称为效果,目前,效果主要应用于游戏杆的X轴和Y轴
(2)效果分两类:力,由事件或按钮触发;状况,源于杆本身的运动或位置
(3)力的长度称为大小,状况的长度称为系数,设置增益值可以对它们缩放
(4)可通过缩放大小或系数使不同设备上由相同的输出
(5)效果的基本参数在DIEFFECT结构中,是否支持各种参数取决于驱动器和所选效果
(6)效果的采样间隔是其大小真正的变化的最小间隔值
(7)效果的方向通常沿两轴方向,以极坐标表示
(8)设备驱动器中可以将效果关联到任一按钮上,使得当按钮按下时启动效果,按钮松开停止效果
(9)效果可分为状况、常力、斜坡力、周期力、特定设备力和自定义力,每一种效果都有其特定类型参数
(10)许多效果可以被封套整形
(11)效果运行前需要被创建并下载到设备中,运行时可以修改参数,但对有些效果如果要修改则效果必须停止
(12)程序结束时必须包含对效果的释放
(13)极坐标:在平面内由极点、极轴和极径组成的坐标系;