手柄 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