diff --git a/CHANGELOG.md b/CHANGELOG.md index 93940b0c..3117ea31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,18 @@ -## Version 1.3.0 - in dev +## Version 1.3.9 - in dev + +### Updates + +- Mobile support has been improved + - WorkAdventure automatically sets the zoom level based on the viewport size to ensure a sensible size of the map is visible, whatever the viewport used + - Mouse wheel support to zoom in / out + - Pinch support on mobile to zoom in / out + - Improved virtual joystick size (adapts to the zoom level) + +### Bug Fixes + +- Pinch gesture does no longer move the character + +## Version 1.3.0 ### New Features diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts index ca1bd6d2..7dd3a175 100644 --- a/front/src/Enum/EnvironmentVariable.ts +++ b/front/src/Enum/EnvironmentVariable.ts @@ -10,8 +10,6 @@ const TURN_USER: string = process.env.TURN_USER || ''; const TURN_PASSWORD: string = process.env.TURN_PASSWORD || ''; const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL; const JITSI_PRIVATE_MODE : boolean = process.env.JITSI_PRIVATE_MODE == "true"; -const RESOLUTION = 2; -const ZOOM_LEVEL = 1; const POSITION_DELAY = 200; // Wait 200ms between sending position events const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '') || 8; @@ -25,8 +23,6 @@ export { PUSHER_URL, UPLOADER_URL, ADMIN_URL, - RESOLUTION, - ZOOM_LEVEL, POSITION_DELAY, MAX_EXTRAPOLATION_TIME, STUN_SERVER, diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 4500779f..5525bfc7 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -15,8 +15,6 @@ import { JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY, - RESOLUTION, - ZOOM_LEVEL } from "../../Enum/EnvironmentVariable"; import { ITiledMap, diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 673ba20a..dda83923 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -11,7 +11,6 @@ import {AbstractCharacterScene} from "./AbstractCharacterScene"; import {areCharacterLayersValid} from "../../Connexion/LocalUser"; import { MenuScene } from "../Menu/MenuScene"; import { SelectCharacterSceneName } from "./SelectCharacterScene"; -import { RESOLUTION } from "../../Enum/EnvironmentVariable"; export const CustomizeSceneName = "CustomizeScene"; @@ -52,8 +51,8 @@ export class CustomizeScene extends AbstractCharacterScene { } create() { - const middleX = this.getMiddleX(); - this.customizeSceneElement = this.add.dom(middleX, 0).createFromCache(customizeSceneKey); + this.customizeSceneElement = this.add.dom(-1000, 0).createFromCache(customizeSceneKey); + this.centerXDomElement(this.customizeSceneElement, 150); MenuScene.revealMenusAfterInit(this.customizeSceneElement, customizeSceneKey); this.customizeSceneElement.addListener('click'); @@ -113,6 +112,8 @@ export class CustomizeScene extends AbstractCharacterScene { this.moveLayers(); this.updateSelectedLayer(); } + + this.onResize(); } private moveCursorHorizontally(index: number): void { @@ -257,16 +258,10 @@ export class CustomizeScene extends AbstractCharacterScene { this.containersRow[i][j].add(children); } } - } + } + + update(time: number, delta: number): void { - update(time: number, delta: number): void { - const middleX = this.getMiddleX(); - this.tweens.add({ - targets: this.customizeSceneElement, - x: middleX, - duration: 1000, - ease: 'Power3' - }); } public onResize(): void { @@ -275,26 +270,9 @@ export class CustomizeScene extends AbstractCharacterScene { this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3; - const middleX = this.getMiddleX(); - this.tweens.add({ - targets: this.customizeSceneElement, - x: middleX, - duration: 1000, - ease: 'Power3' - }); + this.centerXDomElement(this.customizeSceneElement, 150); } - protected getMiddleX() : number{ - return (this.game.renderer.width / RESOLUTION) - - ( - this.customizeSceneElement - && this.customizeSceneElement.node - && this.customizeSceneElement.node.getBoundingClientRect().width > 0 - ? (this.customizeSceneElement.node.getBoundingClientRect().width / (2*RESOLUTION)) - : 150 - ); - } - private nextSceneToCamera(){ const layers: string[] = []; let i = 0; diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index 1af0c092..065973b6 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -1,9 +1,7 @@ import {gameManager} from "../Game/GameManager"; import {TextField} from "../Components/TextField"; import Image = Phaser.GameObjects.Image; -import Rectangle = Phaser.GameObjects.Rectangle; import {mediaManager} from "../../WebRtc/MediaManager"; -import {RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable"; import {SoundMeter} from "../Components/SoundMeter"; import {SoundMeterSprite} from "../Components/SoundMeterSprite"; import {HtmlUtils} from "../../WebRtc/HtmlUtils"; @@ -11,6 +9,7 @@ import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {PinchManager} from "../UserInput/PinchManager"; import Zone = Phaser.GameObjects.Zone; import { MenuScene } from "../Menu/MenuScene"; +import {ResizableScene} from "./ResizableScene"; export const EnableCameraSceneName = "EnableCameraScene"; enum LoginTextures { @@ -23,7 +22,7 @@ enum LoginTextures { const enableCameraSceneKey = 'enableCameraScene'; -export class EnableCameraScene extends Phaser.Scene { +export class EnableCameraScene extends ResizableScene { private textField!: TextField; private cameraNameField!: TextField; private arrowLeft!: Image; @@ -37,7 +36,6 @@ export class EnableCameraScene extends Phaser.Scene { private soundMeter: SoundMeter; private soundMeterSprite!: SoundMeterSprite; private microphoneNameField!: TextField; - private repositionCallback!: (this: Window, ev: UIEvent) => void; private enableCameraSceneElement!: Phaser.GameObjects.DOMElement; @@ -63,8 +61,9 @@ export class EnableCameraScene extends Phaser.Scene { create() { - const middleX = this.getMiddleX(); - this.enableCameraSceneElement = this.add.dom(middleX, 0).createFromCache(enableCameraSceneKey); + this.enableCameraSceneElement = this.add.dom(-1000, 0).createFromCache(enableCameraSceneKey); + this.centerXDomElement(this.enableCameraSceneElement, 300); + MenuScene.revealMenusAfterInit(this.enableCameraSceneElement, enableCameraSceneKey); const continuingButton = this.enableCameraSceneElement.getChildByID('enableCameraSceneFormSubmit') as HTMLButtonElement; @@ -133,8 +132,7 @@ export class EnableCameraScene extends Phaser.Scene { this.soundMeterSprite.setVisible(false); this.add.existing(this.soundMeterSprite); - this.repositionCallback = this.reposition.bind(this); - window.addEventListener('resize', this.repositionCallback); + this.onResize(); } private previousCam(): void { @@ -212,10 +210,9 @@ export class EnableCameraScene extends Phaser.Scene { this.arrowUp.setVisible(this.microphoneSelected > 0); } - this.reposition(); } - private reposition(): void { + public onResize(): void { let div = HtmlUtils.getElementByIdOrFail('myCamVideoSetup'); let bounds = div.getBoundingClientRect(); if (!div.srcObject) { @@ -228,18 +225,18 @@ export class EnableCameraScene extends Phaser.Scene { this.cameraNameField.x = this.game.renderer.width / 2; this.microphoneNameField.x = this.game.renderer.width / 2; - this.cameraNameField.y = bounds.top / RESOLUTION - 8; + this.cameraNameField.y = bounds.top / this.scale.zoom - 8; this.soundMeterSprite.x = this.game.renderer.width / 2 - this.soundMeterSprite.getWidth() / 2; - this.soundMeterSprite.y = bounds.bottom / RESOLUTION + 16; + this.soundMeterSprite.y = bounds.bottom / this.scale.zoom + 16; this.microphoneNameField.y = this.soundMeterSprite.y + 22; - this.arrowRight.x = bounds.right / RESOLUTION + 16; - this.arrowRight.y = (bounds.top + bounds.height / 2) / RESOLUTION; + this.arrowRight.x = bounds.right / this.scale.zoom + 16; + this.arrowRight.y = (bounds.top + bounds.height / 2) / this.scale.zoom; - this.arrowLeft.x = bounds.left / RESOLUTION - 16; - this.arrowLeft.y = (bounds.top + bounds.height / 2) / RESOLUTION; + this.arrowLeft.x = bounds.left / this.scale.zoom - 16; + this.arrowLeft.y = (bounds.top + bounds.height / 2) / this.scale.zoom; this.arrowDown.x = this.microphoneNameField.x + this.microphoneNameField.width / 2 + 16; this.arrowDown.y = this.microphoneNameField.y; @@ -257,20 +254,12 @@ export class EnableCameraScene extends Phaser.Scene { this.soundMeterSprite.setVolume(this.soundMeter.getVolume()); mediaManager.updateScene(); - const middleX = this.getMiddleX(); - this.tweens.add({ - targets: this.enableCameraSceneElement, - x: middleX, - duration: 1000, - ease: 'Power3' - }); - + this.centerXDomElement(this.enableCameraSceneElement, 300); } private login(): void { HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none'; this.soundMeter.stop(); - window.removeEventListener('resize', this.repositionCallback); mediaManager.stopCamera(); mediaManager.stopMicrophone(); @@ -290,15 +279,4 @@ export class EnableCameraScene extends Phaser.Scene { } this.updateWebCamName(); } - - private getMiddleX() : number{ - return (this.scale.width / 2) - - ( - this.enableCameraSceneElement - && this.enableCameraSceneElement.node - && this.enableCameraSceneElement.node.getBoundingClientRect().width > 0 - ? (this.enableCameraSceneElement.node.getBoundingClientRect().width / 2 / this.scale.zoom) - : (300 / RESOLUTION) - ); - } } diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts index 31045480..435592f2 100644 --- a/front/src/Phaser/Login/LoginScene.ts +++ b/front/src/Phaser/Login/LoginScene.ts @@ -4,7 +4,6 @@ import {ResizableScene} from "./ResizableScene"; import { localUserStore } from "../../Connexion/LocalUserStore"; import {MenuScene} from "../Menu/MenuScene"; import { isUserNameValid } from "../../Connexion/LocalUser"; -import { RESOLUTION } from "../../Enum/EnvironmentVariable"; export const LoginSceneName = "LoginScene"; @@ -27,8 +26,8 @@ export class LoginScene extends ResizableScene { } create() { - const middleX = this.getMiddleX(); - this.loginSceneElement = this.add.dom(middleX, 0).createFromCache(loginSceneKey); + this.loginSceneElement = this.add.dom(-1000, 0).createFromCache(loginSceneKey); + this.centerXDomElement(this.loginSceneElement, 200); MenuScene.revealMenusAfterInit(this.loginSceneElement, loginSceneKey); const pErrorElement = this.loginSceneElement.getChildByID('errorLoginScene') as HTMLInputElement; @@ -78,27 +77,10 @@ export class LoginScene extends ResizableScene { } update(time: number, delta: number): void { - const middleX = this.getMiddleX(); - this.tweens.add({ - targets: this.loginSceneElement, - x: middleX, - duration: 1000, - ease: 'Power3' - }); + } public onResize(ev: UIEvent): void { - const middleX = this.getMiddleX(); - this.tweens.add({ - targets: this.loginSceneElement, - x: middleX, - duration: 1000, - ease: 'Power3' - }); - } - - private getMiddleX() : number{ - const middleX = ((window.innerWidth) - ((this.loginSceneElement && this.loginSceneElement.width > 0 ? this.loginSceneElement.width : 200 /*FIXME to use a const will be injected in HTMLElement*/)*2)) / 2; - return (middleX > 0 ? (middleX / 2) : 0); + this.centerXDomElement(this.loginSceneElement, 200); } } diff --git a/front/src/Phaser/Login/ResizableScene.ts b/front/src/Phaser/Login/ResizableScene.ts index 82123e2b..39e2d74b 100644 --- a/front/src/Phaser/Login/ResizableScene.ts +++ b/front/src/Phaser/Login/ResizableScene.ts @@ -1,5 +1,23 @@ import {Scene} from "phaser"; +import DOMElement = Phaser.GameObjects.DOMElement; export abstract class ResizableScene extends Scene { public abstract onResize(ev: UIEvent): void; + + /** + * Centers the DOM element on the X axis. + * + * @param object + * @param defaultWidth The width of the DOM element. We try to compute it but it may not be available if called from "create". + */ + public centerXDomElement(object: DOMElement, defaultWidth: number): void { + object.x = (this.scale.width / 2) - + ( + object + && object.node + && object.node.getBoundingClientRect().width > 0 + ? (object.node.getBoundingClientRect().width / 2 / this.scale.zoom) + : (300 / this.scale.zoom) + ); + } } diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index daa35028..0366dde5 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -52,8 +52,8 @@ export class SelectCharacterScene extends AbstractCharacterScene { create() { - const middleX = this.getMiddleX(); - this.selectCharacterSceneElement = this.add.dom(middleX, 0).createFromCache(selectCharacterKey); + this.selectCharacterSceneElement = this.add.dom(-1000, 0).createFromCache(selectCharacterKey); + this.centerXDomElement(this.selectCharacterSceneElement, 150); MenuScene.revealMenusAfterInit(this.selectCharacterSceneElement, selectCharacterKey); this.selectCharacterSceneElement.addListener('click'); @@ -240,36 +240,12 @@ export class SelectCharacterScene extends AbstractCharacterScene { } update(time: number, delta: number): void { - const middleX = this.getMiddleX(); - this.tweens.add({ - targets: this.selectCharacterSceneElement, - x: middleX, - duration: 1000, - ease: 'Power3' - }); } public onResize(ev: UIEvent): void { //move position of user this.moveUser(); - const middleX = this.getMiddleX(); - this.tweens.add({ - targets: this.selectCharacterSceneElement, - x: middleX, - duration: 1000, - ease: 'Power3' - }); - } - - protected getMiddleX() : number{ - return (this.game.renderer.width / 2) - - ( - this.selectCharacterSceneElement - && this.selectCharacterSceneElement.node - && this.selectCharacterSceneElement.node.getBoundingClientRect().width > 0 - ? (this.selectCharacterSceneElement.node.getBoundingClientRect().width / 4) - : 150 - ); + this.centerXDomElement(this.selectCharacterSceneElement, 150); } } diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts index 1191415b..4ef378d6 100644 --- a/front/src/Phaser/Login/SelectCompanionScene.ts +++ b/front/src/Phaser/Login/SelectCompanionScene.ts @@ -10,7 +10,6 @@ import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingM import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {PinchManager} from "../UserInput/PinchManager"; import { MenuScene } from "../Menu/MenuScene"; -import { RESOLUTION } from "../../Enum/EnvironmentVariable"; export const SelectCompanionSceneName = "SelectCompanionScene"; @@ -43,8 +42,8 @@ export class SelectCompanionScene extends ResizableScene { create() { - const middleX = this.getMiddleX(); - this.selectCompanionSceneElement = this.add.dom(middleX, 0).createFromCache(selectCompanionSceneKey); + this.selectCompanionSceneElement = this.add.dom(-1000, 0).createFromCache(selectCompanionSceneKey); + this.centerXDomElement(this.selectCompanionSceneElement, 150); MenuScene.revealMenusAfterInit(this.selectCompanionSceneElement, selectCompanionSceneKey); this.selectCompanionSceneElement.addListener('click'); @@ -87,13 +86,7 @@ export class SelectCompanionScene extends ResizableScene { } update(time: number, delta: number): void { - const middleX = this.getMiddleX(); - this.tweens.add({ - targets: this.selectCompanionSceneElement, - x: middleX, - duration: 1000, - ease: 'Power3' - }); + } private nextScene(): void { @@ -136,13 +129,7 @@ export class SelectCompanionScene extends ResizableScene { public onResize(ev: UIEvent): void { this.moveCompanion(); - const middleX = this.getMiddleX(); - this.tweens.add({ - targets: this.selectCompanionSceneElement, - x: middleX, - duration: 1000, - ease: 'Power3' - }); + this.centerXDomElement(this.selectCompanionSceneElement, 150); } private updateSelectedCompanion(): void { @@ -238,15 +225,4 @@ export class SelectCompanionScene extends ResizableScene { companion.setX(companionX); companion.setY(companionY); } - - private getMiddleX() : number{ - return (this.game.renderer.width / RESOLUTION) - - ( - this.selectCompanionSceneElement - && this.selectCompanionSceneElement.node - && this.selectCompanionSceneElement.node.getBoundingClientRect().width > 0 - ? (this.selectCompanionSceneElement.node.getBoundingClientRect().width / (2*RESOLUTION)) - : 150 - ); - } } diff --git a/front/src/Phaser/Menu/HelpCameraSettingsScene.ts b/front/src/Phaser/Menu/HelpCameraSettingsScene.ts index f7dd5c2a..cda6adc7 100644 --- a/front/src/Phaser/Menu/HelpCameraSettingsScene.ts +++ b/front/src/Phaser/Menu/HelpCameraSettingsScene.ts @@ -1,14 +1,14 @@ import {mediaManager} from "../../WebRtc/MediaManager"; import {HtmlUtils} from "../../WebRtc/HtmlUtils"; import {localUserStore} from "../../Connexion/LocalUserStore"; -import { RESOLUTION } from "../../Enum/EnvironmentVariable"; +import {ResizableScene} from "../Login/ResizableScene"; export const HelpCameraSettingsSceneName = 'HelpCameraSettingsScene'; const helpCameraSettings = 'helpCameraSettings'; /** * The scene that show how to permit Camera and Microphone access if there are not already allowed */ -export class HelpCameraSettingsScene extends Phaser.Scene { +export class HelpCameraSettingsScene extends ResizableScene { private helpCameraSettingsElement!: Phaser.GameObjects.DOMElement; private helpCameraSettingsOpened: boolean = false; @@ -126,23 +126,23 @@ export class HelpCameraSettingsScene extends Phaser.Scene { } private getMiddleX() : number{ - return (this.game.renderer.width / RESOLUTION) - + return (this.scale.width / 2) - ( this.helpCameraSettingsElement && this.helpCameraSettingsElement.node && this.helpCameraSettingsElement.node.getBoundingClientRect().width > 0 - ? (this.helpCameraSettingsElement.node.getBoundingClientRect().width / 4) + ? (this.helpCameraSettingsElement.node.getBoundingClientRect().width / (2 * this.scale.zoom)) : (400 / 2) ); } private getMiddleY() : number{ - const middleY = ((window.innerHeight) - ( + const middleY = ((this.scale.height) - ( (this.helpCameraSettingsElement && this.helpCameraSettingsElement.node && this.helpCameraSettingsElement.node.getBoundingClientRect().height > 0 - ? this.helpCameraSettingsElement.node.getBoundingClientRect().height : 400 /*FIXME to use a const will be injected in HTMLElement*/)*2)) / 2; - return (middleY > 0 ? middleY / RESOLUTION : 0); + ? this.helpCameraSettingsElement.node.getBoundingClientRect().height : 400 /*FIXME to use a const will be injected in HTMLElement*/)/this.scale.zoom)) / 2; + return (middleY > 0 ? middleY : 0); } } diff --git a/front/src/index.ts b/front/src/index.ts index 0bdf3aa2..72f01e9f 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, isMobile, RESOLUTION} from "./Enum/EnvironmentVariable"; +import {DEBUG_MODE, isMobile} from "./Enum/EnvironmentVariable"; import {LoginScene} from "./Phaser/Login/LoginScene"; import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene"; import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene";