🎮 Adds gamepad api control
This commit is contained in:
@@ -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
|
||||||
|
)
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
import { input, outControllerData, mode, modes, type Modes, ModesEnum } from '$lib/stores'
|
import { input, outControllerData, mode, modes, type Modes, ModesEnum } from '$lib/stores'
|
||||||
import type { vector } from '$lib/types/models'
|
import type { vector } from '$lib/types/models'
|
||||||
import { VerticalSlider } from '$lib/components/input'
|
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 throttle = new throttler()
|
||||||
let left: nipplejs.JoystickManager
|
let left: nipplejs.JoystickManager
|
||||||
@@ -13,6 +15,23 @@
|
|||||||
let throttle_timing = 40
|
let throttle_timing = 40
|
||||||
let data = new Array(8)
|
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(() => {
|
onMount(() => {
|
||||||
left = nipplejs.create({
|
left = nipplejs.create({
|
||||||
zone: document.getElementById('left') as HTMLElement,
|
zone: document.getElementById('left') as HTMLElement,
|
||||||
@@ -70,7 +89,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleRange = (event: Event, key: 'speed' | 'height' | 's1') => {
|
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 => {
|
input.update(inputData => {
|
||||||
inputData[key] = value
|
inputData[key] = value
|
||||||
@@ -127,7 +146,7 @@
|
|||||||
type="range"
|
type="range"
|
||||||
name="s1"
|
name="s1"
|
||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="25"
|
||||||
oninput={e => handleRange(e, 's1')}
|
oninput={e => handleRange(e, 's1')}
|
||||||
class="range range-sm range-primary" />
|
class="range range-sm range-primary" />
|
||||||
</div>
|
</div>
|
||||||
@@ -137,7 +156,7 @@
|
|||||||
type="range"
|
type="range"
|
||||||
name="speed"
|
name="speed"
|
||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="25"
|
||||||
oninput={e => handleRange(e, 'speed')}
|
oninput={e => handleRange(e, 'speed')}
|
||||||
class="range range-sm range-primary" />
|
class="range range-sm range-primary" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user