跳到主要内容
版本:3.0 Beta 🧪

手柄 API

概述

通过 HID 手柄接口可以让设备模拟标准 USB 游戏控制器,提供按钮控制、模拟摇杆、方向键和扳机功能。这样设备就能作为手柄用于游戏和其他支持控制器的软件。

Python HID 手柄 API 实现位于 Applications/Python/PikaPython/MatrixOS_HID_Gamepad.py,类型提示位于 Applications/Python/PikaPython/_MatrixOS_HID_Gamepad.pyi


MatrixOS.HID.Gamepad.Press

def Press(button_id: int) -> None

按下手柄按钮。

参数:

  • button_id (int):按钮标识符(标准手柄按钮为 0-15)

示例:

# 按下按钮 0(通常是 "A" 或 "X")
MatrixOS.HID.Gamepad.Press(0)

# 按下按钮 1(通常是 "B" 或 "○")
MatrixOS.HID.Gamepad.Press(1)

MatrixOS.HID.Gamepad.Release

def Release(button_id: int) -> None

松开手柄按钮。

参数:

  • button_id (int):要松开的按钮标识符

示例:

# 松开按钮 0
MatrixOS.HID.Gamepad.Release(0)

MatrixOS.HID.Gamepad.ReleaseAll

def ReleaseAll() -> None

松开当前按下的所有手柄按钮。

示例:

# 确保没有按钮卡住
MatrixOS.HID.Gamepad.ReleaseAll()

MatrixOS.HID.Gamepad.Button

def Button(button_id: int, state: bool) -> None

设置特定按钮的状态。

参数:

  • button_id (int):按钮标识符
  • state (bool):True 表示按下,False 表示松开

示例:

# 按下按钮 2
MatrixOS.HID.Gamepad.Button(2, True)

# 松开按钮 2
MatrixOS.HID.Gamepad.Button(2, False)

MatrixOS.HID.Gamepad.Buttons

def Buttons(button_mask: int) -> None

使用位掩码设置多个按钮状态。

参数:

  • button_mask (int):位掩码,每一位代表一个按钮状态

示例:

# 按下按钮 0 和 2(第 0 位和第 2 位设置为 1)
MatrixOS.HID.Gamepad.Buttons(0b00000101) # 二进制:按钮 0 和 2

# 不按任何按钮
MatrixOS.HID.Gamepad.Buttons(0)

模拟摇杆函数

MatrixOS.HID.Gamepad.XAxis

def XAxis(value: int) -> None

设置左摇杆 X 轴位置。

参数:

  • value (int):模拟值(通常 -32767 到 32767,0 = 中心)

示例:

# 中心位置
MatrixOS.HID.Gamepad.XAxis(0)

# 完全向右
MatrixOS.HID.Gamepad.XAxis(32767)

# 完全向左
MatrixOS.HID.Gamepad.XAxis(-32767)

MatrixOS.HID.Gamepad.YAxis

def YAxis(value: int) -> None

设置左摇杆 Y 轴位置。

参数:

  • value (int):模拟值(通常 -32767 到 32767,0 = 中心)

示例:

# 中心位置
MatrixOS.HID.Gamepad.YAxis(0)

# 完全向上
MatrixOS.HID.Gamepad.YAxis(-32767)

# 完全向下
MatrixOS.HID.Gamepad.YAxis(32767)

MatrixOS.HID.Gamepad.ZAxis

def ZAxis(value: int) -> None

设置 Z 轴(通常用于左扳机)。

参数:

  • value (int):模拟值

MatrixOS.HID.Gamepad.RXAxis

def RXAxis(value: int) -> None

设置右摇杆 X 轴位置。

参数:

  • value (int):模拟值(通常 -32767 到 32767,0 = 中心)

MatrixOS.HID.Gamepad.RYAxis

def RYAxis(value: int) -> None

设置右摇杆 Y 轴位置。

参数:

  • value (int):模拟值(通常 -32767 到 32767,0 = 中心)

MatrixOS.HID.Gamepad.RZAxis

def RZAxis(value: int) -> None

设置 RZ 轴(通常用于右扳机)。

参数:

  • value (int):模拟值

方向键函数

MatrixOS.HID.Gamepad.DPad

def DPad(direction: GamepadDPadDirection) -> None

设置方向键(D-pad)方向。

参数:

  • direction (GamepadDPadDirection):D-pad 方向

示例:

# 设置 D-pad 为向上
MatrixOS.HID.Gamepad.DPad(GamepadDPadDirection.UP)

# 设置 D-pad 为中性
MatrixOS.HID.Gamepad.DPad(GamepadDPadDirection.NEUTRAL)

手柄使用示例

基本按钮控制器

def button_controller():
"""用网格按键作为手柄按钮"""

button_map = {
0: 0, # A 按钮
1: 1, # B 按钮
2: 2, # X 按钮
3: 3, # Y 按钮
8: 4, # 左肩键
9: 5, # 右肩键
16: 6, # 左扳机按钮
17: 7, # 右扳机按钮
24: 8, # Back/Select
25: 9, # Start
}

def update_display():
"""在 LED 网格上显示按钮映射"""
MatrixOS.LED.Fill(Color(0, 0, 0), 0)

for key_id, button_id in button_map.items():
xy = MatrixOS.KeyPad.ID2XY(key_id)

# 用颜色区分不同按钮类型
if button_id < 4: # 面键
color = Color(0, 255, 0)
elif button_id < 6: # 肩键
color = Color(255, 255, 0)
elif button_id < 8: # 扳机
color = Color(255, 0, 0)
else: # Start/Select
color = Color(0, 0, 255)

MatrixOS.LED.SetColor(xy, color, 100)

MatrixOS.LED.Update(255)

print("手柄按钮控制器")
print("绿色:面键 (A,B,X,Y)")
print("黄色:肩键")
print("红色:扳机按钮")
print("蓝色:Start/Select")

update_display()

while True:
if not MatrixOS.HID.Ready():
print("HID 未准备好,重新尝试...")
MatrixOS.SYS.DelayMs(1000)
continue

key_event = MatrixOS.KeyPad.Get(100)
if key_event is not None:
key_id = key_event.ID()
key_info = key_event.KeyInfo()

if key_id in button_map:
button_id = button_map[key_id]

if key_info.Active():
MatrixOS.HID.Gamepad.Press(button_id)
print(f"按下手柄按钮 {button_id}")

# 点亮被按下的按钮
xy = MatrixOS.KeyPad.ID2XY(key_id)
MatrixOS.LED.SetColor(xy, Color(255, 255, 255), 255)
MatrixOS.LED.Update(255)
else:
MatrixOS.HID.Gamepad.Release(button_id)
print(f"松开手柄按钮 {button_id}")

# 恢复原色
update_display()

button_controller()

模拟摇杆控制器

def analog_stick_controller():
"""用网格区域作为模拟摇杆控制"""

def map_to_analog(x, y, center_x, center_y, max_range):
"""将网格位置映射到模拟摇杆值"""
dx = x - center_x
dy = y - center_y

# 缩放到模拟范围
analog_x = int((dx / max_range) * 32767)
analog_y = int((dy / max_range) * 32767)

# 限制数值
analog_x = max(-32767, min(32767, analog_x))
analog_y = max(-32767, min(32767, analog_y))

return analog_x, analog_y

def update_display():
"""显示模拟摇杆区域"""
MatrixOS.LED.Fill(Color(0, 0, 0), 0)

# 左摇杆区域(左侧)
for x in range(4):
for y in range(8):
intensity = 100 if x == 1 or x == 2 else 50
MatrixOS.LED.SetColor(Point(x, y), Color(0, 255, 0), intensity)

# 右摇杆区域(右侧)
for x in range(4, 8):
for y in range(8):
intensity = 100 if x == 5 or x == 6 else 50
MatrixOS.LED.SetColor(Point(x, y), Color(0, 0, 255), intensity)

# 中心指示器
MatrixOS.LED.SetColor(Point(1, 3), Color(255, 255, 255), 255) # 左中心
MatrixOS.LED.SetColor(Point(5, 3), Color(255, 255, 255), 255) # 右中心

MatrixOS.LED.Update(255)

print("模拟摇杆控制器")
print("左侧:左摇杆(绿色)")
print("右侧:右摇杆(蓝色)")
print("白点:中心位置")

update_display()

while True:
if not MatrixOS.HID.Ready():
print("HID 未准备好,重新尝试...")
MatrixOS.SYS.DelayMs(1000)
continue

key_event = MatrixOS.KeyPad.Get(50)
if key_event is not None:
key_id = key_event.ID()
key_info = key_event.KeyInfo()
xy = MatrixOS.KeyPad.ID2XY(key_id)

if key_info.Active():
if xy.x < 4: # 左摇杆区域
analog_x, analog_y = map_to_analog(xy.x, xy.y, 1.5, 3.5, 2)
MatrixOS.HID.Gamepad.XAxis(analog_x)
MatrixOS.HID.Gamepad.YAxis(analog_y)
print(f"左摇杆:({analog_x}, {analog_y})")

# 视觉反馈
MatrixOS.LED.SetColor(xy, Color(255, 255, 255), 255)
MatrixOS.LED.Update(255)

elif xy.x >= 4: # 右摇杆区域
analog_x, analog_y = map_to_analog(xy.x, xy.y, 5.5, 3.5, 2)
MatrixOS.HID.Gamepad.RXAxis(analog_x)
MatrixOS.HID.Gamepad.RYAxis(analog_y)
print(f"右摇杆:({analog_x}, {analog_y})")

# 视觉反馈
MatrixOS.LED.SetColor(xy, Color(255, 255, 255), 255)
MatrixOS.LED.Update(255)

else:
# 按键松开 - 回到中心
if xy.x < 4: # 左摇杆
MatrixOS.HID.Gamepad.XAxis(0)
MatrixOS.HID.Gamepad.YAxis(0)
elif xy.x >= 4: # 右摇杆
MatrixOS.HID.Gamepad.RXAxis(0)
MatrixOS.HID.Gamepad.RYAxis(0)

# 恢复显示
update_display()

analog_stick_controller()

完整手柄模拟器

def full_gamepad_emulator():
"""完整手柄,包含按钮、摇杆和 D-pad"""

# 按钮映射
face_buttons = {56: 0, 57: 1, 48: 2, 49: 3} # A, B, X, Y(右下区域)
shoulder_buttons = {8: 4, 15: 5} # L1, R1(顶部角落)
start_select = {7: 8, 0: 9} # Select, Start(顶部角落)

# 模拟摇杆死区
current_left_stick = [0, 0]
current_right_stick = [0, 0]

def update_display():
"""更新手柄布局显示"""
MatrixOS.LED.Fill(Color(0, 0, 0), 0)

# 左摇杆区域(绿色)
for x in range(3):
for y in range(2, 6):
MatrixOS.LED.SetColor(Point(x, y), Color(0, 100, 0), 50)

# 右摇杆区域(蓝色)
for x in range(5, 8):
for y in range(2, 6):
MatrixOS.LED.SetColor(Point(x, y), Color(0, 0, 100), 50)

# 面键(黄色)
for key_id in face_buttons.keys():
xy = MatrixOS.KeyPad.ID2XY(key_id)
MatrixOS.LED.SetColor(xy, Color(200, 200, 0), 100)

# 肩键(红色)
for key_id in shoulder_buttons.keys():
xy = MatrixOS.KeyPad.ID2XY(key_id)
MatrixOS.LED.SetColor(xy, Color(200, 0, 0), 100)

# Start/Select(紫色)
for key_id in start_select.keys():
xy = MatrixOS.KeyPad.ID2XY(key_id)
MatrixOS.LED.SetColor(xy, Color(200, 0, 200), 100)

# D-pad(青色) - 中心区域
dpad_keys = [27, 35, 36, 28] # 上、下、右、左
for key_id in dpad_keys:
xy = MatrixOS.KeyPad.ID2XY(key_id)
MatrixOS.LED.SetColor(xy, Color(0, 200, 200), 100)

MatrixOS.LED.Update(255)

def handle_analog_stick(xy, is_left_stick):
"""处理模拟摇杆输入"""
if is_left_stick:
# 左摇杆区域 (0-2, 2-5)
center_x, center_y = 1, 3.5
max_range = 2

dx = xy.x - center_x
dy = xy.y - center_y

analog_x = int((dx / max_range) * 32767)
analog_y = int((dy / max_range) * 32767)

# 限制并设置
analog_x = max(-32767, min(32767, analog_x))
analog_y = max(-32767, min(32767, analog_y))

MatrixOS.HID.Gamepad.XAxis(analog_x)
MatrixOS.HID.Gamepad.YAxis(analog_y)

current_left_stick[0] = analog_x
current_left_stick[1] = analog_y

print(f"左摇杆:({analog_x}, {analog_y})")

else:
# 右摇杆区域 (5-7, 2-5)
center_x, center_y = 6, 3.5
max_range = 2

dx = xy.x - center_x
dy = xy.y - center_y

analog_x = int((dx / max_range) * 32767)
analog_y = int((dy / max_range) * 32767)

# 限制并设置
analog_x = max(-32767, min(32767, analog_x))
analog_y = max(-32767, min(32767, analog_y))

MatrixOS.HID.Gamepad.RXAxis(analog_x)
MatrixOS.HID.Gamepad.RYAxis(analog_y)

current_right_stick[0] = analog_x
current_right_stick[1] = analog_y

print(f"右摇杆:({analog_x}, {analog_y})")

def handle_dpad(key_id):
"""处理 D-pad 输入"""
dpad_map = {
27: GamepadDPadDirection.UP,
35: GamepadDPadDirection.DOWN,
36: GamepadDPadDirection.RIGHT,
28: GamepadDPadDirection.LEFT
}

if key_id in dpad_map:
MatrixOS.HID.Gamepad.DPad(dpad_map[key_id])
return True
return False

print("完整手柄模拟器")
print("左区域:左摇杆")
print("右区域:右摇杆")
print("中间:D-pad")
print("角落:肩键、Start/Select")
print("右下:面键")

update_display()

active_sticks = set()

while True:
if not MatrixOS.HID.Ready():
print("HID 未准备好,重新尝试...")
MatrixOS.SYS.DelayMs(1000)
continue

key_event = MatrixOS.KeyPad.Get(30)
if key_event is not None:
key_id = key_event.ID()
key_info = key_event.KeyInfo()
xy = MatrixOS.KeyPad.ID2XY(key_id)

if key_info.Active():
# 面键
if key_id in face_buttons:
MatrixOS.HID.Gamepad.Press(face_buttons[key_id])
print(f"按下面键 {face_buttons[key_id]}")

# 肩键
elif key_id in shoulder_buttons:
MatrixOS.HID.Gamepad.Press(shoulder_buttons[key_id])
print(f"按下肩键 {shoulder_buttons[key_id]}")

# Start/Select
elif key_id in start_select:
MatrixOS.HID.Gamepad.Press(start_select[key_id])
print(f"按下 start/select {start_select[key_id]}")

# D-pad
elif handle_dpad(key_id):
print("D-pad 激活")

# 左摇杆
elif xy.x < 3 and 2 <= xy.y <= 5:
handle_analog_stick(xy, True)
active_sticks.add(('left', key_id))

# 右摇杆
elif xy.x >= 5 and 2 <= xy.y <= 5:
handle_analog_stick(xy, False)
active_sticks.add(('right', key_id))

# 视觉反馈
MatrixOS.LED.SetColor(xy, Color(255, 255, 255), 255)
MatrixOS.LED.Update(255)

else:
# 按键松开
if key_id in face_buttons:
MatrixOS.HID.Gamepad.Release(face_buttons[key_id])
elif key_id in shoulder_buttons:
MatrixOS.HID.Gamepad.Release(shoulder_buttons[key_id])
elif key_id in start_select:
MatrixOS.HID.Gamepad.Release(start_select[key_id])
elif key_id in [27, 35, 36, 28]: # D-pad
MatrixOS.HID.Gamepad.DPad(GamepadDPadDirection.NEUTRAL)

# 处理摇杆松开
active_sticks = {(stick, kid) for stick, kid in active_sticks if kid != key_id}

# 如果该区域没有激活的按键,就把摇杆回到中心
left_active = any(stick == 'left' for stick, _ in active_sticks)
right_active = any(stick == 'right' for stick, _ in active_sticks)

if not left_active:
MatrixOS.HID.Gamepad.XAxis(0)
MatrixOS.HID.Gamepad.YAxis(0)

if not right_active:
MatrixOS.HID.Gamepad.RXAxis(0)
MatrixOS.HID.Gamepad.RYAxis(0)

# 恢复显示
update_display()

full_gamepad_emulator()

手柄 D-Pad 方向

GamepadDPadDirection 中可用的方向:

  • NEUTRAL:无方向按下
  • UP:D-pad 向上
  • DOWN:D-pad 向下
  • LEFT:D-pad 向左
  • RIGHT:D-pad 向右
  • UP_LEFT:D-pad 左上对角线
  • UP_RIGHT:D-pad 右上对角线
  • DOWN_LEFT:D-pad 左下对角线
  • DOWN_RIGHT:D-pad 右下对角线

Comments