Lzh on GitHub

Gamepad API 提供响应式绑定。

Demo

使用

由于 Gamepad API 的工作方式,你必须先使用游戏手柄与页面进行交互,然后它才会被检测到。

<script setup>
import { useGamepad } from '@vueuse/core'
import { computed } from 'vue'

const { isSupported, gamepads } = useGamepad()
const gamepad = computed(() => gamepads.value.find(g => g.mapping === 'standard'))
</script>

<template>
  <span>
    {{ gamepad.id }}
  </span>
</template>

游戏手柄更新

目前 Gamepad API 不提供事件支持来更新游戏手柄的状态。为了更新游戏手柄状态,requestAnimationFrame 被用于轮询游戏手柄的变化。你可以通过 useGamepad 提供的 pauseresume 函数来控制此轮询。

import { useGamepad } from '@vueuse/core'

const { pause, resume, gamepads } = useGamepad()

pause()

// gamepads object will not update

resume()

// gamepads object will update on user input

游戏手柄连接与断开事件

onConnectedonDisconnected 事件将在游戏手柄连接或断开时触发。

const { gamepads, onConnected, onDisconnected } = useGamepad()

onConnected((index) => {
  console.log(`${gamepads.value[index].id} connected`)
})

onDisconnected((index) => {
  console.log(`${index} disconnected`)
})

震动

Gamepad Haptics API 功能稀疏,因此在使用前请查看 兼容性表格

import { computed } from 'vue'

const supportsVibration = computed(() => gamepad.hapticActuators.length > 0)
function vibrate() {
  if (supportsVibration.value) {
    const actuator = gamepad.hapticActuators[0]
    actuator.playEffect('dual-rumble', {
      startDelay: 0,
      duration: 1000,
      weakMagnitude: 1,
      strongMagnitude: 1,
    })
  }
}

映射

为了让 Gamepad API 更易于使用,我们提供了映射,将控制器映射到控制器的按钮布局。

Xbox360 控制器

<script setup>
import { mapGamepadToXbox360Controller } from '@vueuse/core'

const controller = mapGamepadToXbox360Controller(gamepad)
</script>

<template>
  <span>{{ controller.buttons.a.pressed }}</span>
  <span>{{ controller.buttons.b.pressed }}</span>
  <span>{{ controller.buttons.x.pressed }}</span>
  <span>{{ controller.buttons.y.pressed }}</span>
</template>

目前,只提供了 Xbox 360 控制器的映射。如果你有想添加映射的控制器,请随时提交 PR(Pull Request)来贡献更多的控制器映射!

SSR 兼容性

此组件 设计用于客户端。在某些情况下,SSR(服务端渲染)可能会导致 注水不匹配 问题。

如果你正在使用 Nuxt,你可以简单地将组件文件 重命名为 .client.vue 后缀(例如,GamepadComponent.client.vue),这会自动使其 仅在客户端渲染,从而避免注水不匹配。

在其他框架或纯 Vue 中,你可以将使用该组件的地方包裹在一个 <ClientOnly> 组件 中,以确保它仅在客户端渲染。

类型声明

export interface UseGamepadOptions
  extends ConfigurableWindow,
    ConfigurableNavigator {}
/**
 * Maps a standard standard gamepad to an Xbox 360 Controller.
 */
export declare function mapGamepadToXbox360Controller(
  gamepad: Ref<Gamepad | undefined>,
): ComputedRef<{
  buttons: {
    a: GamepadButton
    b: GamepadButton
    x: GamepadButton
    y: GamepadButton
  }
  bumper: {
    left: GamepadButton
    right: GamepadButton
  }
  triggers: {
    left: GamepadButton
    right: GamepadButton
  }
  stick: {
    left: {
      horizontal: number
      vertical: number
      button: GamepadButton
    }
    right: {
      horizontal: number
      vertical: number
      button: GamepadButton
    }
  }
  dpad: {
    up: GamepadButton
    down: GamepadButton
    left: GamepadButton
    right: GamepadButton
  }
  back: GamepadButton
  start: GamepadButton
} | null>
export declare function useGamepad(options?: UseGamepadOptions): {
  isSupported: ComputedRef<boolean>
  onConnected: EventHookOn<number>
  onDisconnected: EventHookOn<number>
  gamepads: Ref<
    {
      readonly axes: ReadonlyArray<number>
      readonly buttons: readonly {
        readonly pressed: boolean
        readonly touched: boolean
        readonly value: number
      }[]
      readonly connected: boolean
      readonly id: string
      readonly index: number
      readonly mapping: GamepadMappingType
      readonly timestamp: DOMHighResTimeStamp
      readonly vibrationActuator: {
        playEffect: (
          type: GamepadHapticEffectType,
          params?: GamepadEffectParameters,
        ) => Promise<GamepadHapticsResult>
        reset: () => Promise<GamepadHapticsResult>
      }
    }[],
    | Gamepad[]
    | {
        readonly axes: ReadonlyArray<number>
        readonly buttons: readonly {
          readonly pressed: boolean
          readonly touched: boolean
          readonly value: number
        }[]
        readonly connected: boolean
        readonly id: string
        readonly index: number
        readonly mapping: GamepadMappingType
        readonly timestamp: DOMHighResTimeStamp
        readonly vibrationActuator: {
          playEffect: (
            type: GamepadHapticEffectType,
            params?: GamepadEffectParameters,
          ) => Promise<GamepadHapticsResult>
          reset: () => Promise<GamepadHapticsResult>
        }
      }[]
  >
  pause: Fn
  resume: Fn
  isActive: Readonly<ShallowRef<boolean>>
}
export type UseGamepadReturn = ReturnType<typeof useGamepad>