🎮 Adds gamepad api control

This commit is contained in:
Rune Harlyk
2025-04-18 21:10:44 +02:00
committed by Rune Harlyk
parent dac21a499f
commit 20c5a8ee92
2 changed files with 69 additions and 3 deletions
+47
View File
@@ -0,0 +1,47 @@
import { readable, derived } from 'svelte/store'
export type GamepadState = {
available: boolean
gamepads: Gamepad[]
}
export const gamepads = readable<GamepadState>({ available: false, gamepads: [] }, set => {
const update = () => {
const hasGamepadAPI = 'getGamepads' in navigator
if (!hasGamepadAPI) {
set({ available: false, gamepads: [] })
return
}
const gps = navigator.getGamepads?.() ?? []
const validGamepads = gps.filter(Boolean) as Gamepad[]
set({
available: true,
gamepads: validGamepads
})
raf = requestAnimationFrame(update)
}
window.addEventListener('gamepadconnected', update)
window.addEventListener('gamepaddisconnected', update)
let raf = requestAnimationFrame(update)
return () => {
cancelAnimationFrame(raf)
window.removeEventListener('gamepadconnected', update)
window.removeEventListener('gamepaddisconnected', update)
}
})
export const gamepad = derived(gamepads, $gamepads =>
$gamepads.available && $gamepads.gamepads.length > 0 ? $gamepads.gamepads[0] : null
)
export const gamepadAxes = derived(gamepad, $gamepad => $gamepad?.axes ?? [0, 0, 0, 0])
export const gamepadButtons = derived(gamepad, $gamepad => $gamepad?.buttons ?? [])
export const hasGamepad = derived(
gamepads,
$gamepads => $gamepads.available && $gamepads.gamepads.length > 0
)
+22 -3
View File
@@ -5,6 +5,8 @@
import { input, outControllerData, mode, modes, type Modes, ModesEnum } from '$lib/stores'
import type { vector } from '$lib/types/models'
import { VerticalSlider } from '$lib/components/input'
import { gamepadAxes, gamepadButtons, hasGamepad } from '$lib/stores/gamepad'
import { notifications } from '$lib/components/toasts/notifications'
let throttle = new throttler()
let left: nipplejs.JoystickManager
@@ -13,6 +15,23 @@
let throttle_timing = 40
let data = new Array(8)
$effect(() => {
if ($hasGamepad) {
notifications.success('🎮 Gamepad connected', 3000)
}
})
$effect(() => {
handleJoyMove('left', { x: $gamepadAxes[0], y: -$gamepadAxes[1] })
handleJoyMove('right', { x: $gamepadAxes[2], y: $gamepadAxes[3] })
})
// TODO React to button press
// $effect(() => {
// if ($gamepadButtons.length === 0) return
//
// })
onMount(() => {
left = nipplejs.create({
zone: document.getElementById('left') as HTMLElement,
@@ -70,7 +89,7 @@
}
const handleRange = (event: Event, key: 'speed' | 'height' | 's1') => {
const value: number = event.target?.value
const value: number = Number((event.target as HTMLInputElement).value)
input.update(inputData => {
inputData[key] = value
@@ -127,7 +146,7 @@
type="range"
name="s1"
min="0"
max="100"
max="25"
oninput={e => handleRange(e, 's1')}
class="range range-sm range-primary" />
</div>
@@ -137,7 +156,7 @@
type="range"
name="speed"
min="0"
max="100"
max="25"
oninput={e => handleRange(e, 'speed')}
class="range range-sm range-primary" />
</div>