首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >😎web手柄终极适配方案

😎web手柄终极适配方案

原创
作者头像
用户8578242
发布2025-07-22 23:36:35
发布2025-07-22 23:36:35
1100
举报

前言

掘金:https://juejin.cn/post/7528405614440726582

最近用手柄玩星铁和绝区零的时候,发现他们的手柄适配做的很舒服,就突发奇想,web这边有没有现成的手柄库。但是搜了一圈,竟然没能找到一个像样的库(都是简单调用api,没有封装),难道没这样的需求吗😅。于是打算自己手撸一个了💢。

GitHub

不说废话,先贴仓库链接,web-gamepad

需求分析

需求不高,能像游戏一样,支持以下功能

  • 适配全部标准手柄按键(如:xbox、ps5)
  • 支持多场景ui控制与切换
  • 支持多个手柄输入

思路

俗话说,技术为需求服务,那么,设计也得围绕需求来,下面是对以上需求的设计思路。这里就不写api方法了(网上都能查到),只讲思路

适配手柄

众所周知,市面上有很多手柄厂商,每个厂商生产出来的手柄都不太一样,但大多遵守一套手柄按键标准。左右两侧四个按钮、顶部四个按钮、中间三个按钮、左右两个摇杆。

w3c手柄文档

既然有标准,那就很简单了,可以用标准的key值做为参数去绑定事件。不遵守标准的,可以设计成自定义传参兼容。

在我的库里,提供了xbox和ps5的手柄按键映射表,如下:

代码语言:ts
复制
/** xbox key map */
export const XBOX_KEY_MAP = {
  A: 0,
  B: 1,
  X: 2,
  Y: 3,
  LB: 4,
  RB: 5,
  LT: 6,
  RT: 7,
  view: 8,
  menu: 9,
  LS: 10,
  RS: 11,
  up: 12,
  down: 13,
  left: 14,
  right: 15,
  home: 16
} as const

/** ps5 key map */
export const PS5_BUTTON_MAP = {
  cross: 0,
  circle: 1,
  square: 2,
  triangle: 3,
  L1: 4,
  R1: 5,
  L2: 6,
  R2: 7,
  share: 8,
  options: 9,
  L3: 10,
  R3: 11,
  PS: 12,
  touchpad: 13,
  microphone: 14,
  up: 15,
  down: 16,
  left: 17,
  right: 18
} as const

// 绑定按钮监听事件,传入对应手柄按键的key值和输入事件类型
controller.addBtnEvents(XBOX_KEY_MAP.A, INPUT_TYPE.down, () => {})

支持多场景ui控制与切换

这个需求是最核心的一个,要怎么理解呢,这里用星铁来举例:

同一个按钮在不同ui场景中绑定不同事件

在地图中,RB是跑步

但切换到每日任务中,RB是切换tab栏

也就是说,同一个按钮,会存在多个不同ui场景组件的事件绑定,但是因为按键监听是全局的,就不能简单在监听事件里面绑定,组件多了,管理起来会很混乱

我的设计思路如下:

全局一个manager管理多个ui组件的事件(controller)切换,切换某个controller,就只触发该controller下的事件,这样就能实现像星铁一样切换事件

多个ui组件(controller)同时存在,并且分别控制不同按键

怎么理解呢,还是看图吧(以下是我自己的拆分)

如图,可以拆分多个组件,在前端中也是很常见的应用场景,不同组件分别控制手柄不同按钮

这个其实也很好解决,controller继续细分控制每个按钮事件禁用就行了。

代码:

代码语言:ts
复制
import {
  createGamepadController,
  XBOX_KEY_MAP,
  INPUT_TYPE,
  switchGamepadController
} from 'web-gamepad'

// 创建多个控制器
const controller = createGamepadController('btn1')
const menuController = createGamepadController('drawer', false)

const menu = ref(false)
controller.addBtnEvents(XBOX_KEY_MAP.menu, INPUT_TYPE.down, () => {
    menu.value = true // 打开菜单栏
    // 切换菜单Controller
    menuGamepadController(drawerController.id)
})

controller.addBtnEvents(XBOX_KEY_MAP.menu, INPUT_TYPE.down, () => {
    menu.value = true // 打开菜单栏
    // 切换菜单Controller
    menuGamepadController(menuGamepadController.id)
})

回溯历史绑定事件

有多个场景ui切换,必然要有回溯之前ui场景的功能。比如:

一个提示弹窗,可以由多个组件触发,当关闭弹窗后,得切换回之前的ui场景(controller)。

如果没有回溯功能,那么关闭弹窗就得写很多if-else判断之前是哪个组件(controller)触发的

这里就用栈去存储之前的controller id,当触发回溯时,取栈的第一个id

代码:

代码语言:ts
复制
const idStack: string[] = []
// 记录之前激活的controller id,用setTimeout是为了收集同一时间激活的controllerid
function recordActiveIdStack() {
  if (recordTimeout) {
    return
  }
  recordTimeout = setTimeout(() => {
// 获取全部激活的controller id
    const ids = getActiveControllers()
      .map((item) => item.id)
      .join(',')
    ids && idStack.push(ids)
    clearTimeout(recordTimeout)
    recordTimeout = null
  })
}

function recallController(offset: number) {
  if (offset <= 0) {
    return
  }
  const [recallId] = idStack.splice(
    -clamp(Math.abs(offset) + 1, 0, idStack.length)
  )
  switchGamepadController(recallId.split(',').filter((id) => id))
}

// ...省略业务代码

// 当关闭弹窗是,回退前1位激活的controllers
function handleClose() {
  recallController(1)
}

支持多个手柄输入

这个需求也很简单,web的gamepad本身就支持监听多个手柄输入,在根据上面的manager设计,就很好实现管理

代码:

代码语言:ts
复制
const GamepadManager: {
  [key: number]: Gamepad
} = {}

function addGamepad(gamepad: Gamepad) {
  GamepadManager[gamepad.index] = gamepad
}

window.addEventListener(
    'gamepadconnected',
    function ({ gamepad }: GamepadEvent) {
      addGamepad(gamepad)
    }
  )

至此,核心需求算是解决了,当然,还有很多细节问题这里就不细说了。

总结

参考文档:https://w3c.github.io/gamepad/#remapping

欢迎大家来提issue,之后会开发更多有趣的库~

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • GitHub
  • 需求分析
  • 思路
    • 适配手柄
  • 支持多场景ui控制与切换
    • 同一个按钮在不同ui场景中绑定不同事件
    • 多个ui组件(controller)同时存在,并且分别控制不同按键
    • 回溯历史绑定事件
  • 支持多个手柄输入
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档