游戏手柄(JoyStick)编程学习笔记(2)

在我的上一篇博客中(http://blog.csdn.net/liyuanbhu/article/details/51714045),介绍了通过 multimedia joystick API 来访问游戏手柄的基本方法。

最后说到了利用 joySetCapture 函数监听手柄事件的方式并不是非常的好用。建议大家字节写个监听线程,有针对性的监听需要的事件。这里,我把我以前写的一份代码放上来。代码是基于 Qt 的,监听了按键、摇杆和视觉头盔的状态,在状态发生改变时发出Qt 的信号。

按键对应两个信号:

void Joy_ButtonPressed(int i);
void Joy_ButtonReleased(int i);

摇杆分为两种模式:SWITCH_MODE 和 COORDINATE_MODE

SWITCH_MODE 模式下,输出四个方向的开关量。对应的信号是:

    void Joy_MoveForward();
    void Joy_MoveBackward();
    void Joy_MoveForwardStop();
    void Joy_MoveBackwardStop();
    void Joy_MoveLeft();
    void Joy_MoveRight();
    void Joy_MoveLeftStop();
    void Joy_MoveRightStop();

COORDINATE_MODE 模式直接输出坐标值(只有在摇杆位置发生变化时才会有输出)。

void Joy_Position(int x, int y);

视觉头盔模拟了摇杆的开关量模式。对应的信号同样是:

    void Joy_MoveForward();
    void Joy_MoveBackward();
    void Joy_MoveForwardStop();
    void Joy_MoveBackwardStop();
    void Joy_MoveLeft();
    void Joy_MoveRight();
    void Joy_MoveLeftStop();
    void Joy_MoveRightStop();

具体的代码不用过多介绍,主要就是几个状态机。下面贴出代码来。

首先是头文件:

#ifndef JOYSTICK_H
#define JOYSTICK_H

#include <QObject>
#include <QThread>
#include <Windows.h>
#include <Mmsystem.h>

class JoyStick;

/**
 * @brief The JoyStickThread class
 * @details 内部类,定时轮询 JoyStick 的状态。
 */
class JoyStickThread : public QThread
{
public:
    explicit JoyStickThread();
    void setJoyStick(JoyStick *joystick);
    void run();
    void stop();
private:
    void Coordinate_StateMachine(int xPos, int yPos);
    void AxisX_StateMachine(int xPos);
    void AxisY_StateMachine(int yPos);
    void Button_StateMachine(int button);
    void POV_StateMachine_Axis(int pov);
    void POV_StateMachine(int pov);
    int old_xPos;
    int old_yPos;
    int old_pov;
    bool pov_forward;
    bool pov_backward;
    bool pov_left;
    bool pov_right;
    bool pov_center;
    int m_button[16];
    JoyStick *m_joystick;
    bool m_run;
};

class JoyStick : public QObject
{
    Q_OBJECT
public:
    enum MODE{SWITCH_MODE, COORDINATE_MODE};
    /**
     * @brief JoyStick
     * @param mode SWITCH_MODE 模式时,摇杆输出开关量。COORDINATE_MODE 模式时,输出摇杆的位置。
     * @param parent
     */
    explicit JoyStick(enum MODE mode = SWITCH_MODE, QObject *parent = 0);

    enum MODE mode() const {return m_mode;}
    ~JoyStick();
    /**
     * @brief listen
     * @return 启动监听线程,之后就可以接收手柄的各种消息了。
     */
    bool listen();
    /**
     * @brief stop 停止监听线程,不需要接收手柄消息时可以调用这个函数。
     */
    void stop();
signals:
    /// JoyStick 的摇杆的各种信号。
    /// 高级些的手柄是可以返回摇杆的坐标值的。
    /// 这里模仿的是工业摇杆,输出的都是开关量。
    /// 前后左右四组开关量。
    void Joy_MoveForward();
    void Joy_MoveBackward();
    void Joy_MoveForwardStop();
    void Joy_MoveBackwardStop();
    void Joy_MoveLeft();
    void Joy_MoveRight();
    void Joy_MoveLeftStop();
    void Joy_MoveRightStop();

    /// JoyStick 的摇杆位置。
    void Joy_Position(int x, int y);
    /**
     * @brief Joy_ButtonPressed 当手柄上某个按键被按下时发出这个信号
     * @param i 用来标志是哪个按键被按下了。
     */
    void Joy_ButtonPressed(int i);
    /**
     * @brief Joy_ButtonReleased 当手柄上某个按键被释放时发出这个信号
     * @param i 用来标志是哪个按键被释放了。
     */
    void Joy_ButtonReleased(int i);

    /// 以下是视觉头盔的各种信号
    void Joy_POVForward();
    void Joy_POVBackward();
    void Joy_POVLeft();
    void Joy_POVRight();
    void Joy_POVForwardLeft();
    void Joy_POVForwardRight();
    void Joy_POVBackwardLeft();
    void Joy_POVBackwardRight();
    /**
     * @brief Joy_POVCentered 当视觉头盔从前8种状态恢复到默认状态时发出这个信号。
     */
    void Joy_POVCentered();

private:
    enum MODE m_mode;
    bool m_valid;
    JoyStickThread m_thread;
};

#endif // JOYSTICK_H

下面是cpp 文件:

#include "joystick.h"
#include <Windows.h>
#include <QDebug>

JoyStick::JoyStick(enum MODE mode, QObject *parent) : QObject(parent)
{
    m_mode = mode;
    m_valid = false;
    m_thread.setJoyStick(this);
    JOYINFO joyinfo;
    if( joyGetNumDevs() > 0 && joyGetPos(JOYSTICKID1, &joyinfo) != JOYERR_UNPLUGGED )
    {
        qDebug() << "JoyStick Init success!";
        m_valid = true;
    }
}

JoyStick::~JoyStick()
{
    m_thread.stop();
    m_thread.wait();
}

bool JoyStick::listen()
{
    m_thread.start();
    return true;
}
void JoyStick::stop()
{
    if(m_thread.isRunning())
    {
        m_thread.stop();
        m_thread.wait();
    }
}

JoyStickThread::JoyStickThread()
{
    m_run = 1;

    old_xPos = 32767;
    old_yPos = 32767;
    for(int i = 0; i < 16; i++)
    {
        m_button[i] = 0;
    }
    old_pov = JOY_POVCENTERED;
    pov_forward = false;
    pov_backward = false;
    pov_left = false;
    pov_right = false;
    pov_center = true;
}

void JoyStickThread::stop()
{
   m_run = 0;
}

void JoyStickThread::setJoyStick(JoyStick *joystick)
{
    m_joystick = joystick;
}

void JoyStickThread::run()
{
    //JOYINFO joyinfo;
    JOYINFOEX joyinfoex;
    joyinfoex.dwSize = sizeof(JOYINFOEX);
    joyinfoex.dwFlags = JOY_RETURNALL;
    while(m_run)
    {
//        if(joyGetPos(JOYSTICKID1, &joyinfo) == JOYERR_NOERROR)
//        {
//            //qDebug() << joyinfo.wXpos;
//            AxisX_StateMachine(joyinfo.wXpos);
//            AxisY_StateMachine(joyinfo.wYpos);
//            Button_StateMachine(joyinfo.wButtons);
//        }
        if(joyGetPosEx(JOYSTICKID1, &joyinfoex) == JOYERR_NOERROR)
        {
            if(m_joystick->mode() == JoyStick::SWITCH_MODE)
            {
                AxisX_StateMachine(joyinfoex.dwXpos);
                AxisY_StateMachine(joyinfoex.dwYpos);
            }
            else
            {
                Coordinate_StateMachine(joyinfoex.dwXpos, joyinfoex.dwYpos);
            }
            Button_StateMachine(joyinfoex.dwButtons);
            POV_StateMachine_Axis(joyinfoex.dwPOV);
        }
        msleep(50);
    }
}

void JoyStickThread::POV_StateMachine(int pov)
{
    if(pov != old_pov)
    {
        //qDebug() << pov;
        if( pov == JOY_POVFORWARD )
        {
            emit m_joystick->Joy_POVForward();
        }
        if( pov == JOY_POVFORWARD + 4500 )
        {
            emit m_joystick->Joy_POVForwardRight();
        }
        if( pov == JOY_POVRIGHT )
        {
            emit m_joystick->Joy_POVRight();
        }
        if( pov == JOY_POVRIGHT  + 4500 )
        {
            emit m_joystick->Joy_POVBackwardRight();
        }
        if( pov == JOY_POVBACKWARD )
        {
            emit m_joystick->Joy_POVBackward();
        }
        if( pov == JOY_POVBACKWARD + 4500 )
        {
            emit m_joystick->Joy_POVBackwardLeft();
        }
        if( pov == JOY_POVLEFT )
        {
            emit m_joystick->Joy_POVLeft();
        }
        if( pov == JOY_POVLEFT + 4500 )
        {
            emit m_joystick->Joy_POVForwardLeft();
        }
        if( pov == JOY_POVCENTERED )
        {
            emit m_joystick->Joy_POVCentered();
        }
        old_pov = pov;
    }
}

void JoyStickThread::POV_StateMachine_Axis(int pov)
{
    if(pov != old_pov)
    {
        //qDebug() << pov;
        if( pov == JOY_POVFORWARD )
        {
            if(pov_forward == false)
            {
                pov_forward = true;
                emit m_joystick->Joy_MoveForward();
                //qDebug() << "JOY_POVFORWARD";
            }
            if(pov_left == true)
            {
                emit m_joystick->Joy_MoveLeftStop();
                //qDebug() << "JOY_POVLEFT RETURN";
            }
            if(pov_right == true)
            {
                emit m_joystick->Joy_MoveRightStop();
                //qDebug() << "JOY_POVRIGHT RETURN";
            }
            pov_left = false;
            pov_right = false;
        }
        if( pov == JOY_POVFORWARD + 4500 )
        {
            if(pov_forward == false)
            {
                pov_forward = true;
                emit m_joystick->Joy_MoveForward();
                //qDebug() << "JOY_POVFORWARD";
            }
            if(pov_right == false)
            {
                pov_right = true;
                emit m_joystick->Joy_MoveRight();
                //qDebug() << "JOY_POVRIGHT";
            }
        }
        if( pov == JOY_POVRIGHT )
        {
            if(pov_right == false)
            {
                pov_right = true;
                emit m_joystick->Joy_MoveRight();
                //qDebug() << "JOY_POVRIGHT";
            }
            if(pov_forward == true)
            {
                emit m_joystick->Joy_MoveForwardStop();
                //qDebug() << "JOY_POVFORWARD RETURN";
            }
            if(pov_backward == true)
            {
                emit m_joystick->Joy_MoveBackwardStop();
                //qDebug() << "JOY_POVBACKWARD RETURN";
            }
            pov_forward = false;
            pov_backward = false;
        }
        if( pov == JOY_POVRIGHT  + 4500 )
        {
            if(pov_right == false)
            {
                pov_right = true;
                emit m_joystick->Joy_MoveRight();
               // qDebug() << "JOY_POVRIGHT";
            }
            if(pov_backward == false)
            {
                pov_backward = true;
                emit m_joystick->Joy_MoveBackward();
               // qDebug() << "JOY_POVBACKWARD";
            }
        }
        if( pov == JOY_POVBACKWARD )
        {
            if(pov_backward == false)
            {
                pov_backward = true;
                emit m_joystick->Joy_MoveBackward();
                //qDebug() << "JOY_POVBACKWARD";
            }
            if(pov_left == true)
            {
                emit m_joystick->Joy_MoveLeftStop();
                //qDebug() << "JOY_POVLEFT RETURN";
            }
            if(pov_right == true)
            {
                emit m_joystick->Joy_MoveRightStop();
                //qDebug() << "JOY_POVRIGHT RETURN";
            }
            pov_left = false;
            pov_right = false;
        }
        if( pov == JOY_POVBACKWARD + 4500 )
        {
            if(pov_backward == false)
            {
                pov_backward = true;
                emit m_joystick->Joy_MoveBackward();
                //qDebug() << "JOY_POVBACKWARD";
            }
            if(pov_left == false)
            {
                pov_left = true;
                emit m_joystick->Joy_MoveLeft();
               // qDebug() << "JOY_POVLEFT";
            }
        }
        if( pov == JOY_POVLEFT )
        {
            if(pov_left == false)
            {
                pov_left = true;
                emit m_joystick->Joy_MoveLeft();
                //qDebug() << "JOY_POVLEFT";
            }
            if(pov_forward == true)
            {
                emit m_joystick->Joy_MoveForwardStop();
                //qDebug() << "JOY_POVFORWARD RETURN";
            }
            if(pov_backward == true)
            {
                emit m_joystick->Joy_MoveBackwardStop();
                //qDebug() << "JOY_POVBACKWARD RETURN";
            }
            pov_backward = false;
            pov_forward = false;
        }
        if( pov == JOY_POVLEFT + 4500 )
        {
            if(pov_left == false)
            {
                pov_left = true;
                emit m_joystick->Joy_MoveLeft();
                //qDebug() << "JOY_POVLEFT";
            }
            if(pov_forward == false)
            {
                pov_forward = true;
                emit m_joystick->Joy_MoveForward();
                //qDebug() << "JOY_POVFORWARD";
            }
        }
        if( pov == JOY_POVCENTERED )
        {
            if(pov_forward == true)
            {
                emit m_joystick->Joy_MoveForwardStop();
                //qDebug() << "JOY_POVFORWARD RETURN";
            }
            if(pov_backward == true)
            {
                emit m_joystick->Joy_MoveBackwardStop();
                //qDebug() << "JOY_POVBACKWARD RETURN";
            }
            if(pov_left == true)
            {
                emit m_joystick->Joy_MoveLeftStop();
                //qDebug() << "JOY_POVLEFT RETURN";
            }
            if(pov_right == true)
            {
                emit m_joystick->Joy_MoveRightStop();
                //qDebug() << "JOY_POVRIGHT RETURN";
            }
            pov_forward = false;
            pov_right = false;
            pov_left = false;
            pov_backward = false;
            //qDebug() << "JOY_POVCENTERED";
        }
        old_pov = pov;
    }
}

void JoyStickThread::Button_StateMachine(int button)
{
    //if( button )
    //qDebug() << QString::number(button, 16);
    const int mask[16] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768};
    for(int i = 0; i < 12; i++)
    {
        int n = button & mask[i];
        if(n ^ m_button[i])
        {
            m_button[i] = n;
            if( n )
            {
                emit m_joystick->Joy_ButtonPressed(i + 1);
                //qDebug() << "ButtonPressed(" << i + 1 << ")";
            }
            else
            {
                emit m_joystick->Joy_ButtonReleased(i + 1);
                //qDebug() << "ButtonReleased(" << i + 1 << ")";;
            }
        }
    }
}

void JoyStickThread::Coordinate_StateMachine(int xPos, int yPos)
{
    if(xPos == old_xPos && yPos == old_yPos)
    {
        return;
    }
    old_xPos = xPos;
    old_yPos = yPos;
    emit m_joystick->Joy_Position(xPos, yPos);
}

void JoyStickThread::AxisX_StateMachine(int xPos)
{
    if(xPos == old_xPos) return;
    switch(xPos)
    {
    case 0:
        emit m_joystick->Joy_MoveLeft();
        break;
    case 32767:
        if(old_xPos == 0)
        {
            emit m_joystick->Joy_MoveLeftStop();
        }
        else
        {
            emit m_joystick->Joy_MoveRightStop();
        }
        break;
    case 65535:
        emit m_joystick->Joy_MoveRight();
        break;
    }
    old_xPos = xPos;
}

void JoyStickThread::AxisY_StateMachine(int yPos)
{
    if(yPos == old_yPos) return;
    switch(yPos)
    {
    case 0:
        emit m_joystick->Joy_MoveForward();
        break;
    case 32767:
        if(old_xPos == 0)
        {
            emit m_joystick->Joy_MoveForwardStop();
        }
        else
        {
            emit m_joystick->Joy_MoveBackwardStop();
        }
        break;
    case 65535:
        emit m_joystick->Joy_MoveBackward();
        break;
    }
    old_yPos = yPos;
}

具体的使用方法很简答,建立对象后,只要调用 listen() 函数开始监听就可以了。

当然在监听之前,还要连接好需要的信号和槽。

上一篇:活动目录的介绍:深入浅出Active Directory系列(一)


下一篇:Pell方程及其一般形式