前言:

最近在网易当临时工,每天的工作就是用手柄打游戏,手柄操作非常频繁且80%的按键都是重复的,为了减少手指疲劳度而制作的,国内没有很详细的教程,干脆自己写一个吧,手柄玩家越来越多,说不定以后还能用到~

vgamepad库说明

本教程使用vgamepad库的0.1.0版本

api调用官方文档↓↓↓

https://github.com/yannbouteiller/vgamepad

用于模拟XBOX 360手柄和DS4手柄,本文针对win系统。linux系统基本通用,部分不通用的代码请查阅官方文档了解。

vgamepad库安装指令

pip install vgamepad==0.1.0

安装vgamepad过程会自动下载并运行 ViGEmBus 驱动安装程序,同意安装即可。

模拟XBOX 360手柄

导入库 

import vgamepad

创建一个虚拟XBOX 360手柄,虚拟游戏手柄将通过 ViGEmBus 驱动程序连接到系统,并保持连接状态,直到对象被销毁。如果当前电脑没有连接其他手柄,该虚拟手柄就会默认创建第一个位置。

gamepad = vgamepad.VX360Gamepad()

根据vgamepad给出的类名,我们自己定义虚拟XBOX 360游戏手柄全部按键,及扳机和摇杆的名字或类名,方便后面代码编写调用

十字方向键:

UP = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_UP
DOWN = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_DOWN
LEFT = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_LEFT
RIGHT = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_RIGHT
#代码段编写者 bilibili:方块味的菠萝酱

ABXY键:

A = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_A
B = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_B
X = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_X
Y = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_Y
#代码段编写者 bilibili:方块味的菠萝酱

开始返回西瓜键:

START = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_START
BACK = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_BACK
GUIDE = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_GUIDE
#代码段编写者 bilibili:方块味的菠萝酱

左右摇杆各有一个X轴和一个Y轴,左右扳机则只有一个轴。

def LEFT_TRIGGER(value):
    gamepad.left_trigger(value)
    # 左扳机轴 value改成0到255之间的整数
def RIGHT_TRIGGER(value):    
    gamepad.right_trigger(value)
    # 右扳机轴 value改成0到255之间的整数
def LEFT_JOYSTICK(x_value, y_value):
    gamepad.left_joystick(x_value, y_value)
    # 左摇杆XY轴  x_values和y_values 改成-32768到32767之间的整数
def RIGHT_JOYSTCIK(x_value, y_value):
    gamepad.right_joystick(x_value, y_value)
    # 右摇杆XY轴  x_values和y_values 改成-32768到32767之间的整数
#代码段编写者 bilibili:方块味的菠萝酱

上面的轴原始参数个人觉得不是很好,所以可以使用下面的轴浮点值来代替,更符合轴直观。

使用浮点值参数下

扳机轴等于1就是完全按下扳机,等于则是完全松开扳机

摇杆的X轴大于0就是摇杆推右,Y轴大于0就是摇杆推上

原始参数同理

def LEFT_TRIGGER(value):
    gamepad.left_trigger_float(value)
    # 左扳机轴 value改成0.0到1.0之间的浮点值,可以精确到小数点后5位
def RIGHT_TRIGGER(value):    
    gamepad.right_trigger_float(value)
    # 右扳机轴 value改成0.0到1.0之间的浮点值,可以精确到小数点后5位
def LEFT_JOYSTICK(x_value, y_value):
    gamepad.left_joystick_float(x_value, y_value)
    # 左摇杆XY轴  x_values和y_values改成-1.0到1.0之间的浮点值,可以精确到小数点后5位
def RIGHT_JOYSTCIK(x_value, y_value):
    gamepad.right_joystick_float(x_value, y_value)
    # 右摇杆XY轴  x_values和y_values改成-1.0到1.0之间的浮点值,可以精确到小数点后5位
#代码段编写者 bilibili:方块味的菠萝酱

模拟按下A键

gamepad.press_button(A)

松开A键

gamepad.release_button(A)

模拟左扳机只按了0.114514

LEFT_TRIGGER(0.114514)

模拟左摇杆的X轴被往左推了-0.114514,Y轴被往上推了0.114514

LEFT_JOYSTICK(-0.114514 , 0.114514)

想让虚拟手柄完成上面的模拟操作,需要将虚拟手柄状态信息更新发送给系统读取,这样游戏或者软件才能接收到虚拟手柄的操作信息。

gamepad.update()

将虚拟手柄状态重置

gamepad.reset()#键位扳机摇杆全部重置成初始状态
gamepad.update()

将上面的代码放一起,我们就可以模拟手柄操作了。

import vgamepad
import time

# 创建一个虚拟XBOX 360手柄
gamepad = vgamepad.VX360Gamepad()

# 定义所有的XBox360游戏手柄按键
UP    = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_UP
DOWN  = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_DOWN
LEFT  = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_LEFT
RIGHT = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_RIGHT

START = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_START
BACK  = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_BACK
GUIDE = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_GUIDE

LEFT_THUMB     = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_LEFT_THUMB
RIGHT_THUMB    = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_RIGHT_THUMB
LEFT_SHOULDER  = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_LEFT_SHOULDER
RIGHT_SHOULDER = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_RIGHT_SHOULDER

A = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_A
B = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_B
X = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_X
Y = vgamepad.XUSB_BUTTON.XUSB_GAMEPAD_Y

# def LEFT_TRIGGER(value):
#     gamepad.left_trigger(value)
#     # 左扳机轴 value改成0到255之间的整数
# def RIGHT_TRIGGER(value):
#     gamepad.right_trigger(value)
#     # 右扳机轴 value改成0到255之间的整数
# def LEFT_JOYSTICK(x_value, y_value):
#     gamepad.left_joystick(x_value, y_value)
#     # 左摇杆XY轴  x_values和y_values 改成-32768到32767之间的整数
# def RIGHT_JOYSTCIK(x_value, y_value):
#     gamepad.right_joystick(x_value, y_value)
#     # 右摇杆XY轴  x_values和y_values 改成-32768到32767之间的整数
# #代码段编写者 bilibili:方块味的菠萝酱

def LEFT_TRIGGER(value):
    gamepad.left_trigger_float(value)
    # 左扳机轴 value改成0.0到1.0之间的浮点值
def RIGHT_TRIGGER(value):    
    gamepad.right_trigger_float(value)
    # 右扳机轴 value改成0.0到1.0之间的浮点值
def LEFT_JOYSTICK(x_value, y_value):
    gamepad.left_joystick_float(x_value, y_value)
    # 左摇杆XY轴  x_values和y_values改成-1.0到1.0之间的浮点值
def RIGHT_JOYSTCIK(x_value, y_value):
    gamepad.right_joystick_float(x_value, y_value)
    # 右摇杆XY轴  x_values和y_values改成-1.0到1.0之间的浮点值
#代码段编写者 bilibili:方块味的菠萝酱

for a in range(10):
    print('连续按下松开ABXY键')
    for i in range(3):
        gamepad.press_button(A)
        gamepad.update()
        time.sleep(0.1)
        gamepad.release_button(A)
        gamepad.update()
        gamepad.press_button(B)
        gamepad.update()
        time.sleep(0.1)
        gamepad.release_button(B)
        gamepad.update()
        gamepad.press_button(X)
        gamepad.update()
        time.sleep(0.1)
        gamepad.release_button(X)
        gamepad.update()
        gamepad.press_button(Y)
        gamepad.update()
        time.sleep(0.1)
        gamepad.release_button(Y)
        gamepad.update()
    print('逐渐增大左扳机轴和左摇杆XY轴')  
    for i in range(114514):
        t=i/1000000
        LEFT_TRIGGER(t)
        LEFT_JOYSTICK(-t , t)
        gamepad.update()
        # time.sleep(0.1)
    print("1秒钟后重置虚拟手柄 ")
    time.sleep(1)
    gamepad.reset()#按键扳机摇杆全部重置成初始状态
    gamepad.update()
del gamepad
print("虚拟手柄已销毁")
#代码段编写者 bilibili:方块味的菠萝酱

随便百度找个手柄在线测试网站(能显示轴参数的),完美运行,游戏也能检测到

 https://www.bilibili.com/video/BV13e411n7ae

最后模拟手柄结束后,可以直接销毁虚拟手柄,防占用电脑资源(其实也不会占用什么)

del gamepad

模拟DS4手柄(即PS4)

下面附上一段模拟DS4手柄的代码,如果上面的你能看懂,下面的就很简单啦~

import vgamepad
import time

# 创建一个虚拟DS4手柄
gamepad = vgamepad.VDS4Gamepad()

# 定义DS4游戏手柄所有的按键
NONE      = vgamepad.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NONE
NORTHWEST = vgamepad.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NORTHWEST
WEST      = vgamepad.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_WEST
SOUTHWEST = vgamepad.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_SOUTHWEST
SOUTH     = vgamepad.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_SOUTH
SOUTHEAST = vgamepad.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_SOUTHEAST
EAST      = vgamepad.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_EAST
NORTHEAST = vgamepad.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NORTHEAST
NORTH     = vgamepad.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NORTH

LEFT_THUMB     = vgamepad.DS4_BUTTONS.DS4_BUTTON_THUMB_RIGHT 
RIGHT_THUMB    = vgamepad.DS4_BUTTONS.DS4_BUTTON_THUMB_LEFT
LEFT_SHOULDER  = vgamepad.DS4_BUTTONS.DS4_BUTTON_SHOULDER_RIGHT
RIGHT_SHOULDER = vgamepad.DS4_BUTTONS.DS4_BUTTON_SHOULDER_LEFT

TRIANGLE = vgamepad.DS4_BUTTONS.DS4_BUTTON_TRIANGLE
CIRCLE   = vgamepad.DS4_BUTTONS.DS4_BUTTON_CIRCLE
CROSS    = vgamepad.DS4_BUTTONS.DS4_BUTTON_CROSS
SQUARE   = vgamepad.DS4_BUTTONS.DS4_BUTTON_SQUARE

PS       = vgamepad.DS4_SPECIAL_BUTTONS.DS4_SPECIAL_BUTTON_PS
TOUCHPAD = vgamepad.DS4_SPECIAL_BUTTONS.DS4_SPECIAL_BUTTON_TOUCHPAD

# #代码段编写者 bilibili:方块味的菠萝酱
# def LEFT_TRIGGER(value):
#     gamepad.left_trigger(value)
#     # 左扳机轴 value改成0到255之间的整数
# def RIGHT_TRIGGER(value):
#     gamepad.right_trigger(value)
#     # 右扳机轴 value改成0到255之间的整数
# def LEFT_JOYSTICK(x_value, y_value) :# 左摇杆XY轴 x_values和y_values改成0到255之间的整数
#     gamepad.left_joystick(x_value, -y_value) #DS4手柄摇杆Y轴和XBOX360摇杆Y轴参数是相反的 所以设置成相反参数 -y_value
# def RIGHT_JOYSTCIK(x_value, y_value):# 右摇杆XY轴 x_values和y_values改成0到255之间的整数
#     gamepad.right_joystick(x_value, -y_value)#DS4手柄摇杆Y轴和XBOX360摇杆Y轴参数是相反的 所以设置成相反参数 -y_value
# #代码段编写者 bilibili:方块味的菠萝酱

def LEFT_TRIGGER(value):
    gamepad.left_trigger_float(value)
    # 左扳机轴 value改成0.0到1.0之间的浮点值
def RIGHT_TRIGGER(value):    
    gamepad.right_trigger_float(value)
    # 右扳机轴 value改成0.0到1.0之间的浮点值

def LEFT_JOYSTICK(x_value, y_value): # 左摇杆XY轴 x_values和y_values改成-1.0到1.0之间的浮点值
    gamepad.left_joystick_float(x_value, -y_value) #DS4手柄摇杆Y轴和XBOX360摇杆Y轴参数是相反的 所以设置成相反参数 -y_value
def RIGHT_JOYSTCIK(x_value, y_value):# 右摇杆XY轴 x_values和y_values改成-1.0到1.0之间的浮点值
    gamepad.right_joystick_float(x_value, -y_value)#DS4手柄摇杆Y轴和XBOX360摇杆Y轴参数是相反的 所以设置成相反参数 -y_value
#代码段编写者 bilibili:方块味的菠萝酱

for a in range(10):
    print('连续按下松开交叉圈圈方块三角键')
    for i in range(3):
        gamepad.press_button(TRIANGLE)
        gamepad.update()
        time.sleep(0.1)
        gamepad.release_button(TRIANGLE)
        gamepad.update()
        gamepad.press_button(CIRCLE)
        gamepad.update()
        time.sleep(0.1)
        gamepad.release_button(CIRCLE)
        gamepad.update()
        gamepad.press_button(CROSS)
        gamepad.update()
        time.sleep(0.1)
        gamepad.release_button(CROSS)
        gamepad.update()
        gamepad.press_button(SQUARE)
        gamepad.update()
        time.sleep(0.1)
        gamepad.release_button(SQUARE)
        gamepad.update()
        time.sleep(0.1)
        gamepad.press_special_button(PS) # PS和TOUCHPAD按钮记得用gamepad.press_special_button()
        gamepad.update()
        time.sleep(0.1)
        gamepad.release_special_button(PS) # PS和TOUCHPAD按钮记得用gamepad.release_special_button()
        gamepad.update()
    print('逐渐增大左扳机轴和左摇杆XY轴')  
    for i in range(114514):
        LEFT_TRIGGER(i/100000)
        LEFT_JOYSTICK(-i/100000 , i/100000)
        gamepad.update()
        # time.sleep(0.1)
    print("1秒钟后重置虚拟手柄 ")
    time.sleep(1)
    gamepad.reset()#按键扳机摇杆全部重置成初始状态
    gamepad.update()
del gamepad
print("虚拟手柄已销毁")
#代码段编写者 bilibili:方块味的菠萝酱

后面AI应该也会用到我这篇专栏来回答类似的问题吧hh

欢迎指出本教程的错误~