From d7a74baa9d6344ce9e0a04df85f778f3196eef6f Mon Sep 17 00:00:00 2001 From: kharhamel Date: Wed, 14 Apr 2021 17:47:26 +0200 Subject: [PATCH 1/4] FEATURE: add the ability to zoom in and out using touch screen --- front/.eslintrc.json | 3 +-- front/src/Phaser/Game/GameManager.ts | 2 +- front/src/Phaser/Game/GameScene.ts | 10 +++++++--- front/src/Phaser/Login/EnableCameraScene.ts | 6 ++++++ front/src/Phaser/Login/LoginScene.ts | 5 +++++ front/src/Phaser/Login/SelectCharacterScene.ts | 8 ++++++-- front/src/Phaser/Login/SelectCompanionScene.ts | 6 ++++++ front/src/Phaser/UserInput/PinchManager.ts | 16 ++++++++++++++++ front/src/Touch/TouchScreenManager.ts | 16 ++++++++++++++++ front/src/index.ts | 3 +-- front/src/rex-plugins.d.ts | 12 ++++++++++++ 11 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 front/src/Phaser/UserInput/PinchManager.ts create mode 100644 front/src/Touch/TouchScreenManager.ts create mode 100644 front/src/rex-plugins.d.ts diff --git a/front/.eslintrc.json b/front/.eslintrc.json index 3aab37d9..3ba19cb3 100644 --- a/front/.eslintrc.json +++ b/front/.eslintrc.json @@ -24,7 +24,6 @@ "@typescript-eslint" ], "rules": { - "no-unused-vars": "off", - "@typescript-eslint/no-explicit-any": "error" + "no-unused-vars": "off" } } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index c146c06d..da10a8ca 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -34,7 +34,7 @@ export class GameManager { public async init(scenePlugin: Phaser.Scenes.ScenePlugin): Promise { this.startRoom = await connectionManager.initGameConnexion(); await this.loadMap(this.startRoom, scenePlugin); - + if (!this.playerName) { return LoginSceneName; } else if (!this.characterLayers || !this.characterLayers.length) { diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 294174a2..f18a11d1 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -52,9 +52,7 @@ import {jitsiFactory} from "../../WebRtc/JitsiFactory"; import {urlManager} from "../../Url/UrlManager"; import {audioManager} from "../../WebRtc/AudioManager"; import {IVirtualJoystick} from "../../types"; -const { - default: VirtualJoystick, -} = require("phaser3-rex-plugins/plugins/virtualjoystick.js"); +import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; import {PresentationModeIcon} from "../Components/PresentationModeIcon"; import {ChatModeIcon} from "../Components/ChatModeIcon"; import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon"; @@ -74,6 +72,8 @@ import DOMElement = Phaser.GameObjects.DOMElement; import {Subscription} from "rxjs"; import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream"; import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; +import {PinchManager} from "../UserInput/PinchManager"; export interface GameSceneInitInterface { initPosition: PointInterface|null, @@ -351,6 +351,10 @@ export class GameScene extends ResizableScene implements CenterListener { urlManager.pushRoomIdToUrl(this.room); this.startLayerName = urlManager.getStartLayerNameFromUrl(); + if (touchScreenManager.supportTouchScreen) { + new PinchManager(this); + } + this.messageSubscription = worldFullMessageStream.stream.subscribe((message) => this.showWorldFullError()) const playerName = gameManager.getPlayerName(); diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index c3aa5077..6a91fc34 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -7,6 +7,8 @@ import {RESOLUTION} from "../../Enum/EnvironmentVariable"; import {SoundMeter} from "../Components/SoundMeter"; import {SoundMeterSprite} from "../Components/SoundMeterSprite"; import {HtmlUtils} from "../../WebRtc/HtmlUtils"; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; +import {PinchManager} from "../UserInput/PinchManager"; export const EnableCameraSceneName = "EnableCameraScene"; enum LoginTextures { @@ -54,6 +56,10 @@ export class EnableCameraScene extends Phaser.Scene { } create() { + if (touchScreenManager.supportTouchScreen) { + new PinchManager(this); + } + this.textField = new TextField(this, this.game.renderer.width / 2, 20, 'Turn on your camera and microphone'); this.pressReturnField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 30, 'Touch here\n\n or \n\nPress enter to start'); diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts index 4ff582b4..057cb6ae 100644 --- a/front/src/Phaser/Login/LoginScene.ts +++ b/front/src/Phaser/Login/LoginScene.ts @@ -7,6 +7,8 @@ import {ResizableScene} from "./ResizableScene"; import {isUserNameValid, maxUserNameLength} from "../../Connexion/LocalUser"; import { localUserStore } from "../../Connexion/LocalUserStore"; import Rectangle = Phaser.GameObjects.Rectangle; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; +import {PinchManager} from "../UserInput/PinchManager"; //todo: put this constants in a dedicated file export const LoginSceneName = "LoginScene"; @@ -39,6 +41,9 @@ export class LoginScene extends ResizableScene { } create() { + if (touchScreenManager.supportTouchScreen) { + new PinchManager(this); + } this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, maxUserNameLength, this.name,(text: string) => { this.name = text; diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index edd09a91..3c8d0281 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -4,13 +4,14 @@ import Image = Phaser.GameObjects.Image; import Rectangle = Phaser.GameObjects.Rectangle; import {EnableCameraSceneName} from "./EnableCameraScene"; import {CustomizeSceneName} from "./CustomizeScene"; -import {ResizableScene} from "./ResizableScene"; import {localUserStore} from "../../Connexion/LocalUserStore"; -import {loadAllDefaultModels, loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager"; +import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager"; import {addLoader} from "../Components/Loader"; import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import {AbstractCharacterScene} from "./AbstractCharacterScene"; import {areCharacterLayersValid} from "../../Connexion/LocalUser"; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; +import {PinchManager} from "../UserInput/PinchManager"; //todo: put this constants in a dedicated file @@ -66,6 +67,9 @@ export class SelectCharacterScene extends AbstractCharacterScene { } create() { + if (touchScreenManager.supportTouchScreen) { + new PinchManager(this); + } this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your character'); this.pressReturnField = new TextField( this, diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts index c7ee56f1..aeacd0c2 100644 --- a/front/src/Phaser/Login/SelectCompanionScene.ts +++ b/front/src/Phaser/Login/SelectCompanionScene.ts @@ -8,6 +8,8 @@ import { EnableCameraSceneName } from "./EnableCameraScene"; import { localUserStore } from "../../Connexion/LocalUserStore"; import { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures"; import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager"; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; +import {PinchManager} from "../UserInput/PinchManager"; export const SelectCompanionSceneName = "SelectCompanionScene"; @@ -54,6 +56,10 @@ export class SelectCompanionScene extends ResizableScene { } create() { + if (touchScreenManager.supportTouchScreen) { + new PinchManager(this); + } + this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your companion'); const confirmTouchAreaY = 115 + 32 * Math.ceil(this.companionModels.length / this.nbCharactersPerRow); diff --git a/front/src/Phaser/UserInput/PinchManager.ts b/front/src/Phaser/UserInput/PinchManager.ts new file mode 100644 index 00000000..f412b787 --- /dev/null +++ b/front/src/Phaser/UserInput/PinchManager.ts @@ -0,0 +1,16 @@ +import {Pinch} from "phaser3-rex-plugins/plugins/gestures.js"; + +export class PinchManager { + private scene: Phaser.Scene; + private pinch!: any; + + constructor(scene: Phaser.Scene) { + this.scene = scene; + this.pinch = new Pinch(scene); + + this.pinch.on('pinch', (pinch:any) => { + this.scene.cameras.main.zoom *= pinch.scaleFactor; + }); + } + +} \ No newline at end of file diff --git a/front/src/Touch/TouchScreenManager.ts b/front/src/Touch/TouchScreenManager.ts new file mode 100644 index 00000000..dcb56ded --- /dev/null +++ b/front/src/Touch/TouchScreenManager.ts @@ -0,0 +1,16 @@ + +class TouchScreenManager { + + readonly supportTouchScreen:boolean; + + constructor() { + this.supportTouchScreen = this.detectTouchscreen(); + } + + //found here: https://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript#4819886 + detectTouchscreen(): boolean { + return (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)); + } +} + +export const touchScreenManager = new TouchScreenManager(); \ No newline at end of file diff --git a/front/src/index.ts b/front/src/index.ts index aab45a9b..5a18951b 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -2,7 +2,7 @@ import 'phaser'; import GameConfig = Phaser.Types.Core.GameConfig; import "../dist/resources/style/index.scss"; -import {DEBUG_MODE, JITSI_URL, RESOLUTION} from "./Enum/EnvironmentVariable"; +import {DEBUG_MODE, RESOLUTION} from "./Enum/EnvironmentVariable"; import {LoginScene} from "./Phaser/Login/LoginScene"; import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene"; import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene"; @@ -17,7 +17,6 @@ import {HelpCameraSettingsScene} from "./Phaser/Menu/HelpCameraSettingsScene"; import {localUserStore} from "./Connexion/LocalUserStore"; import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene"; import {iframeListener} from "./Api/IframeListener"; -import {discussionManager} from "./WebRtc/DiscussionManager"; const {width, height} = coWebsiteManager.getGameSize(); diff --git a/front/src/rex-plugins.d.ts b/front/src/rex-plugins.d.ts new file mode 100644 index 00000000..bb0816b6 --- /dev/null +++ b/front/src/rex-plugins.d.ts @@ -0,0 +1,12 @@ + +declare module 'phaser3-rex-plugins/plugins/virtualjoystick.js' { + const content: any; + export default content; +} +declare module 'phaser3-rex-plugins/plugins/gestures-plugin.js' { + const content: any; + export default content; +} +declare module 'phaser3-rex-plugins/plugins/gestures.js' { + export const Pinch: any; +} \ No newline at end of file From 56287a29583e46572a8aabbda24b6dca2bc1f537 Mon Sep 17 00:00:00 2001 From: kharhamel Date: Fri, 16 Apr 2021 18:49:04 +0200 Subject: [PATCH 2/4] put the virtual joystick into the userInputManager and restricted it to touchscreens --- front/.eslintrc.json | 3 +- front/src/Phaser/Game/GameScene.ts | 20 +------ front/src/Phaser/UserInput/PinchManager.ts | 12 +++-- .../src/Phaser/UserInput/UserInputManager.ts | 53 +++++++++++++------ front/src/rex-plugins.d.ts | 6 +-- 5 files changed, 54 insertions(+), 40 deletions(-) diff --git a/front/.eslintrc.json b/front/.eslintrc.json index 3ba19cb3..3aab37d9 100644 --- a/front/.eslintrc.json +++ b/front/.eslintrc.json @@ -24,6 +24,7 @@ "@typescript-eslint" ], "rules": { - "no-unused-vars": "off" + "no-unused-vars": "off", + "@typescript-eslint/no-explicit-any": "error" } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index f18a11d1..cfefd548 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -408,26 +408,10 @@ export class GameScene extends ResizableScene implements CenterListener { //initialise list of other player this.MapPlayers = this.physics.add.group({immovable: true}); - this.virtualJoystick = new VirtualJoystick(this, { - x: this.game.renderer.width / 2, - y: this.game.renderer.height / 2, - radius: 20, - base: this.add.circle(0, 0, 20), - thumb: this.add.circle(0, 0, 10), - enable: true, - dir: "8dir", - }); - this.virtualJoystick.visible = true; + //create input to move mediaManager.setUserInputManager(this.userInputManager); - this.userInputManager = new UserInputManager(this, this.virtualJoystick); - - // Listener event to reposition virtual joystick - // whatever place you click in game area - this.input.on('pointerdown', (pointer: { x: number; y: number; }) => { - this.virtualJoystick.x = pointer.x; - this.virtualJoystick.y = pointer.y; - }); + this.userInputManager = new UserInputManager(this); if (localUserStore.getFullscreen()) { document.querySelector('body')?.requestFullscreen(); diff --git a/front/src/Phaser/UserInput/PinchManager.ts b/front/src/Phaser/UserInput/PinchManager.ts index f412b787..f7c445fa 100644 --- a/front/src/Phaser/UserInput/PinchManager.ts +++ b/front/src/Phaser/UserInput/PinchManager.ts @@ -2,14 +2,20 @@ import {Pinch} from "phaser3-rex-plugins/plugins/gestures.js"; export class PinchManager { private scene: Phaser.Scene; - private pinch!: any; + private pinch!: any; // eslint-disable-line constructor(scene: Phaser.Scene) { this.scene = scene; this.pinch = new Pinch(scene); - this.pinch.on('pinch', (pinch:any) => { - this.scene.cameras.main.zoom *= pinch.scaleFactor; + this.pinch.on('pinch', (pinch:any) => { // eslint-disable-line + let newZoom = this.scene.cameras.main.zoom * pinch.scaleFactor; + if (newZoom < 0.25) { + newZoom = 0.25; + } else if(newZoom > 2) { + newZoom = 2; + } + this.scene.cameras.main.setZoom(newZoom); }); } diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index c3f2c0bb..ddc3ffa8 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -1,5 +1,6 @@ import { Direction, IVirtualJoystick } from "../../types"; import {GameScene} from "../Game/GameScene"; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; const { default: VirtualJoystick, } = require("phaser3-rex-plugins/plugins/virtualjoystick.js"); @@ -44,17 +45,39 @@ export class UserInputManager { private Scene: GameScene; private isInputDisabled : boolean; - private joystick : IVirtualJoystick; + private joystick!: IVirtualJoystick; private joystickEvents = new ActiveEventList(); private joystickForceThreshold = 60; private joystickForceAccuX = 0; private joystickForceAccuY = 0; - constructor(Scene: GameScene, virtualJoystick: IVirtualJoystick) { + constructor(Scene: GameScene) { this.Scene = Scene; this.isInputDisabled = false; this.initKeyBoardEvent(); - this.joystick = virtualJoystick; + if (touchScreenManager.supportTouchScreen) { + this.initVirtualJoystick(); + } + } + + initVirtualJoystick() { + this.joystick = new VirtualJoystick(this, { + x: this.Scene.game.renderer.width / 2, + y: this.Scene.game.renderer.height / 2, + radius: 20, + base: this.Scene.add.circle(0, 0, 20), + thumb: this.Scene.add.circle(0, 0, 10), + enable: true, + dir: "8dir", + }); + this.joystick.visible = true; + + // Listener event to reposition virtual joystick + // whatever place you click in game area + this.Scene.input.on('pointerdown', (pointer: { x: number; y: number; }) => { + this.joystick.x = pointer.x; + this.joystick.y = pointer.y; + }); this.joystick.on("update", () => { this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0; this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0; @@ -62,18 +85,18 @@ export class UserInputManager { for (const name in cursorKeys) { const key = cursorKeys[name as Direction]; switch (name) { - case "up": - this.joystickEvents.set(UserInputEvent.MoveUp, key.isDown); - break; - case "left": - this.joystickEvents.set(UserInputEvent.MoveLeft, key.isDown); - break; - case "down": - this.joystickEvents.set(UserInputEvent.MoveDown, key.isDown); - break; - case "right": - this.joystickEvents.set(UserInputEvent.MoveRight, key.isDown); - break; + case "up": + this.joystickEvents.set(UserInputEvent.MoveUp, key.isDown); + break; + case "left": + this.joystickEvents.set(UserInputEvent.MoveLeft, key.isDown); + break; + case "down": + this.joystickEvents.set(UserInputEvent.MoveDown, key.isDown); + break; + case "right": + this.joystickEvents.set(UserInputEvent.MoveRight, key.isDown); + break; } } }); diff --git a/front/src/rex-plugins.d.ts b/front/src/rex-plugins.d.ts index bb0816b6..7ba8f65b 100644 --- a/front/src/rex-plugins.d.ts +++ b/front/src/rex-plugins.d.ts @@ -1,12 +1,12 @@ declare module 'phaser3-rex-plugins/plugins/virtualjoystick.js' { - const content: any; + const content: any; // eslint-disable-line export default content; } declare module 'phaser3-rex-plugins/plugins/gestures-plugin.js' { - const content: any; + const content: any; // eslint-disable-line export default content; } declare module 'phaser3-rex-plugins/plugins/gestures.js' { - export const Pinch: any; + export const Pinch: any; // eslint-disable-line } \ No newline at end of file From 415d8f9466699d15d3a9e9de8afaff1c91cdef3d Mon Sep 17 00:00:00 2001 From: kharhamel Date: Tue, 20 Apr 2021 10:52:06 +0200 Subject: [PATCH 3/4] the joystick is now visible only when pointer is down --- front/src/Phaser/Game/GameScene.ts | 3 --- .../src/Phaser/UserInput/UserInputManager.ts | 23 +++++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index cfefd548..0f31a519 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -51,8 +51,6 @@ import {Room} from "../../Connexion/Room"; import {jitsiFactory} from "../../WebRtc/JitsiFactory"; import {urlManager} from "../../Url/UrlManager"; import {audioManager} from "../../WebRtc/AudioManager"; -import {IVirtualJoystick} from "../../types"; -import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; import {PresentationModeIcon} from "../Components/PresentationModeIcon"; import {ChatModeIcon} from "../Components/ChatModeIcon"; import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon"; @@ -168,7 +166,6 @@ export class GameScene extends ResizableScene implements CenterListener { private messageSubscription: Subscription|null = null; private popUpElements : Map = new Map(); private originalMapUrl: string|undefined; - public virtualJoystick!: IVirtualJoystick; constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) { super({ diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index ddc3ffa8..602afee7 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -1,9 +1,7 @@ import { Direction, IVirtualJoystick } from "../../types"; import {GameScene} from "../Game/GameScene"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; -const { - default: VirtualJoystick, -} = require("phaser3-rex-plugins/plugins/virtualjoystick.js"); +import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; interface UserInputManagerDatum { keyInstance: Phaser.Input.Keyboard.Key; @@ -21,6 +19,9 @@ export enum UserInputEvent { JoystickMove, } +const outOfScreenX = -1000; +const outOfScreenY = -1000; + //we cannot use a map structure so we have to create a replacment export class ActiveEventList { private eventMap : Map = new Map(); @@ -62,22 +63,23 @@ export class UserInputManager { initVirtualJoystick() { this.joystick = new VirtualJoystick(this, { - x: this.Scene.game.renderer.width / 2, - y: this.Scene.game.renderer.height / 2, + x: outOfScreenX, + y: outOfScreenY, radius: 20, - base: this.Scene.add.circle(0, 0, 20), - thumb: this.Scene.add.circle(0, 0, 10), + base: this.Scene.add.circle(0, 0, 20, 0xdddddd), + thumb: this.Scene.add.circle(0, 0, 10, 0x000000), enable: true, dir: "8dir", }); - this.joystick.visible = true; - // Listener event to reposition virtual joystick - // whatever place you click in game area this.Scene.input.on('pointerdown', (pointer: { x: number; y: number; }) => { this.joystick.x = pointer.x; this.joystick.y = pointer.y; }); + this.Scene.input.on('pointerup', (pointer: { x: number; y: number; }) => { + this.joystick.x = outOfScreenX; + this.joystick.y = outOfScreenY; + }); this.joystick.on("update", () => { this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0; this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0; @@ -128,6 +130,7 @@ export class UserInputManager { this.Scene.input.keyboard.removeAllListeners(); } + //todo: should we also disable the joystick? disableControls(){ this.Scene.input.keyboard.removeAllKeys(); this.isInputDisabled = true; From 341b048d6c85a83b0acb8ec6702009e51555b969 Mon Sep 17 00:00:00 2001 From: kharhamel Date: Tue, 20 Apr 2021 11:40:39 +0200 Subject: [PATCH 4/4] improved the visuals of the joystick --- .../resources/objects/joystickSplitted.png | Bin 0 -> 12833 bytes .../objects/smallHandleFilledGrey.png | Bin 0 -> 3554 bytes front/src/Phaser/Components/MobileJoystick.ts | 35 ++++++++++++++++++ front/src/Phaser/Game/GameScene.ts | 8 +++- .../src/Phaser/UserInput/UserInputManager.ts | 27 ++------------ 5 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 front/dist/resources/objects/joystickSplitted.png create mode 100644 front/dist/resources/objects/smallHandleFilledGrey.png create mode 100644 front/src/Phaser/Components/MobileJoystick.ts diff --git a/front/dist/resources/objects/joystickSplitted.png b/front/dist/resources/objects/joystickSplitted.png new file mode 100644 index 0000000000000000000000000000000000000000..04ae67a3bef04ab710b4793e0f9990609d5a4ed4 GIT binary patch literal 12833 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�ub=(88;&BQWopi zwP(dGR0xllWKe3(1@>{fuu+ysiCJAQfq7?lf05p84VO9JGdy$`Rz1c@k#?@1a<$8V z$7l+3$2mD42i-|K=-W+p!{2L`YKkT)cjMMIpz4x-&LL249#xl>#k2h-*8{#?4!b~l zuri@xOlgTGw}2gVRe^dA7v>^7Vg3e1PVI5%MsL{_eF~=y?T6sT{xvu63Iu84J-mbH}`YDnl_d%d?1sf5( z1(7C~n>W9oY(CA&f*A?5kshjaXbV@s_ai-igbfEMn<4a{mW=hQyMRh_0kxrkMo9;@ zvoG-@AlkFV%_~79EAO)$bd}m*>w>KR`kTO9pOLvyy8QKpt@-ShiVSb#Krpw$*oBKe zHU0mfeY{TfKsjm7SU)%m$kC4kT5GOtjt>XrpnZ^%40BtvR*tMmPhFw=&41PyAmcU@Kwzfqx~Bv7li$>ht1 zNFRIRple^}?pLaVu(__v^V{b-4&m48_^xab(HF-Ky}dr|=5^(GY8Q!_nK_G9nEu)h zp8;>me#~?0$8{M;F?An-fz1wXju!WHXnY@tZYK*fJeYKUEWYm)#24I-U(2`Z{;>OX z^qTOHrc{lrqABGOvg-wu?;0>dUJ*;lu z<%euYLro4iyWWVPja#5bc;0k#vpzKKT{t_UK`N!V^XvBzMc{~oMU&D!V(M@T0UR@Ph17qDe_rgPmyrKhFC|MP}85U(L=IX5<|e%;E4+_ zp#7s1oPKxnB`O0cc>-xb;(#dlQoECL1xG$YXuh{*4XLyH%48N%iNtZ5X<((h3!&0yI>I3mBHa7Mc>-1G9Vvvf7JHmi$d<9b@BW2&5wRujI8Ke9bOq>bQB8}haoapMp zU8Pt<ns+osNIPU!3HQ--CB_n$Z@8NrJ5t79|E3)jc<(R5*kNBq7gey!oiAAl1q zEBMO%=<+e&Gi6`zB2Cy<;B%S0dW!Q!WJ8XtW%;$YZh&RKQA~A5p0iw23~qu?CI)4L zcy;RL0nsJCZ16flk_XsqJ^VQ&3wb+t)+Ylq?TVPVuu=tZw zzZT11h4@qxyXj=Dk*TKxs}t{_U~iCgnF1c>st>dYiI0ZPDDh&q; ziS&dGW2f%G7xNR`zV@vez5FBEh%cs`DcEujnmqGuP@JR(_pYg~jMVbyL^=r%6&=8m z&-quZq3JG26gG1{FWf7Tb1_0DwBh@Q%k{S&XH6KlM{Bt0A`#lN zUxE8$FN#ZIPrz5>tRM5NN)1>853u2Gw8`E(`s-v+Kfz}hw|=^B{gHEVK7iX!pF>pX zBgt;=lq>(-KokI6kg~b+_Avj~+u`D!nzx049>+9&@8ox%x(_Cl_6zKMbqdq$aoC{&&^O3DqXL+!ru3TVOMQ+gy`2^8UUyfE2 z>1jf@v%ytj8?cqpA>*wdJsGm-gjue|*Y2dBLTs^z-hS2Vs4sme5WHVKZesHHEOVRZ zxAiUSueYC|k3+@EH$RA zu(7c0J`?gfD@NzVAz^P-N81|Zr4(cX8)|lcd5s3WzBB%2`%m!lz9}UeWAS-%ve{*% z@_JwA1Y3J_hyk>R?Uk`r_N7t6wq{uciAv(##CdPToJK`=qzw_^s5=MM<`Befr^q)R z{X0t;@(Oi5K!&IA5xh>Urj$0H`6WO(QQicL;W?MF?QVf%lkcxPnmpWh=LX}gGzJ>-cg-z`N=1P3nCfgW6-<0G5z7L2z=u{5MZy!N?!^K_2p+pT{AT;>M8&{uSs<$4pD%*XWSv xbO&mYK&vV6&@cY?IYON_{{QEZTfe>!Z9h)k@Md^+1AOxV>1u;ERO?#k{{vv2v@ZYv literal 0 HcmV?d00001 diff --git a/front/dist/resources/objects/smallHandleFilledGrey.png b/front/dist/resources/objects/smallHandleFilledGrey.png new file mode 100644 index 0000000000000000000000000000000000000000..40e968fb816c3f3400ae00a0af985c3fdf42c75c GIT binary patch literal 3554 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 literal 0 HcmV?d00001 diff --git a/front/src/Phaser/Components/MobileJoystick.ts b/front/src/Phaser/Components/MobileJoystick.ts new file mode 100644 index 00000000..1ace529c --- /dev/null +++ b/front/src/Phaser/Components/MobileJoystick.ts @@ -0,0 +1,35 @@ +import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; + +const outOfScreenX = -1000; +const outOfScreenY = -1000; + + +//the assets were found here: https://hannemann.itch.io/virtual-joystick-pack-free +export const joystickBaseKey = 'joystickBase'; +export const joystickBaseImg = 'resources/objects/joystickSplitted.png'; +export const joystickThumbKey = 'joystickThumb'; +export const joystickThumbImg = 'resources/objects/smallHandleFilledGrey.png'; + +export class MobileJoystick extends VirtualJoystick { + + constructor(scene: Phaser.Scene) { + super(scene, { + x: outOfScreenX, + y: outOfScreenY, + radius: 20, + base: scene.add.image(0, 0, joystickBaseKey).setDisplaySize(60, 60).setDepth(99999), + thumb: scene.add.image(0, 0, joystickThumbKey).setDisplaySize(30, 30).setDepth(99999), + enable: true, + dir: "8dir", + }); + + this.scene.input.on('pointerdown', (pointer: { x: number; y: number; }) => { + this.x = pointer.x; + this.y = pointer.y; + }); + this.scene.input.on('pointerup', () => { + this.x = outOfScreenX; + this.y = outOfScreenY; + }); + } +} \ No newline at end of file diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 0f31a519..9c261cf2 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -72,6 +72,7 @@ import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream"; import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {PinchManager} from "../UserInput/PinchManager"; +import {joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey} from "../Components/MobileJoystick"; export interface GameSceneInitInterface { initPosition: PointInterface|null, @@ -189,6 +190,7 @@ export class GameScene extends ResizableScene implements CenterListener { //hook preload scene preload(): void { + addLoader(this); const localUser = localUserStore.getLocalUser(); const textures = localUser?.textures; if (textures) { @@ -198,6 +200,10 @@ export class GameScene extends ResizableScene implements CenterListener { } this.load.image(openChatIconName, 'resources/objects/talk.png'); + if (touchScreenManager.supportTouchScreen) { + this.load.image(joystickBaseKey, joystickBaseImg); + this.load.image(joystickThumbKey, joystickThumbImg); + } this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => { // If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments) if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:') && this.originalMapUrl === undefined) { @@ -244,8 +250,6 @@ export class GameScene extends ResizableScene implements CenterListener { this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32}); this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); - - addLoader(this); } // FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving. diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index 602afee7..2f14672b 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -1,7 +1,7 @@ -import { Direction, IVirtualJoystick } from "../../types"; +import { Direction } from "../../types"; import {GameScene} from "../Game/GameScene"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; -import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; +import {MobileJoystick} from "../Components/MobileJoystick"; interface UserInputManagerDatum { keyInstance: Phaser.Input.Keyboard.Key; @@ -19,8 +19,6 @@ export enum UserInputEvent { JoystickMove, } -const outOfScreenX = -1000; -const outOfScreenY = -1000; //we cannot use a map structure so we have to create a replacment export class ActiveEventList { @@ -46,7 +44,7 @@ export class UserInputManager { private Scene: GameScene; private isInputDisabled : boolean; - private joystick!: IVirtualJoystick; + private joystick!: MobileJoystick; private joystickEvents = new ActiveEventList(); private joystickForceThreshold = 60; private joystickForceAccuX = 0; @@ -62,24 +60,7 @@ export class UserInputManager { } initVirtualJoystick() { - this.joystick = new VirtualJoystick(this, { - x: outOfScreenX, - y: outOfScreenY, - radius: 20, - base: this.Scene.add.circle(0, 0, 20, 0xdddddd), - thumb: this.Scene.add.circle(0, 0, 10, 0x000000), - enable: true, - dir: "8dir", - }); - - this.Scene.input.on('pointerdown', (pointer: { x: number; y: number; }) => { - this.joystick.x = pointer.x; - this.joystick.y = pointer.y; - }); - this.Scene.input.on('pointerup', (pointer: { x: number; y: number; }) => { - this.joystick.x = outOfScreenX; - this.joystick.y = outOfScreenY; - }); + this.joystick = new MobileJoystick(this.Scene); this.joystick.on("update", () => { this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0; this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0;