Golang获取游戏手柄输入及控制器类型的方法
Golang获取游戏手柄输入及控制器类型的方法 大家好 我目前正在使用 GitHub - 0xcafed00d/joystick: Go Joystick API 来获取手柄状态——在基本形式上,它可以在 Windows 上配合 Xbox360(兼容)手柄、DualShock 4 以及 Legion Go 手柄工作。
然而,DS4 和 Xbox 手柄的面部按钮(我确信摇杆轴也是如此)的按键映射并不相同,也就是说,我希望这些按钮能对应相同的(掩码)值:
- Ⓐ/✕
- Ⓑ/◯
- Ⓧ/☐
- Ⓨ/🛆
似乎没有办法识别控制器类型(使用底层的 Windows 多媒体(旧版)dll/api)。
有没有人知道有哪个包能更好地处理这个问题——而不需要采用整个框架?请注意,这不是用于游戏开发,而是用于一个可以响应游戏手柄输入的后台(操作系统级别)实用程序。
到目前为止,我考虑过的方案包括:
- 我可以猜测控制器类型,因为 DS4 返回的按钮和摇杆轴数量与 Xbox360 不同——但我找不到任何地方有相关文档,所以这只是基于 2/3 个控制器的猜测……
- 自己封装 Windows 调用——但这似乎有些过头了(而且是操作系统特定的),而且我在这方面没有任何经验,很可能是在重复造轮子……
- Windows Gaming Input——但我没有看到任何 Golang 访问该 API 的例子,而且看起来你可能需要购买整个 SDK。
- SDL——但这对于我的目的来说看起来太臃肿了,而且看起来也不太可能解决这个问题。
请注意,我更喜欢跨平台的解决方案,但主要目标是 Windows……
提前感谢 - Andy
更多关于Golang获取游戏手柄输入及控制器类型的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我尝试了一些不同的方法,可能对遇到类似问题的人有所帮助:
-
我直接访问了 Windows 的 xinput dll。这仅对 XBox 360 兼容的摇杆有效。不幸的是,摇杆编号与多媒体编号不匹配,因此无法用于识别 Xbox 摇杆。
-
我尝试访问(新的)GameInput,并设法获取了 dll 以及访问了第一个 API 调用。但我的 Windows C++ 知识为零,所以当我遇到指针的指针时放弃了——将 C++ 代码转换到 Go 需要学习的东西太多了。
我现在正在使用 XInput,具体是通过 xinput1_3.dll 中的 XInputGetState 函数来“自己动手”实现。
我确实曾回退到使用 GitHub - 0xcafed00d/joystick: Go Joystick API,但在 Legion Go 上使用并看到非常奇怪的手柄属性(我认为是 directinput 控制器)后就放弃了。
祝好 - Andy
更多关于Golang获取游戏手柄输入及控制器类型的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
供参考,以下是我的解决方案,或许对其他人有用……
主要的调用将是
go gamepad.CheckChanged()
这将(在我的情况下)向任何监听的客户端发送一个套接字广播的JSON数据……
包的内容如下:
package gamepad
import (
"encoding/json"
"fmt"
"quando/internal/server/socket"
"syscall"
"time"
"unsafe"
)
const (
MAX_GAMEPADS = 4
XINPUT_DLL_FILENAME = "xinput1_3.dll"
XINPUT_GET_STATE = "XInputGetState"
// 按钮映射供参考
// "UP" 0x0001
// "DOWN" 0x0002
// "LEFT" 0x0004
// "RIGHT" 0x0008
// "START" 0x0010
// "BACK" 0x0020
// "L_STICK" 0x0040
// "R_STICK" 0x0080
// "L_BUMPER" 0x0100
// "R_BUMPER" 0x0200
// "A" 0x1000
// "B" 0x2000
// "X" 0x4000
// "Y" 0x8000
// 注意:HOME/GUIDE 键在标准调用中未映射 - 应为 0x0400
)
var getState *syscall.Proc
type Gamepad struct {
_ uint32 // packet - 更新过于频繁且无用
button_mask uint16
left_trigger uint8
right_trigger uint8
left_x int16
left_y int16
right_x int16
right_y int16
}
var gamepads [MAX_GAMEPADS]*Gamepad // 存储上次返回的状态以识别变化 - 或为 nil
type gamepadJSON struct {
Id int8 `json:"id"`
Drop bool `json:"drop,omitempty"`
Mask uint16 `json:"mask,omitempty"`
Ltrigger uint8 `json:"l_trigger,omitempty"`
Rtrigger uint8 `json:"r_trigger,omitempty"`
Lx int16 `json:"l_x,omitempty"`
Ly int16 `json:"l_y,omitempty"`
Rx int16 `json:"r_x,omitempty"`
Ry int16 `json:"r_y,omitempty"`
}
func triggersChanged(gamepad_old, gamepad_new Gamepad) bool {
// 一旦检测到变化就返回
if gamepad_old.left_trigger != gamepad_new.left_trigger {
return true
}
return gamepad_old.right_trigger != gamepad_new.right_trigger
}
func axesChanged(gamepad_old, gamepad_new Gamepad) bool {
// 一旦检测到变化就返回
if gamepad_old.left_x != gamepad_new.left_x {
return true
}
if gamepad_old.left_y != gamepad_new.left_y {
return true
}
if gamepad_old.right_x != gamepad_new.right_x {
return true
}
return gamepad_old.right_y != gamepad_new.right_y
}
func gamepadUpdated(num uint) bool {
changed := false
var gamepad Gamepad
result, _, _ := getState.Call(uintptr(num), uintptr(unsafe.Pointer(&gamepad)))
if result == 0 { // 成功
if gamepads[num] == nil {
fmt.Println("Gamepad connected : ", num)
changed = true
} else {
var last_gamepad = gamepads[num]
if last_gamepad.button_mask != gamepad.button_mask {
changed = true
} else if triggersChanged(*last_gamepad, gamepad) {
changed = true
} else if axesChanged(*last_gamepad, gamepad) {
changed = true
}
}
gamepads[num] = &gamepad // 即使状态未改变也始终更新
} else if gamepads[num] != nil { // 刚刚断开连接
changed = true
gamepads[num] = nil
}
return changed
}
func addPostJSON(gamepad *Gamepad, num int, gamepad_json *gamepadJSON) {
gamepad_json.Id = int8(num)
if gamepad == nil { // 已断开
gamepad_json.Drop = true
} else {
gamepad_json.Mask = gamepad.button_mask
gamepad_json.Ltrigger = gamepad.left_trigger
gamepad_json.Rtrigger = gamepad.right_trigger
gamepad_json.Lx = gamepad.left_x
gamepad_json.Ly = gamepad.left_y
gamepad_json.Rx = gamepad.right_x
gamepad_json.Ry = gamepad.right_y
}
}
func CheckChanged() {
if getState == nil {
fmt.Println("** XInput joystick not being checked...")
return
} // else
for {
updated := false
gamepad_json := gamepadJSON{}
for num := range MAX_GAMEPADS { // 注意这将是 0..3
if gamepadUpdated(uint(num)) {
updated = true
var gamepad *Gamepad // 为 nil
if gamepads[num] != nil {
gamepad = gamepads[num]
}
addPostJSON(gamepad, num, &gamepad_json)
}
}
if updated {
bout, err := json.Marshal(gamepad_json)
if err != nil {
fmt.Println("Error marshalling gamepad", err)
} else {
str := string(bout)
prefix := `{"type":"gamepad"`
if str != "{}" {
prefix += ","
}
str = prefix + str[1:]
socket.Broadcast(str)
}
}
time.Sleep(time.Second / 60) // 每秒 60 次
}
}
func init() {
dll, err := syscall.LoadDLL(XINPUT_DLL_FILENAME) // 暂时使用旧版本
if err != nil {
fmt.Println("** Failed to find", XINPUT_DLL_FILENAME)
} else {
getState, err = dll.FindProc(XINPUT_GET_STATE)
if err != nil {
fmt.Println("** Failed to find proc :", XINPUT_GET_STATE)
}
}
}


