python - 如何让 USB Controller /游戏 handle 与 python 一起工作

标签 python windows pygame pyglet psychopy

我有一个 USB Controller ,我正在尝试从 Microsoft® SideWinder® 即插即用游戏 handle 获取输入。我很难弄清楚如何正确接收其输入。不幸的是,我不能使用 pygame,因为它需要一个窗口来接收输入,但我必须生成一个 pyglet 窗口(通过 PsychoPy)来运行我的程序。使用 pygame,它可以连接并显示按钮的状态,但如果不创建窗口,它就无法接收输入。我尝试寻找其他库,但我遇到的只是输入,它与我的 Controller 不兼容(安装后未检测到设备)。 Controller 本身可以正常工作,因为我已经使用在线游戏 handle 测试仪对其进行了测试。 PsychoPy 的操纵杆 API 目前已损坏且无法正常工作,因此也没有运气。

我真的希望有人对如何从我的 Controller /游戏 handle 接收输入到我的程序中提出建议?

最佳答案

对于 Windows,您可以使用 WINMM.dll直接地。
使用 ctypes加载 dll 的库(参见 Loading shared libraries )。使用 ctypes.WINFUNCTYPE 为函数创建原型(prototype):

  • joyGetNumDevs
  • joyGetDevCapsW
  • joyGetPosEx

  • import ctypes
    
    winmmdll = ctypes.WinDLL('winmm.dll')
    
    # [joyGetNumDevs](https://docs.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetnumdevs)
    """
    UINT joyGetNumDevs();
    """
    joyGetNumDevs_proto = ctypes.WINFUNCTYPE(ctypes.c_uint)
    joyGetNumDevs_func  = joyGetNumDevs_proto(("joyGetNumDevs", winmmdll))
    
    # [joyGetDevCaps](https://docs.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetdevcaps)
    """
    MMRESULT joyGetDevCaps(UINT uJoyID, LPJOYCAPS pjc, UINT cbjc);
    
    32 bit: joyGetDevCapsA
    64 bit: joyGetDevCapsW
    
    sizeof(JOYCAPS): 728
    """
    joyGetDevCaps_proto = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, ctypes.c_uint)
    joyGetDevCaps_param = (1, "uJoyID", 0), (1, "pjc", None), (1, "cbjc", 0)
    joyGetDevCaps_func  = joyGetDevCaps_proto(("joyGetDevCapsW", winmmdll), joyGetDevCaps_param)
    
    # [joyGetPosEx](https://docs.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetposex)
    """
    MMRESULT joyGetPosEx(UINT uJoyID, LPJOYINFOEX pji);
    sizeof(JOYINFOEX): 52
    """
    joyGetPosEx_proto = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p)
    joyGetPosEx_param = (1, "uJoyID", 0), (1, "pji", None)
    joyGetPosEx_func  = joyGetPosEx_proto(("joyGetPosEx", winmmdll), joyGetPosEx_param)
    
    创建python函数joyGetNumDevs , joyGetDevCapsjoyGetPosEx它委托(delegate)给 DLL。并为类型 JOYCAPS 创建类分别 JOYINFOEX :
    # joystickapi - joyGetNumDevs
    def joyGetNumDevs():
        try:
            num = joyGetNumDevs_func()
        except:
            num = 0
        return num
    
    # joystickapi - joyGetDevCaps
    def joyGetDevCaps(uJoyID):
        try:
            buffer = (ctypes.c_ubyte * JOYCAPS.SIZE_W)()
            p1 = ctypes.c_uint(uJoyID)
            p2 = ctypes.cast(buffer, ctypes.c_void_p)
            p3 = ctypes.c_uint(JOYCAPS.SIZE_W)
            ret_val = joyGetDevCaps_func(p1, p2, p3)
            ret = (False, None) if ret_val != JOYERR_NOERROR else (True, JOYCAPS(buffer))   
        except:
            ret = False, None
        return ret 
    
    # joystickapi - joyGetPosEx
    def joyGetPosEx(uJoyID):
        try:
            buffer = (ctypes.c_uint32 * (JOYINFOEX.SIZE // 4))()
            buffer[0] = JOYINFOEX.SIZE
            buffer[1] = JOY_RETURNALL
            p1 = ctypes.c_uint(uJoyID)
            p2 = ctypes.cast(buffer, ctypes.c_void_p)
            ret_val = joyGetPosEx_func(p1, p2)
            ret = (False, None) if ret_val != JOYERR_NOERROR else (True, JOYINFOEX(buffer))   
        except:
            ret = False, None
        return ret 
    
    JOYERR_NOERROR = 0
    JOY_RETURNX = 0x00000001
    JOY_RETURNY = 0x00000002
    JOY_RETURNZ = 0x00000004
    JOY_RETURNR = 0x00000008
    JOY_RETURNU = 0x00000010
    JOY_RETURNV = 0x00000020
    JOY_RETURNPOV = 0x00000040
    JOY_RETURNBUTTONS = 0x00000080
    JOY_RETURNRAWDATA = 0x00000100
    JOY_RETURNPOVCTS = 0x00000200
    JOY_RETURNCENTERED = 0x00000400
    JOY_USEDEADZONE = 0x00000800
    JOY_RETURNALL = (JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | \
                     JOY_RETURNR | JOY_RETURNU | JOY_RETURNV | \
                     JOY_RETURNPOV | JOY_RETURNBUTTONS)
    
    # joystickapi - JOYCAPS
    class JOYCAPS:
        SIZE_W = 728
        OFFSET_V = 4 + 32*2
        def __init__(self, buffer):
            ushort_array = (ctypes.c_uint16 * 2).from_buffer(buffer)
            self.wMid, self.wPid = ushort_array  
    
            wchar_array = (ctypes.c_wchar * 32).from_buffer(buffer, 4)
            self.szPname = ctypes.cast(wchar_array, ctypes.c_wchar_p).value
            
            uint_array = (ctypes.c_uint32 * 19).from_buffer(buffer, JOYCAPS.OFFSET_V) 
            self.wXmin, self.wXmax, self.wYmin, self.wYmax, self.wZmin, self.wZmax, \
            self.wNumButtons, self.wPeriodMin, self.wPeriodMax, \
            self.wRmin, self.wRmax, self.wUmin, self.wUmax, self.wVmin, self.wVmax, \
            self.wCaps, self.wMaxAxes, self.wNumAxes, self.wMaxButtons = uint_array
    
    # joystickapi - JOYINFOEX
    class JOYINFOEX:
      SIZE = 52
      def __init__(self, buffer):
          uint_array = (ctypes.c_uint32 * (JOYINFOEX.SIZE // 4)).from_buffer(buffer) 
          self.dwSize, self.dwFlags, \
          self.dwXpos, self.dwYpos, self.dwZpos, self.dwRpos, self.dwUpos, self.dwVpos, \
          self.dwButtons, self.dwButtonNumber, self.dwPOV, self.dwReserved1, self.dwReserved2 = uint_array
    
    请参阅测试 API 的简单示例:
    import joystickapi
    import msvcrt
    import time
    
    print("start")
    
    num = joystickapi.joyGetNumDevs()
    ret, caps, startinfo = False, None, None
    for id in range(num):
        ret, caps = joystickapi.joyGetDevCaps(id)
        if ret:
            print("gamepad detected: " + caps.szPname)
            ret, startinfo = joystickapi.joyGetPosEx(id)
            break
    else:
        print("no gamepad detected")
    
    run = ret
    while run:
        time.sleep(0.1)
        if msvcrt.kbhit() and msvcrt.getch() == chr(27).encode(): # detect ESC
            run = False
    
        ret, info = joystickapi.joyGetPosEx(id)
        if ret:
            btns = [(1 << i) & info.dwButtons != 0 for i in range(caps.wNumButtons)]
            axisXYZ = [info.dwXpos-startinfo.dwXpos, info.dwYpos-startinfo.dwYpos, info.dwZpos-startinfo.dwZpos]
            axisRUV = [info.dwRpos-startinfo.dwRpos, info.dwUpos-startinfo.dwUpos, info.dwVpos-startinfo.dwVpos]
            if info.dwButtons:
                print("buttons: ", btns)
            if any([abs(v) > 10 for v in axisXYZ]):
                print("axis:", axisXYZ)
            if any([abs(v) > 10 for v in axisRUV]):
                print("roation axis:", axisRUV)
    
    print("end")
    

    api 绑定(bind)和示例在 GitHub 存储库 python_windows_joystickapi 中提供

    关于python - 如何让 USB Controller /游戏 handle 与 python 一起工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60309652/

    相关文章:

    python - 为什么这个 python 脚本要等到计时器线程执行完毕?

    python - 如何在 Windows 上安装 Python 版 pango?

    python - Autokeras 与 Anaconda 的安装

    python - Pygame 幻灯片延迟通常很长

    python - pygame 敌人朝向玩家的二维运动,如何计算 x 和 y 速度?

    python : How to insert a dictionary to a sqlite database?

    python - 每个值都有新列的 Pandas groupby

    python - Pygame删除对象

    python - 修改列表中的斐波那契数列——Python

    c++ - 使用 c++/WinAPI 在 Windows 上计算进程运行实例的可靠方法