mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 08:11:33 +11:00
Press to start on mobile (#622)
This commit is contained in:
commit
abd5e44ede
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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="" />
|
||||||
|
|
Loading…
Reference in a new issue