⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 di.cpp

📁 一个完整的2D滚屏游戏示例
💻 CPP
字号:
#include "stdafx.h"
#include "define.h"
#include "DI.h"

///////////////////////////////////////////////////////////////////////////
CDI::CDI()
{
	for(int i=0;i<256;i++)
	{
	m_bKeyDown[i]=false;
	}
}

///////////////////////////////////////////////////////////////////////////
CDI::CDI(HWND hWnd)
{
	Initialize(hWnd);
}

///////////////////////////////////////////////////////////////////////////
CDI::~CDI()
{
	Destroy();
}

////////////////////////////////////////////////////////////////////////////
bool CDI::Initialize(HWND hWnd)
{
HRESULT hr;
 
    //创建一个 DIRECTINPUT 对象
    hr=DirectInputCreate(NULL,
		                 0x0300,               //版本号;这个号低也不行,高也不行,很麻烦;
		                 //DIRECTINPUT_VERSION,//这个宏定义为0x0500,即DIRECTX5.0,没想到竟然初始化不了,现在只好改成3.0版了;
                         &m_lpDI,
						 NULL);
    if(FAILED(hr))goto Destroy;
    
    //创建 DIRECTINPUTDEVICE mouse & keyboard界面
    hr=m_lpDI->CreateDevice(GUID_SysMouse, &m_lpDID_Mouse, NULL);
	if(FAILED(hr))goto Destroy;

    hr=m_lpDI->CreateDevice(GUID_SysKeyboard, &m_lpDID_Keyboard, NULL);
	if(FAILED(hr))goto Destroy;
    
    //设定查询鼠标状态的返回数据格式
    hr=m_lpDID_Mouse->SetDataFormat(&c_dfDIMouse); 
    if(FAILED(hr))goto Destroy;
    
	//设定为通过一个256字节的数组返回查询状态值
    hr=m_lpDID_Keyboard->SetDataFormat(&c_dfDIKeyboard); 
    if(FAILED(hr))goto Destroy;
 
    //设定协作模式
    hr=m_lpDID_Mouse->SetCooperativeLevel(hWnd,  DISCL_EXCLUSIVE|DISCL_FOREGROUND);
    if(FAILED(hr))goto Destroy;
    hr=m_lpDID_Keyboard->SetCooperativeLevel(hWnd,  DISCL_NONEXCLUSIVE|DISCL_FOREGROUND);
    if(FAILED(hr))goto Destroy;
    
    // 设定缓冲区大小
    // 如果不设定,缓冲区大小默认值为0,程序就只能按立即模式工作
    // 如果要用缓冲模式工作,必须使缓冲区大小超过0
    DIPROPDWORD     property;

    property.diph.dwSize=sizeof(DIPROPDWORD);
    property.diph.dwHeaderSize=sizeof(DIPROPHEADER);
    property.diph.dwObj=0;
    property.diph.dwHow=DIPH_DEVICE;
    property.dwData=DINPUT_BUFFERSIZE;

    hr=m_lpDID_Mouse->SetProperty(DIPROP_BUFFERSIZE, &property.diph);
    if(FAILED(hr))goto Destroy;
    hr=m_lpDID_Keyboard->SetProperty(DIPROP_BUFFERSIZE, &property.diph);
	if(FAILED(hr))goto Destroy;
    
    hr=m_lpDID_Mouse->Acquire(); 
    if(FAILED(hr))goto Destroy;
	
	hr=m_lpDID_Keyboard->Acquire(); 
    if(FAILED(hr))goto Destroy;
	
return true; 

Destroy:;
Destroy();
MessageBox(NULL,"DirectInput Initialize Error","Sorry.",MB_OK);
return false;
}

////////////////////////////////////////////////////////////////////////////////////
HRESULT CDI::UpDateInputState(void)
{
	DWORD   i;//暂时不用,缓冲区被设为1;
///*
    //鼠标状态更新:
    if(m_lpDID_Mouse!=NULL) 
    {
        DIDEVICEOBJECTDATA  didod;  // Receives buffered data
        DWORD               dwElements;
        HRESULT             hr;

        while(true)
        {
            dwElements=1;               //每次从缓冲区中读一个数据
            hr=m_lpDID_Mouse->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),
											&didod,
											&dwElements,
											0);

            if(FAILED(hr))// 发生了一个错误
			{
                if(hr==DIERR_INPUTLOST)
                {
                    hr=m_lpDID_Mouse->Acquire();    // 试图重新取回设备
                    if(FAILED(hr))return S_FALSE;   // 失败
				}
            }
            else
            if(dwElements==1)
            {
                switch(didod.dwOfs)
                {
                          case DIMOFS_X:m_dwMouse=DI_MOUSE_MOVE_X;
							            m_MousePoint.x=didod.dwData;// X 轴偏移量,didod.dwData 里是具体偏移相对值,单位为像素
                                        break;
                          case DIMOFS_Y:m_dwMouse=DI_MOUSE_MOVE_Y;
							            m_MousePoint.y=didod.dwData;// Y 轴偏移量,didod.dwData 里是具体偏移相对值,单位为像素
						                break;
                    case DIMOFS_BUTTON0:if((LOWORD(didod.dwData)&0x80)!=0)m_dwMouse=DI_MOUSE_LEFT_DOWN;        //0号键(左键)状态,didod.dwData 里是具体状态值,低字节最高位为1则表示按下,低字节最高位为 0 表示未按下 
                                        else m_dwMouse=DI_MOUSE_LEFT_UP;
						                break;
                    case DIMOFS_BUTTON1:if((LOWORD(didod.dwData)&0x80)!=0)m_dwMouse=DI_MOUSE_RIGHT_DOWN;        // 1 号键(右键)状态 
                                        else m_dwMouse=DI_MOUSE_RIGHT_UP;
						                break;
                    case DIMOFS_BUTTON2:if((LOWORD(didod.dwData)&0x80)!=0)m_dwMouse=DI_MOUSE_MIDDLE_DOWN;        // 2 号键(中键)状态 
                                        else m_dwMouse=DI_MOUSE_MIDDLE_UP;
						                break;
                    case DIMOFS_BUTTON3://3号键状态,保留,一般鼠标上没有这个键;
                                        break;
                }
            }
            else if(dwElements==0)break;      // 缓冲区读空
        }
    }
  //*/  
	//键盘状态更新:
	//立即模式
	BYTE diks[256];
	if(m_lpDID_Keyboard!=NULL)      // 如果 lpKeyboard 对象界面存在
    {
		
        HRESULT hr;

        hr=DIERR_INPUTLOST;   // 为循环检测做准备

        // if input is lost then acquire and keep trying
        while(hr==DIERR_INPUTLOST)
        {
            // 读取输入设备状态值到状态数据缓冲区
            hr=m_lpDID_Keyboard->GetDeviceState(sizeof(diks),&diks);

            if(hr==DIERR_INPUTLOST)
            {
                // DirectInput 报告输入流被中断// 必须先重新调用 Acquire 方法,然后再试一次
                hr=m_lpDID_Keyboard->Acquire();
                if(FAILED(hr))return hr;
            }
			else
			{
				for(i=0;i<256;i++)
				{
				if((diks[i]&&0x80)!=0)m_bKeyDown[i]=true;
				else m_bKeyDown[i]=false;
				}
			}
        }
        if(FAILED(hr))return hr;
    }

	

    /*缓冲模式:
	DIDEVICEOBJECTDATA  didod[DINPUT_BUFFERSIZE];  //Receives buffered data
    DWORD               dwElements;
    HRESULT             hr;
	if(m_lpDID_Keyboard!=NULL) 
    {
        hr=DIERR_INPUTLOST;

        while(hr!=DI_OK)
        {
            dwElements=1;//DINPUT_BUFFERSIZE;
            hr=m_lpDID_Keyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),
											   didod,
											   &dwElements,
											   0);
            if (hr!=DI_OK)
            {
                // 发生了一个错误
                // 这个错误有可能是 DI_BUFFEROVERFLOW 缓冲区溢出错误
                // 但不管是哪种错误,都意味着同输入设备的联系被丢失了

                // 这种错误引起的最严重的后果就是如果你按下一个键后还未松开时
                // 发生了错误,就会丢失后面松开该键的消息。这样一来,你的程序
                // 就可能以为该键尚未被松开,从而发生一些意想不到的情况

                // 现在这段代码并未处理该错误

                // 解决该问题的一个办法是,在出现这种错误时,就去调用一次
                // GetDeviceState(),然后把结果同程序最后所记录的状态进行
                // 比较,从而修正可能发生的错误

                hr=m_lpDID_Keyboard->Acquire();
                if(FAILED(hr))return hr;
            }
        }
		if(FAILED(hr))return hr;
	}

    // GetDeviceData() 同 GetDeviceState() 不一样,调用它之后,
    // dwElements 将指明此次调用共读取到了几条缓冲区记录
	*/
	
    // 我们再用一个循环来处理每条记录  
    /*
	for(i=0;i<dwElements;i++) 
    {
        // 此处放入处理代码
        // didod[i].dwOfs 表示那个键被按下或松开
        // didod[i].dwData 记录此键的状态,低字节最高位是 1 表示按下,0 表示松开
        // 一般用 didod[i].dwData&0x80 来测试
    }
	*/
	/*取键盘数据参考:
	if(SUCCEEDED(hr)&&cod>0)
	{
		DWORD	iod;

		for(iod=0; iod<cod; iod++)
		{
			if((rgod[iod].dwData & 0x80))
			{
				KEYBUFFER[KEYENDCOUNTER] = (BYTE)(rgod[iod].dwOfs);
				KEYENDCOUNTER ++;
				if(KEYENDCOUNTER >= DINPUT_BUFFERSIZE) KEYENDCOUNTER = 0;
			}
			KEYSTATUSFLAG[rgod[iod].dwOfs] = (BYTE)(rgod[iod].dwData & 0x80);
		}
	}*/
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////////
void CDI::Destroy()
{
    if(m_lpDI) 
    { 
        if(m_lpDID_Mouse) 
        { 
            // Always unacquire the device before calling Release(). 
            m_lpDID_Mouse->Unacquire(); 
            m_lpDID_Mouse->Release();
            m_lpDID_Mouse=NULL; 
        }
		if(m_lpDID_Keyboard) 
        { 
            // Always unacquire the device before calling Release(). 
            m_lpDID_Keyboard->Unacquire(); 
            m_lpDID_Keyboard->Release();
            m_lpDID_Keyboard=NULL; 
        }
        m_lpDI->Release();
        m_lpDI=NULL; 
    } 
return;
}
////////////////////////////////////////////////////////////////////////////////////
//class CDI end;



/*



long FAR PASCAL WindowProc(HWND hWnd, UINT message, 
                           WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_ACTIVATEAPP:
            if(bActive)
            {
                if(lpKeyboard) lpKeyboard->Acquire();
            }
            else
            {
                if(lpKeyboard) lpKeyboard->Unacquire();
            }
        break;
    ...
}

  哦,对了,前一段例程中还提到了立即模式和缓冲模式。在 DirectINPUT 中,这两种工作模式是有区别的。

  如果使用立即模式的话,在查询数据时,只能返回查询时的设备状态。而缓冲模式则将记录所有设备状态变化过程。就个人喜好而言,笔者偏好后者,因为这样一般不会丢失任何按键信息。对应的,如果在使用前者时的查询频度太低,则很难保证采集数据的完整性。


DIRECTINPUT 的数据查询


  立即模式的数据查询比较简单,请看下面的示例:


BYTE diks[256]; // DirectInput keyboard state buffer 键盘状态数据缓冲区

HRESULT UpdateInputState(void)
{
    if(lpKeyboard != NULL)      // 如果 lpKeyboard 对象界面存在
    {
        HRESULT hr;

        hr = DIERR_INPUTLOST;   // 为循环检测做准备

        // if input is lost then acquire and keep trying
        while(hr == DIERR_INPUTLOST)
        {
            // 读取输入设备状态值到状态数据缓冲区
            hr = lpKeyboard->GetDeviceState(sizeof(diks), &diks);

            if(hr == DIERR_INPUTLOST)
            {
                // DirectInput 报告输入流被中断
                // 必须先重新调用 Acquire 方法,然后再试一次
                hr = lpKeyboard->Acquire();
                if(FAILED(hr))
                    return hr;
            }
        }

        if(FAILED(hr))
            return hr;
    }

    return S_OK;
}

  在上面的示例中,关键处就是使用 GetDeviceState 方法来读取输入设备状态值以及对异常情况的处理。通过使用 GetDeviceState 方法,我们把输入设备的状态值放在了一个 256 字节的数组里。如果该数组中某个数组元素的最高位为 1,则表示相应编码的那个键此时正被按下。例如,如果 diks[1]&0x80>0,那么就表示 ESC 键正被按下。

  学会了立即模式的数据查询以后,下面我们开始研究缓冲模式的情况:


HRESULT UpdateInputState(void)
{
    DWORD   i;

    if(lpKeyboard != NULL) 
    {
        DIDEVICEOBJECTDATA  didod[DINPUT_BUFFERSIZE];  // Receives buffered data
        DWORD               dwElements;
        HRESULT             hr;

        hr = DIERR_INPUTLOST;

        while(hr != DI_OK)
        {
            dwElements = DINPUT_BUFFERSIZE;
            hr = lpKeyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),
                                           didod,
                                           &dwElements,
                                           0);
            if (hr != DI_OK)
            {
                // 发生了一个错误
                // 这个错误有可能是 DI_BUFFEROVERFLOW 缓冲区溢出错误
                // 但不管是哪种错误,都意味着同输入设备的联系被丢失了

                // 这种错误引起的最严重的后果就是如果你按下一个键后还未松开时
                // 发生了错误,就会丢失后面松开该键的消息。这样一来,你的程序
                // 就可能以为该键尚未被松开,从而发生一些意想不到的情况

                // 现在这段代码并未处理该错误

                // 解决该问题的一个办法是,在出现这种错误时,就去调用一次
                // GetDeviceState(),然后把结果同程序最后所记录的状态进行
                // 比较,从而修正可能发生的错误

                hr = lpKeyboard->Acquire();
                if(FAILED(hr))
                    return hr;
            }
        }

        if(FAILED(hr))
            return hr;
    }

    // GetDeviceData() 同 GetDeviceState() 不一样,调用它之后,
    // dwElements 将指明此次调用共读取到了几条缓冲区记录

    // 我们再用一个循环来处理每条记录

    for(int i=0; i<dwElements; i++) 
    {
        // 此处放入处理代码
        // didod[i].dwOfs 表示那个键被按下或松开
        // didod[i].dwData 记录此键的状态,低字节最高位是 1 表示按下,0 表示松开
        // 一般用 didod[i].dwData&0x80 来测试
    }

    return S_OK;
}

  其实,每条记录还有 dwTimeStamp 和 dwSequence 两个字段来记录消息发生的时间和序列编号,以便作更复杂的处理。本文是针对初学者写的,就不打算去谈论这些内容了。

*/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -