Python Arcade及Pyglet环境下游戏摇杆/手柄无法正常检测的问题求助
Python Arcade 游戏控制器检测异常问题
故障环境与表现
故障复现环境为2台搭载Windows10系统、运行Python3的设备,测试设备包含2款非官方有线Xbox360手柄、1款微软官方无线Xbox360手柄,所有测试场景表现完全一致:
- 仅在手柄刚插入的瞬间,
arcade.get_joysticks()会短暂返回非空结果,随即返回空列表 - 程序启动时手柄已经处于连接状态,调用接口同样返回空列表
- 官方摇杆示例、自行编写的精简Pyglet测试脚本均复现相同问题:加入
sleep延时的脚本完全无法识别手柄;无延时的无限循环检测脚本仅在手柄插拔瞬间短暂返回Joystick对象,之后立刻返回空列表 - 测试全程手柄保持物理连接、硬件功能正常
测试代码与运行输出
初始控制器测试模块(tq_menu)
# tq_menu import arcade class CursorControl(): joystick_count = len(arcade.get_joysticks()) def on_step(self): if (self.joystick_count != len(arcade.get_joysticks())): print("Joystick count changed") print(f"Joy count is: {self.joystick_count}") controller__cursor = CursorControl()
首次运行时程序可短暂识别手柄插入事件,后续测试该功能完全失效。
主游戏测试逻辑
import tq_menu import arcade import pyglet class MainGame(arcade.Window): def __init__(self): super().__init__(600, 600, title="Test Game") self.update_tick_count = 0 print("Initial: " + str(arcade.get_game_controllers())) def on_update(self, delta_time): self.update_tick_count += 1 if (self.update_tick_count % 60 == 0): self.on_update_second() def on_update_second(self): # 约每秒调用一次 print(pyglet.input.get_joysticks()) print("Before: " + str(arcade.get_game_controllers())) print("1 sec") tq_menu.controller__cursor.on_step()
手柄保持连接状态下的程序运行输出:
Initial: [] [] Before: [] 1 sec Joy count is: 0 [] Before: [] 1 sec Joy count is: 0 [<pyglet.input.base.Joystick object at 0x000001D677DC9E40>] Before: [] 1 sec Joy count is: 0 [] Before: [] 1 sec Joy count is: 0
精简Pyglet验证脚本
import pyglet import time while True: joysticks = pyglet.input.get_joysticks() print(joysticks) #time.sleep(0.5)
脚本运行输出:
[] <- 启动时手柄已插入,初始返回空 [] [] [] [] [] [] [] [] [] <- 此处拔下手柄 [] [] [] [] [] [<pyglet.input.base.Joystick object at 0x000001F7992877F0>] <- 此处插回手柄 [] [] [] [] [] [] <- 此处再次拔下手柄 [] [] [] [] [] [] [] [] [] [] [<pyglet.input.base.Joystick object at 0x000001F79AD49CF0>] <- 此处重新插入手柄 [] [] [] [] [] []
修复方案
- 升级依赖到兼容版本:执行
pip install --upgrade arcade pyglet,将Arcade升级到2.6.17以上、Pyglet升级到2.0.10以上版本。旧版本Pyglet在Windows平台存在即插即用设备枚举后被自动回收的已知bug,会导致枚举到的Joystick对象瞬间失效,接口返回空列表。 - 调整设备枚举时机:禁止在类属性定义、模块加载阶段调用
get_joysticks()/get_game_controllers()获取设备列表,必须在Window实例初始化完成、Pyglet事件循环启动后再做枚举。模块加载阶段Pyglet的输入设备管理器还未完成初始化,此时拿到的设备列表完全无效,这也是测试代码中CursorControl类的joystick_count值始终为0的核心原因。 - 枚举到手柄对象后必须手动持有引用并打开设备:不能只临时调用接口拿列表就直接使用,没有被引用的Joystick对象会被Python垃圾回收机制自动释放,后续接口就会返回空。正确的初始化逻辑参考:
class MainGame(arcade.Window): def __init__(self): super().__init__(600, 600, title="Test Game") self.connected_joysticks = [] # 窗口初始化完成后再枚举设备 controllers = arcade.get_game_controllers() for ctrl in controllers: ctrl.open() # 必须调用open打开设备,持有系统设备句柄 self.connected_joysticks.append(ctrl)
- 修正设备检测逻辑:不要脱离Arcade/Pyglet主事件循环,在独立死循环里调用
get_joysticks()做检测。这种写法不会运行Pyglet的系统事件泵,无法正常处理设备插拔事件,只能在插拔瞬间拿到临时设备对象。设备检测逻辑必须放在Window的on_update等主循环回调中执行。 - 驱动与后端适配:Xbox360手柄在Windows10上直接使用系统自带的XInput驱动即可,不要安装第三方老旧的DirectInput版360手柄驱动,这类驱动会和Pyglet的枚举逻辑冲突,导致设备识别后立刻掉线。如果升级依赖后仍存在识别问题,可在代码最开头强制指定Pyglet使用XInput后端枚举手柄,绕过DirectInput的枚举bug:
import pyglet pyglet.options["win32_xinput"] = True import arcade
内容的提问来源于stack exchange,提问作者Pogmog
相关产品推荐
相关产品推荐

