• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            天行健 君子當(dāng)自強(qiáng)而不息

            【ZT】DirectInput里的鍵盤鼠標(biāo)的應(yīng)用

            設(shè)計(jì)一個(gè)PC游戲,鍵盤鼠標(biāo)的輸入是絕不能少。Windows也提供了諸如 WM_LBUTTONDOWN、WM_RBUTTONUP等鼠標(biāo)消息以及WM_KEYDOWN、WM_KEYUP等鍵盤輸入消息。但是 DirectInput中仍然提供了對(duì)鼠標(biāo)鍵盤的支持,其原因就是DirectInput提供一個(gè)更直接更快捷的對(duì)輸入設(shè)備的訪問方法。就象我們?cè)贒OS 下直接接管鍵盤中斷,而不是去用什么討厭的INT16來處理鍵盤輸入一樣(用INT16來處理鍵盤輸入其弊端在《金庸群俠傳》中顯得尤為明顯,人物在走路 之前總要頓那么一下,就是這一下讓我覺得非常之不爽!其原因我想我也不用羅嗦了)。

              當(dāng)然Windows的鍵盤消息比之INT16當(dāng)然有了長足的進(jìn)步(因?yàn)樗峁┝艘? 個(gè)WM_KEYUP消息),但是在某些方面仍顯不足。因?yàn)閃indows的消息機(jī)制是一個(gè)緩沖(buffer)機(jī)制,未被處理的鍵盤鼠標(biāo)消息都放在緩沖區(qū) 里等待下一次處理,這樣對(duì)于一些應(yīng)用軟件是非常重要的,但是對(duì)游戲來說(特別是一些動(dòng)作游戲,包括體育游戲)就顯得有蛇足之嫌了。舉個(gè)例子,在足球游戲 里,你去搶截對(duì)手的球——搶球和射門、鏟斷和長傳(大腳)總是設(shè)成同一個(gè)鍵,這好像是個(gè)公認(rèn)的標(biāo)準(zhǔn)了——但這時(shí)剛好對(duì)手的球脫腳了,球直接就到了你的腳 下,這時(shí)你本來想帶球繞過他的,可是你的搶球鍵已經(jīng)按過了,由于這個(gè)該死的緩沖機(jī)制,先要處理一下這個(gè)搶球鍵(也就是射門鍵),于是你的動(dòng)作就變成了一次 盲目的后場(chǎng)遠(yuǎn)射(等同于大腳解圍)了。控制不了自己的動(dòng)作,做球員做到這個(gè)份兒上真是夠失敗的了。這里就是緩沖機(jī)制不適用的地方了。

              而DirectInput提供了緩沖和立即兩種訪問輸入設(shè)備的方式,對(duì)于立即方 式,正好就是解決上面弊病的方法。DirectInput里關(guān)于鍵盤的初始化部分,已經(jīng)在很早以前的一篇文章里給出來了。雖是針對(duì)于DirectX7的, 但關(guān)于DirectInput部分在DX8和DX7里差別不大,把LPDIRECTINPUT7換成LPDIRECTINPUT8、 LPDIRECTINPUTDEVICE7換成LPDIRECTINPUTDEVICE8就OK了,此外還有一點(diǎn)點(diǎn)需要改動(dòng)的就是DirectInput 對(duì)象的創(chuàng)建,DX8里用的是下面這個(gè)函數(shù):

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

              里面具體的參數(shù)大家看也看得出來,我就不多說了。

              下面說一下鼠標(biāo),鼠標(biāo)的緩沖機(jī)制還是滿重要的,鼠標(biāo)的移動(dòng)就建立在滾動(dòng)計(jì)數(shù)累積的 基礎(chǔ)上的。鼠標(biāo)是每隔8ms采樣一次(反正USB鼠標(biāo)是這樣,我估計(jì)一般鼠標(biāo)也是一樣),要是只獲取當(dāng)前狀態(tài)的話,那這個(gè)鼠標(biāo)移動(dòng)起來就太慢了(應(yīng)該不會(huì) 有人在應(yīng)用程序里每隔8ms就調(diào)用一次鼠標(biāo)的狀態(tài)獲取函數(shù)吧)。之所以采用DirectInput,不是因?yàn)榫彌_這個(gè)原因,而是因?yàn)橐粋€(gè)我個(gè)人的喜好因 素。一般的游戲在卷屏?xí)r是判斷鼠標(biāo)的位置是否在屏幕邊緣,如果是就向這一方向卷屏。我個(gè)人不是很喜歡這種做法,可能因?yàn)槲沂直容^笨,玩游戲是經(jīng)常莫名其妙 地畫面就移走了,這讓我覺得很成問題,為什么光標(biāo)指到屏幕邊上就要卷屏?所以我希望鼠標(biāo)的移動(dòng)才是卷屏的依據(jù),這在Windows的消息機(jī)制里就做不到 了。因?yàn)樵赪indows的消息機(jī)制里,當(dāng)鼠標(biāo)一到屏幕邊上時(shí)再向外移動(dòng),應(yīng)用程序是收不到WM_MOUSEMOVE消息的。但在DirectInput 里就可以由我自己來實(shí)現(xiàn),DirectInput接收到的只是鼠標(biāo)的滾動(dòng)計(jì)數(shù),它可沒有什么光標(biāo)位置的限制。

              下面就給出DirectInput鼠標(biāo)對(duì)象的初始化代碼,只能這么一步步的來,沒什么好說的。


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

            // 存放鼠標(biāo)光標(biāo)的Surface
            LPDIRECT3DSURFACE8  lpDSCursor;
            HANDLE  hMouseEvent;

            //是有符號(hào)型,所以可以判斷光標(biāo)是否移出屏幕來決定是否卷屏
            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;  // 預(yù)定義為16
                hres = lpMouse->SetProperty(DIPROP_BUFFERSIZE,&dipdw.diph);
                
            if(FAILED(hres))
                    
            return FALSE;

                lpMouse
            ->Acquire();

                
            return TRUE;
            }


            現(xiàn)在是我們完全接管了鼠標(biāo),那么無可非議的,鼠標(biāo)光標(biāo)的顯示任務(wù)也落到了我們頭上,不過在D3D8入門里我提到了,光標(biāo)的顯示可以由D3D8支持。下面我們就來創(chuàng)建一個(gè)光標(biāo):

            D3DLOCKED_RECT  dlr;
            //光標(biāo)的Surface只能是A8R8G8B8格式的,占了一個(gè)alpha字節(jié)又不支持半透明,真是shit 

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

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

            // 往Surface里寫數(shù)據(jù)呀,不用我說了吧
            ………………
            hres 
            = lpDSCursor->UnlockRect();
            hres 
            = lpDevice->SetCursorProperties(00, lpDSCursor);
            if(FAILED(hres))
                
            return FALSE;

            lpDevice
            ->ShowCursor(TRUE);

            接下來就是鼠標(biāo)數(shù)據(jù)的存取了,這里我只處理了鼠標(biāo)的移動(dòng)。

            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;
                    
            //物理設(shè)備上左鍵或右鍵按下/釋放,如有左右鍵交換可是要自己判斷的
                    case DIMOFS_BUTTON0:
                    
            case DIMOFS_BUTTON1:
                        
            if(od.dwData & 0x80)
                        
            // 鍵按下
                        …………
                    
            else
                        
            // 鍵釋放
                        …………    
                    }
                }

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

              好了,現(xiàn)在算是完了。但是我個(gè)人覺得有一點(diǎn)小小的缺憾,大家如果試一下就會(huì)發(fā)現(xiàn),鼠標(biāo)移動(dòng)的總比Windows下慢一些,這是為什么?我在Windows的鼠標(biāo)設(shè)置里看到一個(gè)加速選項(xiàng),覺得可能是由于這個(gè)原因。那就模擬一下了。(以下只列出上面函數(shù)的改動(dòng)部分)

            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;
                    …………
                }
                ………
            }

                  經(jīng)過這樣改動(dòng)后,鼠標(biāo)再移動(dòng)起來果然順暢了很多。

              此外,DIDEVICEOBJECTDATA結(jié)構(gòu)中還有一個(gè)時(shí)間標(biāo)記,用這個(gè)可以 判斷鼠標(biāo)的雙擊,現(xiàn)在我們的鼠標(biāo)模擬已經(jīng)初具雛形。自己接管鼠標(biāo)后,就可以定義方便自己的消息比如什么拖動(dòng)啦(在Windows下判斷拖動(dòng)就是煩,自己定 義一個(gè))、三擊啦什么的,好處是不言而喻的,當(dāng)然也帶來了壞處——就是編寫的代碼就多了,不過這就是游戲程序員的職責(zé)呀。

            posted on 2007-05-06 17:17 lovedday 閱讀(639) 評(píng)論(0)  編輯 收藏 引用 所屬分類: ■ DirectX 9 Program

            公告

            導(dǎo)航

            統(tǒng)計(jì)

            常用鏈接

            隨筆分類(178)

            3D游戲編程相關(guān)鏈接

            搜索

            最新評(píng)論

            成人精品一区二区久久| 久久99精品久久久久久hb无码 | 久久久久亚洲av无码专区导航| 久久99精品国产99久久6| 国内精品伊人久久久久| 欧洲人妻丰满av无码久久不卡| 久久中文字幕精品| 国产69精品久久久久久人妻精品| 少妇久久久久久被弄到高潮 | 色婷婷综合久久久中文字幕| 久久人人爽人人爽人人av东京热| 少妇人妻综合久久中文字幕| 伊人精品久久久久7777| 久久人做人爽一区二区三区 | 91视频国产91久久久| 91久久婷婷国产综合精品青草| 久久国产精品久久久| 国产精品综合久久第一页| 久久国产视频99电影| 日本五月天婷久久网站| 人妻无码中文久久久久专区| 色综合久久综合网观看| 美女久久久久久| 久久精品无码一区二区无码| 九九99精品久久久久久| 青青青国产精品国产精品久久久久 | 久久亚洲2019中文字幕| 久久精品国产2020| 国产99久久精品一区二区| 久久综合给合综合久久| 一本色道久久综合亚洲精品| 欧洲精品久久久av无码电影| 99久久久久| 久久99国产综合精品| 久久九九久精品国产| 九九精品99久久久香蕉| 亚洲中文字幕伊人久久无码| 一级做a爰片久久毛片16| 99久久精品国产一区二区| 亚洲国产精品久久久久婷婷老年| 久久免费看黄a级毛片|