feat: virtual gamepad

This commit is contained in:
cha0s 2022-03-24 19:47:11 -05:00
parent 30ad575bfd
commit 8c11487892
13 changed files with 200 additions and 10 deletions

View File

@ -37,7 +37,9 @@
"@flecks/redux": "^1.4.1",
"@flecks/socket": "^1.4.1",
"@flecks/user": "^1.4.1",
"@humus/inventory": "^2.0.0"
"@humus/inventory": "^2.0.0",
"fscreen": "^1.2.0",
"react-joystick-component": "^4.0.0"
},
"devDependencies": {
"@flecks/fleck": "^1.4.1"

View File

@ -0,0 +1,29 @@
import {PropTypes, React} from '@flecks/react';
import styles from './button.module.scss';
function Button({down, up}) {
return (
// eslint-disable-next-line jsx-a11y/control-has-associated-label
<button
className={styles.button}
onPointerDown={down}
onPointerUp={up}
type="button"
/>
);
}
Button.defaultProps = {
down: () => {},
up: () => {},
};
Button.displayName = 'Button';
Button.propTypes = {
down: PropTypes.func,
up: PropTypes.func,
};
export default Button;

View File

@ -0,0 +1,12 @@
.button {
background: radial-gradient(rgb(92, 113, 175) 30%, rgb(61, 89, 171)) !important;
border: 0.25rem solid rgb(0, 0, 51);
border-radius: 50px;
height: 5rem;
margin: 0 1.5rem;
opacity: 0.5;
width: 5rem;
&:hover {
background: radial-gradient(rgb(113, 129, 175) 30%, rgb(61, 89, 171)) !important;
}
}

View File

@ -0,0 +1,38 @@
import {PropTypes, React} from '@flecks/react';
import Button from './button';
import styles from './index.module.scss';
function Buttons({inputNormalizer}) {
return (
<div className={styles.buttons}>
<Button
down={() => {
inputNormalizer.onButtonPress({index: 0});
}}
up={() => {
inputNormalizer.onButtonRelease({index: 0});
}}
/>
<Button
down={() => {
inputNormalizer.onButtonPress({index: 1});
}}
up={() => {
inputNormalizer.onButtonRelease({index: 1});
}}
/>
</div>
);
}
Buttons.defaultProps = {};
Buttons.displayName = 'Buttons';
Buttons.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
inputNormalizer: PropTypes.any.isRequired,
};
export default Buttons;

View File

@ -0,0 +1,3 @@
.buttons {
display: flex;
}

View File

@ -0,0 +1,25 @@
import {PropTypes, React} from '@flecks/react';
import Buttons from './buttons';
import styles from './index.module.scss';
import Joystick from './joystick';
function Controller({inputNormalizer}) {
return (
<div className={styles.controller}>
<Joystick inputNormalizer={inputNormalizer} />
<Buttons inputNormalizer={inputNormalizer} />
</div>
);
}
Controller.defaultProps = {};
Controller.displayName = 'Controller';
Controller.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
inputNormalizer: PropTypes.any.isRequired,
};
export default Controller;

View File

@ -0,0 +1,9 @@
.controller {
align-items: center;
bottom: 20%;
display: flex;
justify-content: space-between;
left: 10%;
position: absolute;
width: 80%;
}

View File

@ -0,0 +1,33 @@
import {PropTypes, React} from '@flecks/react';
import {Joystick as JoystickComponent} from 'react-joystick-component';
import styles from './joystick.module.scss';
function Joystick({inputNormalizer}) {
return (
<div className={styles.joystick}>
<JoystickComponent
move={(event) => {
inputNormalizer.onAxisChange({index: 0, value: event.x / 50});
inputNormalizer.onAxisChange({index: 1, value: event.y / -50});
}}
size={100}
stop={() => {
inputNormalizer.onAxisChange({index: 0, value: 0});
inputNormalizer.onAxisChange({index: 1, value: 0});
}}
/>
</div>
);
}
Joystick.defaultProps = {};
Joystick.displayName = 'Joystick';
Joystick.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
inputNormalizer: PropTypes.any.isRequired,
};
export default Joystick;

View File

@ -0,0 +1,13 @@
.joystick {
opacity: 0.5;
transform: scale(calc(1 / var(--scale)));
> div {
background: radial-gradient(rgb(0, 0, 21) 20%, rgb(0, 0, 51)) !important;
> button {
background: radial-gradient(rgb(92, 113, 175) 30%, rgb(61, 89, 171)) !important;
&:hover {
background: radial-gradient(rgb(113, 129, 175) 30%, rgb(61, 89, 171)) !important;
}
}
}
}

View File

@ -2,23 +2,34 @@ import {InputNormalizer} from '@avocado/input/client';
import {
React,
useEffect,
useState,
} from '@flecks/react';
import {push} from '@flecks/react/router';
import {useDispatch} from '@flecks/redux';
import {useSocket} from '@flecks/socket';
import {setSelfEntity} from '@humus/app/state';
import fscreen from 'fscreen';
import {
useSelfEntity,
} from '../../../hooks';
import styles from './index.module.scss';
import Controller from './controller';
import Renderable from './renderable';
import Ui from './ui';
const focus = (element) => {
element.focus();
};
const inputNormalizer = new InputNormalizer();
function Play() {
const dispatch = useDispatch();
const selfEntity = useSelfEntity();
const socket = useSocket();
const [controllerVisible, setControllerVisible] = useState(false);
// Join.
useEffect(() => {
const join = async () => {
@ -42,7 +53,6 @@ function Play() {
if (!selfEntity) {
return undefined;
}
const inputNormalizer = new InputNormalizer();
inputNormalizer.listen(window.document.body);
selfEntity.listenForInput(inputNormalizer);
selfEntity.actionRegistry.setTransformerFor('UseItem', (type, value) => {
@ -73,10 +83,21 @@ function Play() {
}
}, [selfEntity]);
return (
<>
<div
className={styles.play}
onTouchStart={async () => {
await fscreen.requestFullscreen(window.document.body);
setControllerVisible(true);
}}
ref={focus}
role="presentation"
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex="0"
>
<Renderable />
<Ui />
</>
{controllerVisible && <Controller inputNormalizer={inputNormalizer} />}
</div>
);
}

View File

@ -0,0 +1,4 @@
.play {
height: 100%;
width: 100%;
}

View File

@ -11,18 +11,14 @@ import {
useSelfEntity,
} from '../../../../hooks';
import Hotbar from './hotbar';
const focus = (element) => {
element.focus();
};
import styles from './index.module.scss';
const PlayUi = () => {
const {config: {'@humus/app': {resolution}}} = useFlecks();
const room = useRoom();
const selfEntity = useSelfEntity();
return (
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
<div ref={focus} tabIndex="0">
<div className={styles.ui}>
<RoomUi
domScale={Vector.div(resolution, usePropertyChange(selfEntity?.camera, 'viewSize', [0, 0]))}
position={Vector.scale(usePropertyChange(selfEntity?.camera, 'realOffset', [0, 0]), -1)}

View File

@ -0,0 +1,5 @@
.ui {
height: 100%;
position: relative;
width: 100%;
}