DirectInput手柄在Windows环境下震动实现

DirectInput手柄Windows环境下震动实现

  • 背景
  • 1.direcrInput手柄震动控制
    • 1.1运行环境
    • 1.2代码实现
  • 2 模拟XInput设备,通过XInput实现
    • 2.1 x360ce设置
  • 2.2 XInput 代码实现
    • 2.3 x360ce分析

背景

近期项目里面有个需求,需要在控制终端上安装摇杆,且需要通过震动反馈设备的某些重要运行状态。自以为机智的自己在万能的某宝买了一个飞行摇杆,带震动反馈,DInput接口,结果是踩坑的开始。
Windows下手柄分为两类,XInput和DirectInput,XInput主要是Xbox系列手柄,比较贵,亲儿子;市面上大多杂牌的手柄都只支持DirectInput。查阅官方文档:xinput and directinput 心凉了半截:The vibration effects will not be available,意思就是directinput不再支持手柄震动了。不过directInput有一个directInputEffect,从文档看是力反馈,论坛说也能拿来做震动,因此先用directInput做一下尝试。

1.direcrInput手柄震动控制

1.1运行环境

环境搭建就不再赘述,主要就是选一个win10的tool kit,已经集成了directx。Qt是项目中用于界面搭建的环境。
(1)windows 10 1903
(2)Qt 5.9

1.2代码实现

github 上有比较完整的 QGameController,能够实现手柄的识别、各参数的读取,封装比较完整,但是没有手柄震动的驱动。本着不重复造轮子的原则,从这个开源项目开始进行代码实现。

    DWORD dwAxisX = DIJOFS_X;  //一个震动电机
    LONG lDirecX = 0;
    DIPERIODIC diPeriodic;      
    ZeroMemory(&diPeriodic, sizeof(DIPERIODIC));
    DICONSTANTFORCE diConstantForce;
    ZeroMemory(&diConstantForce, sizeof(DICONSTANTFORCE));
    DIEFFECT   diEffect;        // general parameters
    // set up the effect structure itself
    diEffect.dwSize = sizeof(DIEFFECT);
    diEffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
    diEffect.dwDuration = (DWORD) INFINITE; 
    diEffect.dwStartDelay = 0;
    // set up details of effect
    diEffect.dwSamplePeriod = 0;               
    diEffect.dwGain = lVibraStrength;      
    diEffect.dwTriggerButton = DIEB_NOTRIGGER;
     // connect effect to trigger button
    diEffect.dwTriggerRepeatInterval = 0;
    diEffect.cAxes = 1;
    diEffect.rgdwAxes = &dwAxisX;
    diEffect.rglDirection = &lDirecX;
    diEffect.lpEnvelope = 0;

    diConstantForce.lMagnitude = DI_FFNOMINALMAX;
    diEffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
    diEffect.lpvTypeSpecificParams = &diConstantForce;

    // create the effect and get the interface to it
    hr = g_pJoystick->CreateEffect(GUID_ConstantForce,  // standard GUID
                         &diEffect,      // where the data is
                         &lpdieffect,    // where to put interface pointer
                         NULL);          // no aggregation
    if(FAILED(hr))
    {
        qDebug()<< "create effect failed";
    }
    g_pJoystick->SendForceFeedbackCommand(DISFFC_RESET);
    //g_pJoystick->SendForceFeedbackCommand(DISFFC_SETACTUATORSON);
    if( FAILED(g_pJoystick->SendForceFeedbackCommand(DISFFC_SETACTUATORSON)))
        qDebug() << "start failed";
    hr = lpdieffect->Download();
    if(FAILED(hr))
        qDebug("download failed, the error NO is %X",hr);
    hr = lpdieffect->Start(INFINITE,DIES_SOLO);
    if(FAILED(hr))
        qDebug("start failed, the error NO is %X",hr);

代码有些冗长,参照了策随心和code从业员两位大佬的一些参数设置,虽然用的语言不一样,但是道理都是一样的。
代码运行并没有那么顺利,手柄在download和start以后,并没有反应,整个世界都是清净的。查找错误代码是0x80040205 DIERR_NOTEXCLUSIVEACQUIRED,注释是:The operation cannot be performed unless the device is acquired in DISCL_EXCLUSIVE mode。意思是这个操作在独占模式下才可以实现。因此我在create前面加了:

    if( FAILED(hr = g_pJoystick->SetCooperativeLevel(windID,DISCL_BACKGROUND|DISCL_EXCLUSIVE)))
        qDebug("error set coopreative, error NO is %X",hr);

运行,世界还是一片寂静。这个bug直到现在都没调好,有大佬知道为啥,请告诉我!

2 模拟XInput设备,通过XInput实现

deadline越来越近,不能在一条路上撞死。逛论坛找大神的时候,发现DInput的设备可以通过软件模拟,变成XInput设备。在微软提供的XInput库中,有函数直接可以实现vibration。尝试了多个软件以后,找到了开源的x360ce,程序和源代码都能在官网上找到。

2.1 x360ce设置

在完成相关设置后,可以一键autosetting。上面一排标签可以看到Force Feedback,在这个选项卡中,拖动test的进度条后,手柄震动起来了,第一步完成。DirectInput手柄在Windows环境下震动实现_第1张图片

2.2 XInput 代码实现

在qt中搭一个简单界面,按键做震动开关。把下面的代码块放到按键的槽函数中。

    XINPUT_VIBRATION vibration;
    ZeroMemory( &vibration, sizeof(XINPUT_VIBRATION) );
    vibration.wLeftMotorSpeed = LeftMotorSpeed; 
    vibration.wRightMotorSpeed = RightMotorSpeed; 
    XInputSetState( uID, &vibration );

把x360ce中生成的xinput1_3.dll放到程序的运行目录下面,点击运行,动了!果然是亲儿子,这么小的代码量就能解决上面一片的所有问题。

2.3 x360ce分析

看了一下开源的代码,这个软件主要分为两大部分,一部分是生成dll文件,C++;一部分是界面,C#。
x360ce_dll工程里,代码量不大,主要是通过DirectInput控制手柄,再给XInput提供接口。震动的实时是调用XInputSetState,而不是directInputEffect.start,所以没有出现上面的独占问题。

你可能感兴趣的:(DirectInput,windows,directx,qt,c++)