feat: virtual gamepad
This commit is contained in:
parent
30ad575bfd
commit
8c11487892
|
@ -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"
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -0,0 +1,3 @@
|
|||
.buttons {
|
||||
display: flex;
|
||||
}
|
25
packages/app/src/components/app/play/controller/index.jsx
Normal file
25
packages/app/src/components/app/play/controller/index.jsx
Normal 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;
|
|
@ -0,0 +1,9 @@
|
|||
.controller {
|
||||
align-items: center;
|
||||
bottom: 20%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
left: 10%;
|
||||
position: absolute;
|
||||
width: 80%;
|
||||
}
|
33
packages/app/src/components/app/play/controller/joystick.jsx
Normal file
33
packages/app/src/components/app/play/controller/joystick.jsx
Normal 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;
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
4
packages/app/src/components/app/play/index.module.scss
Normal file
4
packages/app/src/components/app/play/index.module.scss
Normal file
|
@ -0,0 +1,4 @@
|
|||
.play {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
|
@ -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)}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
.ui {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
Loading…
Reference in New Issue
Block a user