Add controller support (#630)

Simple use of gamepad apis to add controller support.
This commit is contained in:
Corwin 2024-04-10 20:38:54 +01:00 committed by GitHub
commit 27ad5d1184
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 141 additions and 48 deletions

View file

@ -9,6 +9,7 @@ import mGBA, { mGBAEmulator } from "./vendor/mgba";
import { GbaKey, KeyBindings } from "./bindings"; import { GbaKey, KeyBindings } from "./bindings";
import { styled } from "styled-components"; import { styled } from "styled-components";
import { useFrameSkip } from "./useFrameSkip.hook"; import { useFrameSkip } from "./useFrameSkip.hook";
import { useController } from "./useController.hook";
type Module = any; type Module = any;
@ -105,6 +106,7 @@ export const Mgba = forwardRef<MgbaHandle, MgbaProps>(
}, [state]); }, [state]);
useFrameSkip(mgbaModule); useFrameSkip(mgbaModule);
useController(mgbaModule);
useEffect(() => { useEffect(() => {
if (!gameLoaded) return; if (!gameLoaded) return;

View file

@ -0,0 +1,94 @@
import { MutableRefObject, useEffect } from "react";
import { mGBAEmulator } from "./vendor/mgba";
import { GbaKey } from "./bindings";
export function useController(mgbaModule: MutableRefObject<mGBAEmulator>) {
useEffect(() => {
let stopped = false;
let previouslyPressedButtons = new Set<GbaKey>();
function raf(time: DOMHighResTimeStamp) {
const controllers = navigator.getGamepads();
const currentlyPressed = new Set<GbaKey>();
for (let controller of controllers) {
if (!controller) continue;
if (controller.buttons[1].pressed) {
currentlyPressed.add(GbaKey.A);
}
if (controller.buttons[0].pressed) {
currentlyPressed.add(GbaKey.B);
}
if (controller.buttons[5].pressed) {
currentlyPressed.add(GbaKey.R);
}
if (controller.buttons[4].pressed) {
currentlyPressed.add(GbaKey.L);
}
if (controller.buttons[8].pressed) {
currentlyPressed.add(GbaKey.Select);
}
if (controller.buttons[9].pressed) {
currentlyPressed.add(GbaKey.Start);
}
if (controller.buttons[12].pressed) {
currentlyPressed.add(GbaKey.Up);
}
if (controller.buttons[13].pressed) {
currentlyPressed.add(GbaKey.Down);
}
if (controller.buttons[14].pressed) {
currentlyPressed.add(GbaKey.Left);
}
if (controller.buttons[15].pressed) {
currentlyPressed.add(GbaKey.Right);
}
if (controller.axes[0] < -0.5) {
currentlyPressed.add(GbaKey.Left);
}
if (controller.axes[0] > 0.5) {
currentlyPressed.add(GbaKey.Right);
}
if (controller.axes[1] < -0.5) {
currentlyPressed.add(GbaKey.Up);
}
if (controller.axes[1] > 0.5) {
currentlyPressed.add(GbaKey.Down);
}
}
for (let oldButton of previouslyPressedButtons) {
if (!currentlyPressed.has(oldButton)) {
mgbaModule.current.buttonUnpress(oldButton);
}
}
for (let newButton of currentlyPressed) {
if (!previouslyPressedButtons.has(newButton)) {
mgbaModule.current.buttonPress(newButton);
}
}
previouslyPressedButtons = currentlyPressed;
if (!stopped) {
window.requestAnimationFrame(raf);
}
}
function gamepadConnectedEvent() {}
window.addEventListener("gamepadconnected", gamepadConnectedEvent);
window.requestAnimationFrame(raf);
return () => {
stopped = true;
window.removeEventListener("gamepadconnected", gamepadConnectedEvent);
};
}, [mgbaModule]);
}

View file

@ -1,7 +1,6 @@
import { MutableRefObject, useEffect } from "react"; import { MutableRefObject, useEffect } from "react";
import { mGBAEmulator } from "./vendor/mgba"; import { mGBAEmulator } from "./vendor/mgba";
export function useFrameSkip(mgbaModule: MutableRefObject<mGBAEmulator>) { export function useFrameSkip(mgbaModule: MutableRefObject<mGBAEmulator>) {
useEffect(() => { useEffect(() => {
let previous: number | undefined = undefined; let previous: number | undefined = undefined;
@ -19,7 +18,6 @@ export function useFrameSkip(mgbaModule: MutableRefObject<mGBAEmulator>) {
const smoothedFrameRate = Math.round(1 / (smoothedFrameTime / 1000)); const smoothedFrameRate = Math.round(1 / (smoothedFrameTime / 1000));
totalTime += 1 / smoothedFrameRate; totalTime += 1 / smoothedFrameRate;
if (totalTime >= 1 / 60) { if (totalTime >= 1 / 60) {
@ -34,8 +32,6 @@ export function useFrameSkip(mgbaModule: MutableRefObject<mGBAEmulator>) {
paused = true; paused = true;
} }
} }
} }
previous = time; previous = time;
@ -45,9 +41,8 @@ export function useFrameSkip(mgbaModule: MutableRefObject<mGBAEmulator>) {
} }
window.requestAnimationFrame(raf); window.requestAnimationFrame(raf);
return () => { stopped = true; }; return () => {
stopped = true;
};
}, [mgbaModule]); }, [mgbaModule]);
} }

View file

@ -1,7 +1,9 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
export function useLocalStorage<T>(defaultValue: T, export function useLocalStorage<T>(
appName: string): [T, (newValue: T) => void] { defaultValue: T,
appName: string
): [T, (newValue: T) => void] {
const [value, setValue] = useState(() => { const [value, setValue] = useState(() => {
try { try {
const storageValue = localStorage.getItem(appName); const storageValue = localStorage.getItem(appName);