SDL3_API分类参考_摇杆(CategoryJoystick)

2026-3-6 / 0 评论 / 4 阅读

摇杆子系统(CategoryJoystick)

SDL 提供的底层摇杆设备处理模块,适用于需要精细控制摇杆硬件的场景。如果只需简单的、按键功能标准化的控制器交互,建议使用游戏手柄(Gamepad)API 而非本模块

核心概念说明

  1. instance_id(实例ID):系统中摇杆设备的当前实例标识。摇杆拔插后会生成新的 instance_id,该ID是单调递增的数值。
  2. player_index(玩家索引):分配给特定控制器的玩家编号(XInput 控制器会返回 XInput 用户索引,多数普通摇杆无法提供此信息)。
  3. SDL_GUID:摇杆设备的稳定 128 位唯一标识,不会随时间/拔插变化,用于识别设备类型(如 Xbox 360 有线手柄),该标识与平台相关。

基础使用前提

  • 调用 SDL_Init() 时必须传入 SDL_INIT_JOYSTICK 标志,SDL 会扫描系统摇杆设备并加载对应驱动;
  • 若需应用在后台时仍接收摇杆事件,需设置 SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS 提示。

虚拟摇杆功能

SDL 支持创建虚拟摇杆:

  • 通过 SDL_AttachVirtualJoystick() 定义虚拟控制器;
  • 调用 SDL_SetJoystickVirtualAxis()/SDL_SetJoystickVirtualButton() 等接口模拟输入;
  • 虚拟摇杆对 SDL 而言与物理摇杆无差异(无硬件驱动支撑),可用于适配 VR 控制器、实现输入录制/回放等场景。

函数

  • SDL_AttachVirtualJoystick:创建并附加虚拟摇杆(传入设备描述结构体,返回虚拟摇杆ID)
  • SDL_CloseJoystick:关闭已打开的摇杆设备(释放摇杆资源)
  • SDL_DetachVirtualJoystick:分离并销毁虚拟摇杆
  • SDL_GetJoystickAxis:获取摇杆指定轴的当前值(范围:SDL_JOYSTICK_AXIS_MINSDL_JOYSTICK_AXIS_MAX
  • SDL_GetJoystickAxisInitialState:获取摇杆轴的初始状态(设备刚连接时的轴值)
  • SDL_GetJoystickBall:获取摇杆轨迹球的相对移动量(老式摇杆设备的轨迹球)
  • SDL_GetJoystickButton:获取摇杆指定按键的当前状态(1=按下,0=释放)
  • SDL_GetJoystickConnectionState:获取摇杆的连接状态(如已连接、断开、正在连接)
  • SDL_GetJoystickFirmwareVersion:获取摇杆的固件版本号
  • SDL_GetJoystickFromID:通过摇杆ID获取对应的 SDL_Joystick 指针
  • SDL_GetJoystickFromPlayerIndex:通过玩家索引获取对应的 SDL_Joystick 指针
  • SDL_GetJoystickGUID:获取已打开摇杆的 GUID 标识
  • SDL_GetJoystickGUIDForID:通过摇杆ID获取对应的 GUID 标识
  • SDL_GetJoystickGUIDInfo:解析 GUID 信息(提取厂商/产品ID等)
  • SDL_GetJoystickHat:获取摇杆方向键(Hat)的当前状态(如上、下、左、右、组合方向)
  • SDL_GetJoystickID:获取已打开摇杆的实例ID
  • SDL_GetJoystickName:获取已打开摇杆的设备名称
  • SDL_GetJoystickNameForID:通过摇杆ID获取设备名称
  • SDL_GetJoystickPath:获取已打开摇杆的系统路径(如 /dev/input/js0)
  • SDL_GetJoystickPathForID:通过摇杆ID获取设备系统路径
  • SDL_GetJoystickPlayerIndex:获取摇杆的玩家索引
  • SDL_GetJoystickPlayerIndexForID:通过摇杆ID获取玩家索引
  • SDL_GetJoystickPowerInfo:获取摇杆的电源信息(如供电方式、电量)
  • SDL_GetJoystickProduct:获取摇杆的产品ID(PID)
  • SDL_GetJoystickProductForID:通过摇杆ID获取产品ID
  • SDL_GetJoystickProductVersion:获取摇杆的产品版本号
  • SDL_GetJoystickProductVersionForID:通过摇杆ID获取产品版本号
  • SDL_GetJoystickProperties:获取摇杆的属性集合(自定义设备属性)
  • SDL_GetJoysticks:枚举系统中所有已连接的摇杆设备(返回ID列表及数量)
  • SDL_GetJoystickSerial:获取摇杆的序列号(部分设备支持)
  • SDL_GetJoystickType:获取摇杆的设备类型(如普通摇杆、PS手柄、Xbox手柄)
  • SDL_GetJoystickTypeForID:通过摇杆ID获取设备类型
  • SDL_GetJoystickVendor:获取摇杆的厂商ID(VID)
  • SDL_GetJoystickVendorForID:通过摇杆ID获取厂商ID
  • SDL_GetNumJoystickAxes:获取摇杆的轴数量(如左摇杆X/Y轴、右摇杆X/Y轴)
  • SDL_GetNumJoystickBalls:获取摇杆的轨迹球数量
  • SDL_GetNumJoystickButtons:获取摇杆的按键数量
  • SDL_GetNumJoystickHats:获取摇杆的方向键(Hat)数量
  • SDL_HasJoystick:检查系统是否连接了至少一个摇杆设备(返回布尔值)
  • SDL_IsJoystickVirtual:检查指定摇杆是否为虚拟摇杆(返回布尔值)
  • SDL_JoystickConnected:检查指定摇杆是否处于已连接状态
  • SDL_JoystickEventsEnabled:检查摇杆事件是否启用(返回布尔值)
  • SDL_LockJoysticks:锁定摇杆子系统(多线程访问时防止竞争)
  • SDL_OpenJoystick:打开指定ID的摇杆设备(返回 SDL_Joystick 指针,用于后续操作)
  • SDL_RumbleJoystick:启用摇杆的全局震动(设置左右电机的强度和持续时间)
  • SDL_RumbleJoystickTriggers:启用摇杆扳机键的震动(仅部分设备支持)
  • SDL_SendJoystickEffect:发送自定义震动效果到摇杆(如特定的震动模式)
  • SDL_SendJoystickVirtualSensorData:向虚拟摇杆发送传感器数据(如陀螺仪、加速度计)
  • SDL_SetJoystickEventsEnabled:启用/禁用摇杆事件(禁用后不再接收摇杆相关事件)
  • SDL_SetJoystickLED:设置摇杆的LED灯颜色(部分设备支持)
  • SDL_SetJoystickPlayerIndex:设置摇杆的玩家索引
  • SDL_SetJoystickVirtualAxis:设置虚拟摇杆指定轴的数值
  • SDL_SetJoystickVirtualBall:设置虚拟摇杆轨迹球的相对移动量
  • SDL_SetJoystickVirtualButton:设置虚拟摇杆指定按键的状态
  • SDL_SetJoystickVirtualHat:设置虚拟摇杆方向键的状态
  • SDL_SetJoystickVirtualTouchpad:设置虚拟摇杆触摸板的输入数据
  • SDL_TryLockJoysticks:尝试锁定摇杆子系统(非阻塞,失败返回false)
  • SDL_UnlockJoysticks:解锁摇杆子系统
  • SDL_UpdateJoysticks:强制更新所有摇杆的状态(SDL 内部自动调用,一般无需手动调用)

数据类型

  • SDL_Joystick:摇杆设备句柄类型(标识已打开的摇杆设备)
  • SDL_JoystickID:摇杆实例ID类型(系统中摇杆设备的唯一实例标识)

结构体

  • SDL_VirtualJoystickDesc:虚拟摇杆描述结构体(定义虚拟摇杆的轴、按键、方向键数量等)
  • SDL_VirtualJoystickSensorDesc:虚拟摇杆传感器描述结构体(定义支持的传感器类型)
  • SDL_VirtualJoystickTouchpadDesc:虚拟摇杆触摸板描述结构体(定义触摸板参数)

枚举

  • SDL_JoystickConnectionState:摇杆连接状态枚举(SDL_JOYSTICK_DISCONNECTED 断开、SDL_JOYSTICK_CONNECTED 已连接等)
  • SDL_JoystickType:摇杆设备类型枚举(SDL_JOYSTICK_TYPE_UNKNOWN 未知、SDL_JOYSTICK_TYPE_XBOX360 Xbox360手柄等)

  • SDL_JOYSTICK_AXIS_MAX:摇杆轴的最大值(通常为 32767)
  • SDL_JOYSTICK_AXIS_MIN:摇杆轴的最小值(通常为 -32768)

FreeBASIC 示例代码

' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库)
#Include "SDL.bi"

' 辅助函数:打印摇杆设备信息
Sub PrintJoystickInfo(ByVal joystick As SDL_Joystick Ptr)
    If (joystick = NULL) Then Return

    ' 基础信息
    Dim As ZString Ptr name = SDL_GetJoystickName(joystick)
    Dim As SDL_JoystickID joyID = SDL_GetJoystickID(joystick)
    Dim As SDL_JoystickType joyType = SDL_GetJoystickType(joystick)

    ' 硬件信息
    Dim As Uint16 vendor = SDL_GetJoystickVendor(joystick)
    Dim As Uint16 product = SDL_GetJoystickProduct(joystick)
    Dim As Uint16 productVer = SDL_GetJoystickProductVersion(joystick)

    ' 功能数量
    Dim As Integer numAxes = SDL_GetNumJoystickAxes(joystick)
    Dim As Integer numButtons = SDL_GetNumJoystickButtons(joystick)
    Dim As Integer numHats = SDL_GetNumJoystickHats(joystick)
    Dim As Integer numBalls = SDL_GetNumJoystickBalls(joystick)

    SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "==== 摇杆设备信息 ====")
    SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "ID:%d | 名称:%s | 类型:%d", joyID, name, joyType)
    SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "厂商ID:0x%X | 产品ID:0x%X | 版本:%d", vendor, product, productVer)
    SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "轴数量:%d | 按键数量:%d | 方向键数量:%d | 轨迹球数量:%d", _
        numAxes, numButtons, numHats, numBalls)

    If (name <> NULL) Then SDL_free(name)
End Sub

' 辅助函数:打印摇杆轴状态
Sub PrintJoystickAxisState(ByVal joystick As SDL_Joystick Ptr)
    If (joystick = NULL) Then Return

    Dim As Integer numAxes = SDL_GetNumJoystickAxes(joystick)
    If (numAxes = 0) Then Return

    SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "==== 摇杆轴状态 ====")
    For i As Integer = 0 To numAxes - 1
        Dim As Sint16 axisValue = SDL_GetJoystickAxis(joystick, i)
        ' 归一化轴值(-1.0 ~ 1.0)
        Dim As Double normalized = axisValue / 32767.0
        SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "轴 %d:%d (%.2f)", i, axisValue, normalized)
    Next
End Sub

' 主程序
Dim As SDL_Joystick Ptr joystick = NULL
Dim As SDL_JoystickID Ptr joyIDs = NULL
Dim As Integer joyCount = 0
Dim As SDL_Event evt
Dim As Boolean quit = False

' 1. 初始化 SDL(必须包含 JOYSTICK 标志)
If (SDL_Init(SDL_INIT_VIDEO Or SDL_INIT_JOYSTICK) < 0) Then
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError())
    End 1
End If

' 2. 检查摇杆设备
If (Not SDL_HasJoystick()) Then
    SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "系统未检测到摇杆设备,将创建虚拟摇杆演示")

    ' 创建虚拟摇杆
    Dim As SDL_VirtualJoystickDesc virtDesc
    SDL_memset(@virtDesc, 0, SizeOf(SDL_VirtualJoystickDesc))
    virtDesc.type = SDL_JOYSTICK_TYPE_GAMECONTROLLER
    virtDesc.naxes = 4          ' 4个轴(左摇杆X/Y,右摇杆X/Y)
    virtDesc.nbuttons = 16      ' 16个按键
    virtDesc.nhats = 1          ' 1个方向键
    virtDesc.name = "Virtual Gamepad"

    Dim As SDL_JoystickID virtID = SDL_AttachVirtualJoystick(@virtDesc)
    If (virtID = SDL_INVALID_JOYSTICK_ID) Then
        SDL_LogError(SDL_LOG_CATEGORY_INPUT, "创建虚拟摇杆失败:%s", SDL_GetError())
        SDL_Quit()
        End 1
    End If
    SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "创建虚拟摇杆成功,ID:%d", virtID)
End If

' 枚举所有摇杆设备
joyCount = SDL_GetJoysticks(0, NULL)
If (joyCount > 0) Then
    joyIDs = Callocate(joyCount * SizeOf(SDL_JoystickID))
    joyCount = SDL_GetJoysticks(joyCount, joyIDs)
    SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "检测到 %d 个摇杆设备", joyCount)

    ' 打开第一个摇杆设备
    joystick = SDL_OpenJoystick(joyIDs[0])
    If (joystick = NULL) Then
        SDL_LogError(SDL_LOG_CATEGORY_INPUT, "打开摇杆失败:%s", SDL_GetError())
    Else
        PrintJoystickInfo(joystick)
        ' 启用摇杆事件
        SDL_SetJoystickEventsEnabled(SDL_TRUE)
    End If
End If

SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "=== SDL 摇杆示例 ===")
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "摇杆操作:移动轴/按下按键/方向键 | F1:打印轴状态 | F2:触发震动 | ESC:退出")

' 3. 主循环
While (Not quit)
    While (SDL_PollEvent(@evt))
        Select Case evt.type
            Case SDL_QUITEVENT
                quit = True

            ' 键盘事件(功能控制)
            Case SDL_EVENT_KEY_DOWN
                Select Case evt.key.scancode
                    Case SDL_SCANCODE_ESCAPE
                        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "按下 ESC,退出程序")
                        quit = True

                    ' F1:打印轴状态
                    Case SDL_SCANCODE_F1
                        If (joystick <> NULL) Then
                            PrintJoystickAxisState(joystick)
                        End If

                    ' F2:触发摇杆震动(仅物理设备支持)
                    Case SDL_SCANCODE_F2
                        If (joystick <> NULL And Not SDL_IsJoystickVirtual(joystick)) Then
                            ' 左电机(低频)强度 0.5,右电机(高频)强度 0.8,持续 1000ms
                            If (SDL_RumbleJoystick(joystick, 0.5 * 0xFFFF, 0.8 * 0xFFFF, 1000) = 0) Then
                                SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "触发摇杆震动 1 秒")
                            Else
                                SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "该摇杆不支持震动")
                            End If
                        End If
                End Select

            ' 摇杆设备插拔事件
            Case SDL_EVENT_JOYSTICK_ADDED
                SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "摇杆设备已连接,ID:%d", evt.jdevice.which)
                ' 自动打开新连接的摇杆
                If (joystick = NULL) Then
                    joystick = SDL_OpenJoystick(evt.jdevice.which)
                    If (joystick <> NULL) Then
                        PrintJoystickInfo(joystick)
                    End If
                End If

            Case SDL_EVENT_JOYSTICK_REMOVED
                SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "摇杆设备已断开,ID:%d", evt.jdevice.which)
                If (joystick <> NULL And SDL_GetJoystickID(joystick) = evt.jdevice.which) Then
                    SDL_CloseJoystick(joystick)
                    joystick = NULL
                End If

            ' 摇杆轴移动事件
            Case SDL_EVENT_JOYSTICK_AXIS_MOTION
                SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "摇杆轴 %d 移动:%d (设备ID:%d)", _
                    evt.jaxis.axis, evt.jaxis.value, evt.jaxis.which)

            ' 摇杆按键事件
            Case SDL_EVENT_JOYSTICK_BUTTON_DOWN
                SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "摇杆按键 %d 按下(设备ID:%d)", _
                    evt.jbutton.button, evt.jbutton.which)
            Case SDL_EVENT_JOYSTICK_BUTTON_UP
                SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "摇杆按键 %d 释放(设备ID:%d)", _
                    evt.jbutton.button, evt.jbutton.which)

            ' 摇杆方向键事件
            Case SDL_EVENT_JOYSTICK_HAT_MOTION
                Dim As String hatState = ""
                If (evt.jhat.value And SDL_HAT_UP) Then hatState &= "上 "
                If (evt.jhat.value And SDL_HAT_DOWN) Then hatState &= "下 "
                If (evt.jhat.value And SDL_HAT_LEFT) Then hatState &= "左 "
                If (evt.jhat.value And SDL_HAT_RIGHT) Then hatState &= "右 "
                If (hatState = "") Then hatState = "居中"

                SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "摇杆方向键 %d:%s(设备ID:%d)", _
                    evt.jhat.hat, hatState, evt.jhat.which)
        End Select
    Wend

    ' 模拟虚拟摇杆输入(每帧更新)
    If (joyCount > 0 And SDL_IsJoystickVirtual(joyIDs[0])) Then
        Static As Integer frame = 0
        frame += 1

        ' 模拟左摇杆X轴左右摆动
        Dim As Sint16 axisValue = Sin(frame * 0.1) * 32767
        SDL_SetJoystickVirtualAxis(joyIDs[0], 0, axisValue)

        ' 每 100 帧模拟按键按下/释放
        If (frame Mod 100 = 0) Then
            Static As Boolean btnPressed = False
            btnPressed = Not btnPressed
            SDL_SetJoystickVirtualButton(joyIDs[0], 0, btnPressed)
            SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "虚拟摇杆按键 0:%s", IIf(btnPressed, "按下", "释放"))
        End If
    End If

    SDL_Delay(10)
Wend

' 4. 清理资源
If (joystick <> NULL) Then
    SDL_CloseJoystick(joystick)
End If
If (joyIDs <> NULL) Then
    ' 销毁虚拟摇杆
    For i As Integer = 0 To joyCount - 1
        If (SDL_IsJoystickVirtual(joyIDs[i])) Then
            SDL_DetachVirtualJoystick(joyIDs[i])
        End If
    Next
    Deallocate(joyIDs)
End If
SDL_Quit()

SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "程序正常退出")
End 0

核心知识点补充

  1. 摇杆轴数值范围

    • 物理摇杆轴的原始值范围为 -32768 ~ 32767(对应宏 SDL_JOYSTICK_AXIS_MIN/MAX);
    • 通常需归一化为 -1.0 ~ 1.0 便于游戏逻辑处理;
    • 注意轴的死区处理(过滤微小的硬件漂移值)。
  2. 摇杆事件类型

    • SDL_EVENT_JOYSTICK_AXIS_MOTION:轴移动事件;
    • SDL_EVENT_JOYSTICK_BUTTON_DOWN/UP:按键按下/释放事件;
    • SDL_EVENT_JOYSTICK_HAT_MOTION:方向键状态变化事件;
    • SDL_EVENT_JOYSTICK_ADDED/REMOVED:设备插拔事件。
  3. 虚拟摇杆使用场景

    • 适配非标准输入设备(如 VR 手柄、自定义控制器);
    • 输入录制/回放(录制真实摇杆输入,回放时通过虚拟摇杆模拟);
    • 自动化测试(模拟摇杆操作验证游戏逻辑)。

总结

  1. 适用场景
    • 底层硬件控制、非标准摇杆适配、虚拟摇杆创建 → 使用本模块;
    • 标准化游戏手柄交互(如 Xbox/PS 手柄)→ 优先使用 Gamepad API;
  2. 核心流程
    • 初始化(SDL_INIT_JOYSTICK)→ 枚举设备 → 打开摇杆 → 处理事件/查询状态 → 关闭摇杆;
  3. 关键注意事项
    • 多线程访问需调用 SDL_LockJoysticks/SDL_UnlockJoysticks 保证线程安全;
    • 虚拟摇杆需手动模拟输入数据,物理摇杆依赖硬件事件;
    • 摇杆震动、LED 等功能仅部分设备支持,需做兼容性检查。

评论一下?

OωO
取消