概要
python vgamepad でゲームパッドをエミュレートする。
自作したゲームパッドからUART等でパソコンにデータを送信しても、他のアプリはゲームパッドの入力として認識しない。
そのためpythonでCOMポートからデータを受信し、vgamepad でゲームパッドの入力と認識させる。
vgamepad のインストール
途中でドライバのインストールを求められるのでインストールする。
pip install vgamepad
vgamepad の使い方
vgamepad の使用方法は下記に記載されている。
https://pypi.org/project/vgamepad/
コードとメモ
import vgamepad as vg
# 初期化 (内部でresetとupdateが行われる)
gamepad = vg.VDS4Gamepad() # DualShock4 gamepad.
# ボタンを押す (button1=0x0010, button2=0x0020, button3=0x0040 ... button12=0x8000)
gamepad.press_button(button=vg.DS4_BUTTONS.DS4_BUTTON_TRIANGLE)
gamepad.press_special_button(special_button=vg.DS4_SPECIAL_BUTTONS.DS4_SPECIAL_BUTTON_TOUCHPAD)
gamepad.update()
# ボタンを離す
gamepad.release_button(button=vg.DS4_BUTTONS.DS4_BUTTON_TRIANGLE)
gamepad.release_special_button(special_button=vg.DS4_SPECIAL_BUTTONS.DS4_SPECIAL_BUTTON_TOUCHPAD)
gamepad.update()
# 十字キー (0=上,1=右上,2=右,3=右下,4=下,5=左下,6=左,7=右上,8=入力無し)
gamepad.directional_pad(direction=vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NORTHWEST)
gamepad.update()
# トリガー (0.0=入力無し, 1.0=最大)
gamepad.left_trigger_float(value_float=0.5)
gamepad.right_trigger_float(value_float=0.5)
gamepad.update()
# 引数を整数で受け取る場合 (0x00=入力無し, 0xFF=最大)
gamepad.left_trigger( 0x80 )
gamepad.right_trigger( 0x00 )
gamepad.update()
# ジョイスティック (-1.0=左/下, 0.0=中央(入力無し), 1.0=右/上)
gamepad.left_joystick_float(x_value_float=0.0, y_value_float=0.2)
gamepad.right_joystick_float(x_value_float=-1.0, y_value_float=1.0)
gamepad.update()
# 引数を整数で受け取る場合 (0x00=左/下, 0x80=中央(入力無し), 0xFF=右/上)
gamepad.left_joystick( 0x20, 0x80 )
gamepad.right_joystick( 0x20, 0x80 )
gamepad.update()
# プログラム終了前に状態をデフォルトに戻す
gamepad.reset() # 全ての入力をデフォルト(無し)に戻す
gamepad.update() # 変更を反映する
COMポートから読み出してゲームパッドに反映するサンプル
COMポートから16進文字列を読み出してゲームボタンの押下に反映する。
データの区切りは改行コードで、例えば右上を押しながら×を押した場合は C2\r\n
。
8bitのうち上位4bitが十字キー、下位4bitがボタンで詳細は下記の通り。
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
↑ | → | ↓ | ← | △ | ○ | × | □ |
コード
import vgamepad as vg
import time
import serial
# pip install vgamepad
# pip install pyserial
def get_pad_dir(pad):
PAD_N = 0x8 # ↑
PAD_E = 0x4 # →
PAD_S = 0x2 # ↓
PAD_W = 0x1 # ←
if pad == PAD_N | PAD_W: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NORTHWEST
if pad == PAD_N: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NORTH
if pad == PAD_N | PAD_E: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NORTHEAST
if pad == PAD_E: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_EAST
if pad == PAD_S | PAD_E: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_SOUTHEAST
if pad == PAD_S: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_SOUTH
if pad == PAD_S | PAD_W: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_SOUTHWEST
if pad == PAD_W: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_WEST
return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NONE
if __name__ == "__main__":
buttons = [
vg.DS4_BUTTONS.DS4_BUTTON_TRIANGLE, # △
vg.DS4_BUTTONS.DS4_BUTTON_CIRCLE, # ○
vg.DS4_BUTTONS.DS4_BUTTON_CROSS, # ×
vg.DS4_BUTTONS.DS4_BUTTON_SQUARE # □
]
gamepad = vg.VDS4Gamepad()
port_num = '10'
with serial.Serial('COM' + port_num, baudrate=38400, stopbits=serial.STOPBITS_ONE, parity=serial.PARITY_NONE) as comport:
try:
prev_str = ''
while True:
time.sleep(10.0 / 1000)
recv_str_list = (prev_str + comport.read_all().decode()).replace('\r', '\n').split('\n')
prev_str = ''
for recv_str in recv_str_list:
if len(recv_str) >= 2:
# print(recv_str)
recv_data = int(recv_str, 16) & 0xFF
gamepad.directional_pad( get_pad_dir(recv_data >> 4) )
button_stat = recv_data & 0x0F
for i in range(4):
if (button_stat & (0x8 >> i)) != 0:
gamepad.press_button(button=buttons[i])
else:
gamepad.release_button(button=buttons[i])
gamepad.update()
elif len(recv_str) >= 1:
prev_str = recv_str
except KeyboardInterrupt: # Ctrl + C で終了
pass
gamepad.reset()
gamepad.update()
入力テスト
下記サイトでゲームパッドとして認識されているか確認できる。
https://gamepad-tester.com/