网页功能: 加入收藏 设为首页 网站搜索  
DirectInput里的键盘鼠标的应用
发表日期:2006-08-23作者:[转贴] 出处:  

  设计一个PC游戏,键盘鼠标的输入是绝不能少。Windows也提供了诸如WM_LBUTTONDOWN、WM_RBUTTONUP等鼠标消息以及WM_KEYDOWN、WM_KEYUP等键盘输入消息。但是DirectInput中仍然提供了对鼠标键盘的支持,其原因就是DirectInput提供一个更直接更快捷的对输入设备的访问方法。就象我们在DOS下直接接管键盘中断,而不是去用什么讨厌的INT16来处理键盘输入一样(用INT16来处理键盘输入其弊端在《金庸群侠传》中显得尤为明显,人物在走路之前总要顿那么一下,就是这一下让我觉得非常之不爽!其原因我想我也不用罗嗦了)。
  当然Windows的键盘消息比之INT16当然有了长足的进步(因为它提供了一个WM_KEYUP消息),但是在某些方面仍显不足。因为Windows的消息机制是一个缓冲(buffer)机制,未被处理的键盘鼠标消息都放在缓冲区里等待下一次处理,这样对于一些应用软件是非常重要的,但是对游戏来说(特别是一些动作游戏,包括体育游戏)就显得有蛇足之嫌了。举个例子,在足球游戏里,你去抢截对手的球——抢球和射门、铲断和长传(大脚)总是设成同一个键,这好像是个公认的标准了——但这时刚好对手的球脱脚了,球直接就到了你的脚下,这时你本来想带球绕过他的,可是你的抢球键已经按过了,由于这个该死的缓冲机制,先要处理一下这个抢球键(也就是射门键),于是你的动作就变成了一次盲目的后场远射(等同于大脚解围)了。控制不了自己的动作,做球员做到这个份儿上真是够失败的了。这里就是缓冲机制不适用的地方了。

  而DirectInput提供了缓冲和立即两种访问输入设备的方式,对于立即方式,正好就是解决上面弊病的方法。DirectInput里关于键盘的初始化部分,已经在很早以前的一篇文章里给出来了。虽是针对于DirectX7的,但关于DirectInput部分在DX8和DX7里差别不大,把LPDIRECTINPUT7换成LPDIRECTINPUT8、LPDIRECTINPUTDEVICE7换成LPDIRECTINPUTDEVICE8就OK了,此外还有一点点需要改动的就是DirectInput对象的创建,DX8里用的是下面这个函数:

DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8,(LPVOID *)&lpDI, NULL);

  里面具体的参数大家看也看得出来,我就不多说了。

  下面说一下鼠标,鼠标的缓冲机制还是满重要的,鼠标的移动就建立在滚动计数累积的基础上的。鼠标是每隔8ms采样一次(反正USB鼠标是这样,我估计一般鼠标也是一样),要是只获取当前状态的话,那这个鼠标移动起来就太慢了(应该不会有人在应用程序里每隔8ms就调用一次鼠标的状态获取函数吧)。之所以采用DirectInput,不是因为缓冲这个原因,而是因为一个我个人的喜好因素。一般的游戏在卷屏时是判断鼠标的位置是否在屏幕边缘,如果是就向这一方向卷屏。我个人不是很喜欢这种做法,可能因为我手比较笨,玩游戏是经常莫名其妙地画面就移走了,这让我觉得很成问题,为什么光标指到屏幕边上就要卷屏?所以我希望鼠标的移动才是卷屏的依据,这在Windows的消息机制里就做不到了。因为在Windows的消息机制里,当鼠标一到屏幕边上时再向外移动,应用程序是收不到WM_MOUSEMOVE消息的。但在DirectInput里就可以由我自己来实现,DirectInput接收到的只是鼠标的滚动计数,它可没有什么光标位置的限制。

  下面就给出DirectInput鼠标对象的初始化代码,只能这么一步步的来,没什么好说的。

//=================================
LPDIRECTINPUT8  pDI;
LPDIRECTINPUTDEVICE8  lpMouse;

// 存放鼠标光标的Surface
LPDIRECT3DSURFACE8  lpDSCursor;
HANDLE  hMouseEvent;

//是有符号型,所以可以判断光标是否移出屏幕来决定是否卷屏
short  MouseX = FULLSCREEN_WIDTH/2,MouseY =FULLSCREEN_HEIGHT/2;

bool InitInput()
{
    HRESULT   hres;
    hres = DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (LPVOID *)&lpDI, NULL);
    if(FAILED(hres))
        return FALSE;

    hres = lpDI->CreateDevice(GUID_SysMouse, &lpMouse,NULL);
    if(FAILED(hres))
        return FALSE;

    hres = lpMouse->SetDataFormat(&c_dfDIMouse);
    if(FAILED(hres))
        return FALSE;

    hres = lpMouse->SetCooperativeLevel(hMainWnd,DISCL_EXCLUSIVE | DISCL_FOREGROUND);
    if(FAILED(hres))
        return FALSE;

    hMouseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if(!hMouseEvent)
        return FALSE;

    hres = lpMouse->SetEventNotification(hMouseEvent);
    if(FAILED(hres))
        return FALSE;

    DIPROPDWORD dipdw;
    dipdw.diph.dwSize = sizeof(DIPROPDWORD);
    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    dipdw.diph.dwObj = 0;
    dipdw.diph.dwHow = DIPH_DEVICE;
    dipdw.dwData = MOUSE_SAMPLEBUFFER;  // 预定义为16
    hres = lpMouse->SetProperty(DIPROP_BUFFERSIZE,&dipdw.diph);
    if(FAILED(hres))
        return FALSE;

    lpMouse->Acquire();

    return TRUE;
}
  现在是我们完全接管了鼠标,那么无可非议的,鼠标光标的显示任务也落到了我们头上,不过在D3D8入门里我提到了,光标的显示可以由D3D8支持。下面我们就来创建一个光标:

D3DLOCKED_RECT  dlr;
//光标的Surface只能是A8R8G8B8格式的,占了一个alpha字节又不支持半透明,真是shit 

hres = lpDevice->CreateImageSurface(32, 32, D3DFMT_A8R8G8B8,&lpDSCursor);
if(FAILED(hres))
    return FALSE;

hres = lpDSCursor->LockRect(&dlr, NULL, 0);
if(FAILED(hres))
    return FALSE;

// 往Surface里写数据呀,不用我说了吧
………………
hres = lpDSCursor->UnlockRect();
hres = lpDevice->SetCursorProperties(0, 0, lpDSCursor);
if(FAILED(hres))
    return FALSE;

lpDevice->ShowCursor(TRUE);
  接下来就是鼠标数据的存取了,这里我只处理了鼠标的移动。

void MouseEvent()
{
    DIDEVICEOBJECTDATA od;
    HRESULT  hres;
    DWORD   count;
    short   x = 0, y = 0;
    while(1)
    {
        count = 1;
        hres =lpMouse->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), &od,&count, 0);
        if(hres == DIERR_INPUTLOST)
        {
            lpMouse->Acquire();
            return;
        }

        if(FAILED(hres) || !count)
            break;

        switch(od.dwOfs)
        {
        case DIMOFS_X:
            x += (short)od.dwData;
            break;
        case DIMOFS_Y:
            y += (short)od.dwData;
            break;
        //物理设备上左键或右键按下/释放,如有左右键交换可是要自己判断的
        case DIMOFS_BUTTON0:
        case DIMOFS_BUTTON1:
            if(od.dwData & 0x80)
            // 键按下
            …………
        else
            // 键释放
            …………    
        }
    }

    if(x || y)
    {
        MouseX += x;
        MouseY += y;
        // 决定光标的位置以及是否卷屏等等
        …………
        lpDevice->SetCursorPosition(MouseX, MouseY,D3DCURSOR_IMMEDIATE_UPDATE);
    }
}


  好了,现在算是完了。但是我个人觉得有一点小小的缺憾,大家如果试一下就会发现,鼠标移动的总比Windows下慢一些,这是为什么?我在Windows的鼠标设置里看到一个加速选项,觉得可能是由于这个原因。那就模拟一下了。(以下只列出上面函数的改动部分)

short  xaccel,yaccel;
xaccel = yaccel = 1;

while(1)
{
    …………
    switch(od.dwOfs)
    {
    case DIMOFS_X:
        x += (short)od.dwData*xaccel;
        xaccel++;
        break;
    case DIMOFS_Y:
        y += (short)od.dwData*yaccel;
        yaccel++;
        break;
        …………
    }
    ………
}

  经过这样改动后,鼠标再移动起来果然顺畅了很多。

  此外,DIDEVICEOBJECTDATA结构中还有一个时间标记,用这个可以判断鼠标的双击,现在我们的鼠标模拟已经初具雏形。自己接管鼠标后,就可以定义方便自己的消息比如什么拖动啦(在Windows下判断拖动就是烦,自己定义一个)、三击啦什么的,好处是不言而喻的,当然也带来了坏处——就是编写的代码就多了,不过这就是游戏程序员的职责呀。
 

我来说两句】 【加入收藏】 【返加顶部】 【打印本页】 【关闭窗口
中搜索 DirectInput里的键盘鼠标的应用
本类热点文章
  DirectInput里的键盘鼠标的应用
  一个中文输入的类
  掌握DirectX和DirectInput力反馈游戏杆
  格斗类游戏的键盘处理
  游戏鼠标操作的思考
  DX下VC6.0中文輸入問題
  使用系统输入法
  DirectInput 键盘编程入门
  DirectX下软件鼠标的实现
  DirectDraw 与 DirectInput 的游戏编程..
  DirectInput 鼠标编程入门
最新分类信息我要发布 
最新招聘信息

关于我们 / 合作推广 / 给我留言 / 版权举报 / 意见建议 / 广告投放  
Copyright ©2003-2024 Lihuasoft.net webmaster(at)lihuasoft.net
网站编程QQ群   京ICP备05001064号 页面生成时间:0.00378