Press to start on mobile (#622)

This commit is contained in:
Corwin 2024-04-09 02:46:10 +01:00 committed by GitHub
commit abd5e44ede
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 38 additions and 26 deletions

View file

@ -43,13 +43,12 @@ const AppContainer = styled.main`
const StartButtonWrapper = styled.button` const StartButtonWrapper = styled.button`
margin: auto; margin: auto;
font-size: 3em; font-size: 2em;
padding: 1em; padding: 1em;
text-transform: uppercase; text-transform: uppercase;
background-color: black; background-color: black;
color: white; color: white;
border: none; border: none;
border-radius: 0.5em;
aspect-ratio: 240 / 160; aspect-ratio: 240 / 160;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -62,7 +61,8 @@ const StartButtonWrapper = styled.button`
interface MgbaWrapperProps { interface MgbaWrapperProps {
gameUrl: string; gameUrl: string;
startNotPlaying?: boolean; isPlaying?: boolean;
setIsPlaying?: (isPlaying: boolean) => void;
} }
export const MgbaStandalone: FC<MgbaWrapperProps> = (props) => ( export const MgbaStandalone: FC<MgbaWrapperProps> = (props) => (
@ -71,12 +71,8 @@ export const MgbaStandalone: FC<MgbaWrapperProps> = (props) => (
</AppContainer> </AppContainer>
); );
export interface MgbaWrapperHandle extends MgbaHandle { export const MgbaWrapper = forwardRef<MgbaHandle, MgbaWrapperProps>(
hardReset: () => void; ({ gameUrl, isPlaying = true, setIsPlaying }, ref) => {
}
export const MgbaWrapper = forwardRef<MgbaWrapperHandle, MgbaWrapperProps>(
({ gameUrl, startNotPlaying = false }, ref) => {
const [{ volume, bindings }, setState] = useLocalStorage( const [{ volume, bindings }, setState] = useLocalStorage(
{ volume: 1.0, bindings: DefaultBindingsSet() }, { volume: 1.0, bindings: DefaultBindingsSet() },
"agbrswebplayer" "agbrswebplayer"
@ -108,8 +104,6 @@ export const MgbaWrapper = forwardRef<MgbaWrapperHandle, MgbaWrapperProps>(
useAvoidItchIoScrolling(); useAvoidItchIoScrolling();
const [isPlaying, setIsPlaying] = useState(!startNotPlaying);
return ( return (
<> <>
{showBindings && ( {showBindings && (
@ -133,7 +127,7 @@ export const MgbaWrapper = forwardRef<MgbaWrapperHandle, MgbaWrapperProps>(
paused={paused} paused={paused}
/> />
) : ( ) : (
<StartButton onClick={() => setIsPlaying(true)} /> <StartButton onClick={() => setIsPlaying && setIsPlaying(true)} />
)} )}
</> </>
); );
@ -179,7 +173,7 @@ function BindingsWindow({
function StartButton({ onClick }: { onClick: () => void }) { function StartButton({ onClick }: { onClick: () => void }) {
return ( return (
<StartButtonWrapper onClick={onClick}>Press to start</StartButtonWrapper> <StartButtonWrapper onClick={onClick}>Touch to start</StartButtonWrapper>
); );
} }

View file

@ -8,8 +8,8 @@ import DPad from "./gba-parts/dpad.png";
import ABButtons from "./gba-parts/ab.png"; import ABButtons from "./gba-parts/ab.png";
import Select from "./gba-parts/SELECT.png"; import Select from "./gba-parts/SELECT.png";
import Start from "./gba-parts/START.png"; import Start from "./gba-parts/START.png";
import { MgbaWrapperHandle } from "./mgba/mgbaWrapper";
import { GbaKey } from "./mgba/bindings"; import { GbaKey } from "./mgba/bindings";
import { MgbaHandle } from "./mgba/mgba";
const MobileControls = styled.div` const MobileControls = styled.div`
display: flex; display: flex;
@ -48,7 +48,7 @@ const MobileControlsRow = styled.div<{
${(props) => props.$centered && `justify-content: center;`} ${(props) => props.$centered && `justify-content: center;`}
`; `;
const useSimpleButton = (mgba: MgbaWrapperHandle, button: GbaKey) => { const useSimpleButton = (mgba: MgbaHandle, button: GbaKey) => {
return useMemo(() => { return useMemo(() => {
return { return {
onTouchStart: () => { onTouchStart: () => {
@ -80,7 +80,7 @@ const relativeTouch = (touch: Touch) => {
return relativePosition; return relativePosition;
}; };
const useDpadTouch = (mgba: MgbaWrapperHandle) => { const useDpadTouch = (mgba: MgbaHandle) => {
const [previouslyPressedButtons, setTouchedButtons] = useState<Set<GbaKey>>( const [previouslyPressedButtons, setTouchedButtons] = useState<Set<GbaKey>>(
new Set() new Set()
); );
@ -139,7 +139,7 @@ const useDpadTouch = (mgba: MgbaWrapperHandle) => {
}, [mgba, previouslyPressedButtons]); }, [mgba, previouslyPressedButtons]);
}; };
const useAbTouch = (mgba: MgbaWrapperHandle) => { const useAbTouch = (mgba: MgbaHandle) => {
const [previouslyPressedButtons, setTouchedButtons] = useState<Set<GbaKey>>( const [previouslyPressedButtons, setTouchedButtons] = useState<Set<GbaKey>>(
new Set() new Set()
); );
@ -182,7 +182,7 @@ const useAbTouch = (mgba: MgbaWrapperHandle) => {
}, [mgba, previouslyPressedButtons]); }, [mgba, previouslyPressedButtons]);
}; };
export const MobileController: FC<{ mgba: MgbaWrapperHandle }> = ({ mgba }) => { export const MobileController: FC<{ mgba: MgbaHandle }> = ({ mgba }) => {
return ( return (
<MobileControls onContextMenu={(evt) => evt.preventDefault()}> <MobileControls onContextMenu={(evt) => evt.preventDefault()}>
<MobileControlsRow $size={MobileControlsSize.Small}> <MobileControlsRow $size={MobileControlsSize.Small}>
@ -218,7 +218,7 @@ export const MobileController: FC<{ mgba: MgbaWrapperHandle }> = ({ mgba }) => {
/> />
</MobileControlsRow> </MobileControlsRow>
<MobileControlsRow $size={MobileControlsSize.Small} $centered> <MobileControlsRow $size={MobileControlsSize.Small} $centered>
<button onClick={() => mgba.hardReset()}>Restart</button> <button onClick={() => mgba.restart()}>Restart</button>
</MobileControlsRow> </MobileControlsRow>
</MobileControls> </MobileControls>
); );

View file

@ -2,15 +2,16 @@
import styled from "styled-components"; import styled from "styled-components";
import { CenteredBlock, ContentBlock } from "./contentBlock"; import { CenteredBlock, ContentBlock } from "./contentBlock";
import MgbaWrapper, { MgbaWrapperHandle } from "./mgba/mgbaWrapper"; import MgbaWrapper from "./mgba/mgbaWrapper";
import Image from "next/image"; import Image from "next/image";
import left from "./gba-parts/left.png"; import left from "./gba-parts/left.png";
import right from "./gba-parts/right.png"; import right from "./gba-parts/right.png";
import { MobileController } from "./mobileController"; import { MobileController } from "./mobileController";
import { useMemo, useRef } from "react"; import { useMemo, useRef, useState } from "react";
import { GbaKey } from "./mgba/bindings"; import { GbaKey } from "./mgba/bindings";
import { useClientValue } from "./useClientValue.hook"; import { useClientValue } from "./useClientValue.hook";
import { MgbaHandle } from "./mgba/mgba";
const ExternalLink = styled.a` const ExternalLink = styled.a`
text-decoration: none; text-decoration: none;
@ -19,6 +20,10 @@ const ExternalLink = styled.a`
border: solid #fad288 2px; border: solid #fad288 2px;
border-radius: 5px; border-radius: 5px;
padding: 5px 10px; padding: 5px 10px;
&:hover {
border: solid black 2px;
}
`; `;
const HelpLinks = styled.div` const HelpLinks = styled.div`
@ -27,7 +32,7 @@ const HelpLinks = styled.div`
`; `;
const GameDisplay = styled.div` const GameDisplay = styled.div`
height: min(calc(100vw / 1.5), 40vh); height: min(calc(100vw / 1.5), min(90vh, 480px));
max-width: 100vw; max-width: 100vw;
margin-top: 20px; margin-top: 20px;
overflow: hidden; overflow: hidden;
@ -36,7 +41,7 @@ const GameDisplay = styled.div`
const GamePanelWrapper = styled.div` const GamePanelWrapper = styled.div`
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: baseline; align-items: end;
height: 100%; height: 100%;
`; `;
@ -60,12 +65,16 @@ const GameSide = styled.div`
const isTouchScreen = () => navigator.maxTouchPoints > 1; const isTouchScreen = () => navigator.maxTouchPoints > 1;
function shouldStartPlaying(isTouchScreen: boolean | undefined) {
if (isTouchScreen === undefined) return false;
return !isTouchScreen;
}
const MgbaWithControllerSides = () => { const MgbaWithControllerSides = () => {
const mgba = useRef<MgbaWrapperHandle>(null); const mgba = useRef<MgbaHandle>(null);
const mgbaHandle = useMemo( const mgbaHandle = useMemo(
() => ({ () => ({
hardReset: () => mgba.current?.hardReset(),
restart: () => mgba.current?.restart(), restart: () => mgba.current?.restart(),
buttonPress: (key: GbaKey) => mgba.current?.buttonPress(key), buttonPress: (key: GbaKey) => mgba.current?.buttonPress(key),
buttonRelease: (key: GbaKey) => mgba.current?.buttonRelease(key), buttonRelease: (key: GbaKey) => mgba.current?.buttonRelease(key),
@ -73,8 +82,12 @@ const MgbaWithControllerSides = () => {
[] []
); );
const [isPlaying, setIsPlaying] = useState<boolean>();
const shouldUseTouchScreenInput = useClientValue(isTouchScreen); const shouldUseTouchScreenInput = useClientValue(isTouchScreen);
const playEmulator =
isPlaying ?? shouldStartPlaying(shouldUseTouchScreenInput);
return ( return (
<> <>
<GameDisplay> <GameDisplay>
@ -83,7 +96,12 @@ const MgbaWithControllerSides = () => {
<Image src={left} alt="" /> <Image src={left} alt="" />
</GameSide> </GameSide>
<GameDisplayWindow> <GameDisplayWindow>
<MgbaWrapper gameUrl="combo.gba.gz" ref={mgba} /> <MgbaWrapper
gameUrl="combo.gba.gz"
ref={mgba}
isPlaying={playEmulator}
setIsPlaying={setIsPlaying}
/>
</GameDisplayWindow> </GameDisplayWindow>
<GameSide> <GameSide>
<Image src={right} alt="" /> <Image src={right} alt="" />