+ {roomJoined && (
+
+ dispatch(setShowJoystick(!showJoystick))}>
+ {showJoystick ? : }
+
+
+ )}
{showRoomInfo && (
setShowRoomInfo(false)} size="small">
diff --git a/client/src/components/Joystick.jsx b/client/src/components/Joystick.jsx
new file mode 100644
index 00000000..be737f38
--- /dev/null
+++ b/client/src/components/Joystick.jsx
@@ -0,0 +1,79 @@
+import { Joystick } from 'react-joystick-component'
+import React from 'react'
+
+var AngleToDirections = function (angle, out) {
+ if (out === undefined) {
+ out = {}
+ } else if (out === true) {
+ out = globOut
+ }
+
+ out.left = false
+ out.right = false
+ out.up = false
+ out.down = false
+
+ angle = (angle + 360) % 360
+
+ if (angle > 22.5 && angle <= 67.5) {
+ out.down = true
+ out.right = true
+ } else if (angle > 67.5 && angle <= 112.5) {
+ out.down = true
+ } else if (angle > 112.5 && angle <= 157.5) {
+ out.down = true
+ out.left = true
+ } else if (angle > 157.5 && angle <= 202.5) {
+ out.left = true
+ } else if (angle > 202.5 && angle <= 247.5) {
+ out.left = true
+ out.up = true
+ } else if (angle > 247.5 && angle <= 292.5) {
+ out.up = true
+ } else if (angle > 292.5 && angle <= 337.5) {
+ out.up = true
+ out.right = true
+ } else {
+ out.right = true
+ }
+ return out
+}
+
+var globOut = {}
+
+function JoystickItem(props) {
+ return (
+ <>
+ {
+ props.onDirectionChange({
+ isMoving: false,
+ direction: {
+ left: false,
+ right: false,
+ up: false,
+ down: false,
+ },
+ })
+ }}
+ move={(event) => {
+ const x1 = 0
+ const y1 = event.y
+ const x2 = event.x
+ const y2 = 0
+ var deltaX = x2 - x1 // distance between joystick and center
+ var deltaY = y2 - y1 // distance between joystick and center
+ var rad = Math.atan2(deltaY, deltaX) // In radians
+ var deg = (rad * 180) / Math.PI // In degrees
+ var direction = AngleToDirections(deg, true) // Convert degrees to direction
+ props.onDirectionChange({ isMoving: true, direction: direction })
+ }}
+ />
+ >
+ )
+}
+
+export default JoystickItem
diff --git a/client/src/components/MobileVirtualJoystick.tsx b/client/src/components/MobileVirtualJoystick.tsx
new file mode 100644
index 00000000..89286fef
--- /dev/null
+++ b/client/src/components/MobileVirtualJoystick.tsx
@@ -0,0 +1,73 @@
+import React, { useRef, useState, useEffect } from 'react'
+import styled from 'styled-components'
+import 'emoji-mart/css/emoji-mart.css'
+import JoystickItem from './Joystick'
+
+import phaserGame from '../PhaserGame'
+import Game from '../scenes/Game'
+
+import { useAppDispatch, useAppSelector } from '../hooks'
+
+export interface JoystickMovement {
+ isMoving: boolean
+ direction: {
+ left: boolean
+ right: boolean
+ up: boolean
+ down: boolean
+ }
+}
+
+const Backdrop = styled.div`
+ position: fixed;
+ bottom: 100px;
+ right: 32px;
+ max-height: 50%;
+ max-width: 100%;
+`
+
+const Wrapper = styled.div`
+ position: relative;
+ height: 100%;
+ padding: 16px;
+ display: flex;
+ flex-direction: column;
+`
+
+const JoystickWrapper = styled.div`
+ margin-top: auto;
+ align-self: flex-end;
+`
+
+export default function MobileVirtualJoystick() {
+ const inputRef = useRef(null)
+
+ const showJoystick = useAppSelector((state) => state.user.showJoystick)
+ const focused = useAppSelector((state) => state.chat.focused)
+ const showChat = useAppSelector((state) => state.chat.showChat)
+ const game = phaserGame.scene.keys.game as Game
+
+ useEffect(() => {
+ if (focused) {
+ inputRef.current?.focus()
+ }
+ }, [focused])
+
+ useEffect(() => {}, [showJoystick, showChat])
+
+ const handleMovement = (movement: JoystickMovement) => {
+ game.myPlayer?.handleJoystickMovement(movement)
+ }
+
+ return (
+
+
+ {(!showChat || window.innerWidth > 650) && showJoystick && (
+
+
+
+ )}
+
+
+ )
+}
diff --git a/client/src/components/WhiteboardDialog.tsx b/client/src/components/WhiteboardDialog.tsx
index 317f8fc0..dc43b73c 100644
--- a/client/src/components/WhiteboardDialog.tsx
+++ b/client/src/components/WhiteboardDialog.tsx
@@ -14,6 +14,8 @@ const Backdrop = styled.div`
height: 100vh;
overflow: hidden;
padding: 16px 180px 16px 16px;
+ width: 100%;
+ height: 100%;
`
const Wrapper = styled.div`
width: 100%;
@@ -25,11 +27,12 @@ const Wrapper = styled.div`
position: relative;
display: flex;
flex-direction: column;
+ min-width: max-content;
.close {
position: absolute;
- top: 16px;
- right: 16px;
+ top: 0px;
+ right: 0px;
}
`
@@ -37,7 +40,7 @@ const WhiteboardWrapper = styled.div`
flex: 1;
border-radius: 25px;
overflow: hidden;
- margin-right: 50px;
+ margin-right: 25px;
iframe {
width: 100%;
diff --git a/client/src/stores/UserStore.ts b/client/src/stores/UserStore.ts
index a51116e1..8c158579 100644
--- a/client/src/stores/UserStore.ts
+++ b/client/src/stores/UserStore.ts
@@ -18,6 +18,7 @@ export const userSlice = createSlice({
videoConnected: false,
loggedIn: false,
playerNameMap: new Map(),
+ showJoystick: window.innerWidth < 650,
},
reducers: {
toggleBackgroundMode: (state) => {
@@ -43,6 +44,9 @@ export const userSlice = createSlice({
removePlayerNameMap: (state, action: PayloadAction) => {
state.playerNameMap.delete(sanitizeId(action.payload))
},
+ setShowJoystick: (state, action: PayloadAction) => {
+ state.showJoystick = action.payload
+ },
},
})
@@ -53,6 +57,7 @@ export const {
setLoggedIn,
setPlayerNameMap,
removePlayerNameMap,
+ setShowJoystick,
} = userSlice.actions
export default userSlice.reducer
diff --git a/client/yarn.lock b/client/yarn.lock
index e59156fa..5a8d9349 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -1549,6 +1549,11 @@ react-is@^17.0.1, react-is@^17.0.2:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+react-joystick-component@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/react-joystick-component/-/react-joystick-component-6.0.0.tgz#5768762a6c52de57215a74ef54438221afedf36b"
+ integrity sha512-kbhDrPbasELoQcUxG+YNqsjsZDV3xo0TBnGXjv/TekoLZHZmmj/X/Y85YfvEPzWos6C3YmOjQKc5gK59o+jI2w==
+
react-redux@^7.2.5:
version "7.2.8"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.8.tgz#a894068315e65de5b1b68899f9c6ee0923dd28de"