Merge pull request #873 from workadventure-xce/feature/mobile_support
Mobile/touch support
This commit is contained in:
commit
bbc42d2986
3
front/dist/resources/html/gameMenu.html
vendored
3
front/dist/resources/html/gameMenu.html
vendored
@ -36,6 +36,9 @@
|
|||||||
<section>
|
<section>
|
||||||
<button id="editGameSettingsButton">Settings</button>
|
<button id="editGameSettingsButton">Settings</button>
|
||||||
</section>
|
</section>
|
||||||
|
<section>
|
||||||
|
<button id="toggleFullscreen">Toggle fullscreen</button>
|
||||||
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<button id="sparkButton">Create map</button>
|
<button id="sparkButton">Create map</button>
|
||||||
</section>
|
</section>
|
||||||
|
13
front/dist/resources/style/style.css
vendored
13
front/dist/resources/style/style.css
vendored
@ -151,6 +151,7 @@ video#myCamVideo{
|
|||||||
|
|
||||||
|
|
||||||
.btn-cam-action {
|
.btn-cam-action {
|
||||||
|
pointer-events: all;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
@ -186,18 +187,26 @@ video#myCamVideo{
|
|||||||
transition: 280ms;
|
transition: 280ms;
|
||||||
}
|
}
|
||||||
.btn-micro{
|
.btn-micro{
|
||||||
|
pointer-events: auto;
|
||||||
transition: all .3s;
|
transition: all .3s;
|
||||||
right: 44px;
|
right: 44px;
|
||||||
}
|
}
|
||||||
.btn-video{
|
.btn-video{
|
||||||
|
pointer-events: auto;
|
||||||
transition: all .25s;
|
transition: all .25s;
|
||||||
right: 134px;
|
right: 134px;
|
||||||
}
|
}
|
||||||
.btn-monitor{
|
.btn-monitor{
|
||||||
|
pointer-events: auto;
|
||||||
transition: all .2s;
|
transition: all .2s;
|
||||||
right: 224px;
|
right: 224px;
|
||||||
}
|
}
|
||||||
|
.btn-copy{
|
||||||
|
pointer-events: auto;
|
||||||
|
transition: all .3s;
|
||||||
|
right: 44px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
.btn-cam-action div img{
|
.btn-cam-action div img{
|
||||||
height: 22px;
|
height: 22px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
@ -502,6 +511,7 @@ input[type=range]:focus::-ms-fill-upper {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
/* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */
|
/* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,6 +547,7 @@ input[type=range]:focus::-ms-fill-upper {
|
|||||||
.sidebar {
|
.sidebar {
|
||||||
flex: 0 0 25%;
|
flex: 0 0 25%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar > div {
|
.sidebar > div {
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
"generic-type-guard": "^3.2.0",
|
"generic-type-guard": "^3.2.0",
|
||||||
"google-protobuf": "^3.13.0",
|
"google-protobuf": "^3.13.0",
|
||||||
"phaser": "3.24.1",
|
"phaser": "3.24.1",
|
||||||
|
"phaser3-rex-plugins": "^1.1.42",
|
||||||
"queue-typescript": "^1.0.1",
|
"queue-typescript": "^1.0.1",
|
||||||
"quill": "^1.3.7",
|
"quill": "^1.3.7",
|
||||||
"rxjs": "^6.6.3",
|
"rxjs": "^6.6.3",
|
||||||
|
@ -10,6 +10,7 @@ const videoQualityKey = 'videoQuality';
|
|||||||
const audioPlayerVolumeKey = 'audioVolume';
|
const audioPlayerVolumeKey = 'audioVolume';
|
||||||
const audioPlayerMuteKey = 'audioMute';
|
const audioPlayerMuteKey = 'audioMute';
|
||||||
const helpCameraSettingsShown = 'helpCameraSettingsShown';
|
const helpCameraSettingsShown = 'helpCameraSettingsShown';
|
||||||
|
const fullscreenKey = 'fullscreen';
|
||||||
|
|
||||||
class LocalUserStore {
|
class LocalUserStore {
|
||||||
saveUser(localUser: LocalUser) {
|
saveUser(localUser: LocalUser) {
|
||||||
@ -100,6 +101,13 @@ class LocalUserStore {
|
|||||||
getHelpCameraSettingsShown(): boolean {
|
getHelpCameraSettingsShown(): boolean {
|
||||||
return localStorage.getItem(helpCameraSettingsShown) === '1';
|
return localStorage.getItem(helpCameraSettingsShown) === '1';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFullscreen(value: boolean): void {
|
||||||
|
localStorage.setItem(fullscreenKey, value.toString());
|
||||||
|
}
|
||||||
|
getFullscreen(): boolean {
|
||||||
|
return localStorage.getItem(fullscreenKey) === 'true';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const localUserStore = new LocalUserStore();
|
export const localUserStore = new LocalUserStore();
|
||||||
|
@ -1,46 +1,68 @@
|
|||||||
|
|
||||||
|
const IGNORED_KEYS = new Set([
|
||||||
|
'Esc',
|
||||||
|
'Escape',
|
||||||
|
'Alt',
|
||||||
|
'Meta',
|
||||||
|
'Control',
|
||||||
|
'Ctrl',
|
||||||
|
'Space',
|
||||||
|
'Backspace'
|
||||||
|
])
|
||||||
|
|
||||||
export class TextInput extends Phaser.GameObjects.BitmapText {
|
export class TextInput extends Phaser.GameObjects.BitmapText {
|
||||||
private minUnderLineLength = 4;
|
private minUnderLineLength = 4;
|
||||||
private underLine: Phaser.GameObjects.Text;
|
private underLine: Phaser.GameObjects.Text;
|
||||||
|
private domInput = document.createElement('input');
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene, x: number, y: number, maxLength: number, text: string, onChange: (text: string) => void) {
|
constructor(scene: Phaser.Scene, x: number, y: number, maxLength: number, text: string,
|
||||||
|
onChange: (text: string) => void) {
|
||||||
super(scene, x, y, 'main_font', text, 32);
|
super(scene, x, y, 'main_font', text, 32);
|
||||||
this.setOrigin(0.5).setCenterAlign()
|
this.setOrigin(0.5).setCenterAlign();
|
||||||
this.scene.add.existing(this);
|
this.scene.add.existing(this);
|
||||||
|
|
||||||
this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), { fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'})
|
const style = {fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'};
|
||||||
this.underLine.setOrigin(0.5)
|
this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), style);
|
||||||
|
this.underLine.setOrigin(0.5);
|
||||||
|
|
||||||
|
this.domInput.maxLength = maxLength;
|
||||||
this.scene.input.keyboard.on('keydown', (event: KeyboardEvent) => {
|
this.domInput.style.opacity = "0";
|
||||||
if (event.keyCode === 8 && this.text.length > 0) {
|
if (text) {
|
||||||
this.deleteLetter();
|
this.domInput.value = text;
|
||||||
} else if ((event.keyCode === 32 || (event.keyCode >= 48 && event.keyCode <= 90)) && this.text.length < maxLength) {
|
|
||||||
this.addLetter(event.key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.domInput.addEventListener('keydown', event => {
|
||||||
|
if (IGNORED_KEYS.has(event.key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/[a-zA-Z0-9:.!&?()+-]/.exec(event.key)) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.domInput.addEventListener('input', (event) => {
|
||||||
|
if (event.defaultPrevented) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.text = this.domInput.value;
|
||||||
this.underLine.text = this.getUnderLineBody(this.text.length);
|
this.underLine.text = this.getUnderLineBody(this.text.length);
|
||||||
onChange(this.text);
|
onChange(this.text);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.body.append(this.domInput);
|
||||||
|
this.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getUnderLineBody(textLength:number): string {
|
private getUnderLineBody(textLength:number): string {
|
||||||
if (textLength < this.minUnderLineLength) textLength = this.minUnderLineLength;
|
if (textLength < this.minUnderLineLength) textLength = this.minUnderLineLength;
|
||||||
let text = '_______';
|
let text = '_______';
|
||||||
for (let i = this.minUnderLineLength; i < textLength; i++) {
|
for (let i = this.minUnderLineLength; i < textLength; i++) {
|
||||||
text += '__'
|
text += '__';
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
private deleteLetter() {
|
|
||||||
this.text = this.text.substr(0, this.text.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private addLetter(letter: string) {
|
|
||||||
this.text += letter;
|
|
||||||
}
|
|
||||||
|
|
||||||
getText(): string {
|
getText(): string {
|
||||||
return this.text;
|
return this.text;
|
||||||
}
|
}
|
||||||
@ -56,4 +78,13 @@ export class TextInput extends Phaser.GameObjects.BitmapText {
|
|||||||
this.underLine.y = y+1;
|
this.underLine.y = y+1;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.domInput.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
super.destroy();
|
||||||
|
this.domInput.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,10 @@ import {Room} from "../../Connexion/Room";
|
|||||||
import {jitsiFactory} from "../../WebRtc/JitsiFactory";
|
import {jitsiFactory} from "../../WebRtc/JitsiFactory";
|
||||||
import {urlManager} from "../../Url/UrlManager";
|
import {urlManager} from "../../Url/UrlManager";
|
||||||
import {audioManager} from "../../WebRtc/AudioManager";
|
import {audioManager} from "../../WebRtc/AudioManager";
|
||||||
|
import {IVirtualJoystick} from "../../types";
|
||||||
|
const {
|
||||||
|
default: VirtualJoystick,
|
||||||
|
} = require("phaser3-rex-plugins/plugins/virtualjoystick.js");
|
||||||
import {PresentationModeIcon} from "../Components/PresentationModeIcon";
|
import {PresentationModeIcon} from "../Components/PresentationModeIcon";
|
||||||
import {ChatModeIcon} from "../Components/ChatModeIcon";
|
import {ChatModeIcon} from "../Components/ChatModeIcon";
|
||||||
import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon";
|
import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon";
|
||||||
@ -164,6 +168,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
private messageSubscription: Subscription|null = null;
|
private messageSubscription: Subscription|null = null;
|
||||||
private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>();
|
private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>();
|
||||||
private originalMapUrl: string|undefined;
|
private originalMapUrl: string|undefined;
|
||||||
|
public virtualJoystick!: IVirtualJoystick;
|
||||||
|
|
||||||
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
|
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
|
||||||
super({
|
super({
|
||||||
@ -399,9 +404,30 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
//initialise list of other player
|
//initialise list of other player
|
||||||
this.MapPlayers = this.physics.add.group({immovable: true});
|
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
|
//create input to move
|
||||||
this.userInputManager = new UserInputManager(this);
|
|
||||||
mediaManager.setUserInputManager(this.userInputManager);
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (localUserStore.getFullscreen()) {
|
||||||
|
document.querySelector('body')?.requestFullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
//notify game manager can to create currentUser in map
|
//notify game manager can to create currentUser in map
|
||||||
this.createCurrentPlayer();
|
this.createCurrentPlayer();
|
||||||
@ -668,7 +694,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
if(openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
if(openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||||
let message = allProps.get(WEBSITE_MESSAGE_PROPERTIES);
|
let message = allProps.get(WEBSITE_MESSAGE_PROPERTIES);
|
||||||
if(message === undefined){
|
if(message === undefined){
|
||||||
message = 'Press on SPACE to open the web site';
|
message = 'Press SPACE or touch here to open web site';
|
||||||
}
|
}
|
||||||
layoutManager.addActionButton('openWebsite', message.toString(), () => {
|
layoutManager.addActionButton('openWebsite', message.toString(), () => {
|
||||||
openWebsiteFunction();
|
openWebsiteFunction();
|
||||||
@ -700,7 +726,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
if(jitsiTriggerValue && jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
if(jitsiTriggerValue && jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||||
let message = allProps.get(JITSI_MESSAGE_PROPERTIES);
|
let message = allProps.get(JITSI_MESSAGE_PROPERTIES);
|
||||||
if (message === undefined) {
|
if (message === undefined) {
|
||||||
message = 'Press on SPACE to enter in jitsi meet room';
|
message = 'Press SPACE or touch here to enter Jitsi Meet room';
|
||||||
}
|
}
|
||||||
layoutManager.addActionButton('jitsiRoom', message.toString(), () => {
|
layoutManager.addActionButton('jitsiRoom', message.toString(), () => {
|
||||||
openJitsiRoomFunction();
|
openJitsiRoomFunction();
|
||||||
|
@ -35,6 +35,13 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
|
|
||||||
private Rectangle!: Rectangle;
|
private Rectangle!: Rectangle;
|
||||||
|
|
||||||
|
private mobileTapUP!: Rectangle;
|
||||||
|
private mobileTapDOWN!: Rectangle;
|
||||||
|
private mobileTapLEFT!: Rectangle;
|
||||||
|
private mobileTapRIGHT!: Rectangle;
|
||||||
|
|
||||||
|
private mobileTapENTER!: Rectangle;
|
||||||
|
|
||||||
private logo!: Image;
|
private logo!: Image;
|
||||||
|
|
||||||
private selectedLayers: number[] = [0];
|
private selectedLayers: number[] = [0];
|
||||||
@ -70,7 +77,7 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
create() {
|
create() {
|
||||||
this.textField = new TextField(this, this.game.renderer.width / 2, 30, 'Customize your own Avatar!');
|
this.textField = new TextField(this, this.game.renderer.width / 2, 30, 'Customize your own Avatar!');
|
||||||
|
|
||||||
this.enterField = new TextField(this, this.game.renderer.width / 2, 40, 'you can start the game by pressing SPACE..');
|
this.enterField = new TextField(this, this.game.renderer.width / 2, 60, 'Start the game by pressing ENTER\n\n or touching the center rectangle');
|
||||||
|
|
||||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, CustomizeTextures.icon);
|
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, CustomizeTextures.icon);
|
||||||
this.add.existing(this.logo);
|
this.add.existing(this.logo);
|
||||||
@ -78,22 +85,88 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
|
|
||||||
this.arrowRight = new Image(this, this.game.renderer.width*0.9, this.game.renderer.height/2, CustomizeTextures.arrowRight);
|
this.arrowRight = new Image(this, this.game.renderer.width*0.9, this.game.renderer.height/2, CustomizeTextures.arrowRight);
|
||||||
this.add.existing(this.arrowRight);
|
this.add.existing(this.arrowRight);
|
||||||
|
this.mobileTapRIGHT = this.add
|
||||||
|
.rectangle(
|
||||||
|
this.game.renderer.width*0.9,
|
||||||
|
this.game.renderer.height/2,
|
||||||
|
32,
|
||||||
|
32,
|
||||||
|
)
|
||||||
|
.setInteractive()
|
||||||
|
.on("pointerdown", () => {
|
||||||
|
this.moveCursorHorizontally(1);
|
||||||
|
});
|
||||||
|
|
||||||
this.arrowLeft = new Image(this, this.game.renderer.width/9, this.game.renderer.height/2, CustomizeTextures.arrowRight);
|
this.arrowLeft = new Image(this, this.game.renderer.width/9, this.game.renderer.height/2, CustomizeTextures.arrowRight);
|
||||||
this.arrowLeft.flipX = true;
|
this.arrowLeft.flipX = true;
|
||||||
this.add.existing(this.arrowLeft);
|
this.add.existing(this.arrowLeft);
|
||||||
|
this.mobileTapLEFT = this.add
|
||||||
|
.rectangle(
|
||||||
|
this.game.renderer.width/9,
|
||||||
|
this.game.renderer.height/2,
|
||||||
|
32,
|
||||||
|
32,
|
||||||
|
)
|
||||||
|
.setInteractive()
|
||||||
|
.on("pointerdown", () => {
|
||||||
|
this.moveCursorHorizontally(-1);
|
||||||
|
});
|
||||||
|
|
||||||
this.Rectangle = this.add.rectangle(this.cameras.main.worldView.x + this.cameras.main.width / 2, this.cameras.main.worldView.y + this.cameras.main.height / 2, 32, 33)
|
this.Rectangle = this.add.rectangle(this.cameras.main.worldView.x + this.cameras.main.width / 2, this.cameras.main.worldView.y + this.cameras.main.height / 2, 32, 33)
|
||||||
this.Rectangle.setStrokeStyle(2, 0xFFFFFF);
|
this.Rectangle.setStrokeStyle(2, 0xFFFFFF);
|
||||||
this.add.existing(this.Rectangle);
|
this.add.existing(this.Rectangle);
|
||||||
|
this.mobileTapENTER = this.add
|
||||||
|
.rectangle(
|
||||||
|
this.cameras.main.worldView.x + this.cameras.main.width / 2,
|
||||||
|
this.cameras.main.worldView.y + this.cameras.main.height / 2,
|
||||||
|
32,
|
||||||
|
32,
|
||||||
|
)
|
||||||
|
.setInteractive()
|
||||||
|
.on("pointerdown", () => {
|
||||||
|
const layers: string[] = [];
|
||||||
|
let i = 0;
|
||||||
|
for (const layerItem of this.selectedLayers) {
|
||||||
|
if (layerItem !== undefined) {
|
||||||
|
layers.push(this.layers[i][layerItem].name);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameManager.setCharacterLayers(layers);
|
||||||
|
|
||||||
|
this.scene.sleep(CustomizeSceneName);
|
||||||
|
gameManager.tryResumingGame(this, EnableCameraSceneName);
|
||||||
|
});
|
||||||
|
|
||||||
this.arrowDown = new Image(this, this.game.renderer.width - 30, 100, CustomizeTextures.arrowUp);
|
this.arrowDown = new Image(this, this.game.renderer.width - 30, 100, CustomizeTextures.arrowUp);
|
||||||
this.arrowDown.flipY = true;
|
this.arrowDown.flipY = true;
|
||||||
this.add.existing(this.arrowDown);
|
this.add.existing(this.arrowDown);
|
||||||
|
this.mobileTapDOWN = this.add
|
||||||
|
.rectangle(
|
||||||
|
this.game.renderer.width - 30,
|
||||||
|
100,
|
||||||
|
32,
|
||||||
|
32,
|
||||||
|
)
|
||||||
|
.setInteractive()
|
||||||
|
.on("pointerdown", () => {
|
||||||
|
this.moveCursorVertically(1);
|
||||||
|
});
|
||||||
|
|
||||||
this.arrowUp = new Image(this, this.game.renderer.width - 30, 60, CustomizeTextures.arrowUp);
|
this.arrowUp = new Image(this, this.game.renderer.width - 30, 60, CustomizeTextures.arrowUp);
|
||||||
this.add.existing(this.arrowUp);
|
this.add.existing(this.arrowUp);
|
||||||
|
this.mobileTapUP = this.add
|
||||||
|
.rectangle(
|
||||||
|
this.game.renderer.width - 30,
|
||||||
|
60,
|
||||||
|
32,
|
||||||
|
32,
|
||||||
|
)
|
||||||
|
.setInteractive()
|
||||||
|
.on("pointerdown", () => {
|
||||||
|
this.moveCursorVertically(-1);
|
||||||
|
});
|
||||||
|
|
||||||
this.createCustomizeLayer(0, 0, 0);
|
this.createCustomizeLayer(0, 0, 0);
|
||||||
this.createCustomizeLayer(0, 0, 1);
|
this.createCustomizeLayer(0, 0, 1);
|
||||||
@ -268,7 +341,9 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
this.moveLayers();
|
this.moveLayers();
|
||||||
|
|
||||||
this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
||||||
|
this.mobileTapENTER.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
||||||
this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 2;
|
this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 2;
|
||||||
|
this.mobileTapENTER.y = this.cameras.main.worldView.y + this.cameras.main.height / 2;
|
||||||
|
|
||||||
this.textField.x = this.game.renderer.width/2;
|
this.textField.x = this.game.renderer.width/2;
|
||||||
|
|
||||||
@ -276,15 +351,25 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
this.logo.y = this.game.renderer.height - 20;
|
this.logo.y = this.game.renderer.height - 20;
|
||||||
|
|
||||||
this.arrowUp.x = this.game.renderer.width - 30;
|
this.arrowUp.x = this.game.renderer.width - 30;
|
||||||
|
this.mobileTapUP.x = this.game.renderer.width - 30;
|
||||||
this.arrowUp.y = 60;
|
this.arrowUp.y = 60;
|
||||||
|
this.mobileTapUP.y = 60;
|
||||||
|
|
||||||
this.arrowDown.x = this.game.renderer.width - 30;
|
this.arrowDown.x = this.game.renderer.width - 30;
|
||||||
|
this.mobileTapDOWN.x = this.game.renderer.width - 30;
|
||||||
this.arrowDown.y = 100;
|
this.arrowDown.y = 100;
|
||||||
|
this.mobileTapDOWN.y = 100;
|
||||||
|
|
||||||
this.arrowLeft.x = this.game.renderer.width/9;
|
this.arrowLeft.x = this.game.renderer.width/9;
|
||||||
|
this.mobileTapLEFT.x = this.game.renderer.width/9;
|
||||||
this.arrowLeft.y = this.game.renderer.height/2;
|
this.arrowLeft.y = this.game.renderer.height/2;
|
||||||
|
this.mobileTapLEFT.y = this.game.renderer.height/2;
|
||||||
|
|
||||||
this.arrowRight.x = this.game.renderer.width*0.9;
|
this.arrowRight.x = this.game.renderer.width*0.9;
|
||||||
|
this.mobileTapRIGHT.x = this.game.renderer.width*0.9;
|
||||||
this.arrowRight.y = this.game.renderer.height/2;
|
this.arrowRight.y = this.game.renderer.height/2;
|
||||||
|
this.mobileTapRIGHT.y = this.game.renderer.height/2;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {gameManager} from "../Game/GameManager";
|
import {gameManager} from "../Game/GameManager";
|
||||||
import {TextField} from "../Components/TextField";
|
import {TextField} from "../Components/TextField";
|
||||||
import Image = Phaser.GameObjects.Image;
|
import Image = Phaser.GameObjects.Image;
|
||||||
|
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||||
import {RESOLUTION} from "../../Enum/EnvironmentVariable";
|
import {RESOLUTION} from "../../Enum/EnvironmentVariable";
|
||||||
import {SoundMeter} from "../Components/SoundMeter";
|
import {SoundMeter} from "../Components/SoundMeter";
|
||||||
@ -35,6 +36,7 @@ export class EnableCameraScene extends Phaser.Scene {
|
|||||||
private microphoneNameField!: TextField;
|
private microphoneNameField!: TextField;
|
||||||
private repositionCallback!: (this: Window, ev: UIEvent) => void;
|
private repositionCallback!: (this: Window, ev: UIEvent) => void;
|
||||||
|
|
||||||
|
private mobileTapRectangle!: Rectangle;
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
key: EnableCameraSceneName
|
key: EnableCameraSceneName
|
||||||
@ -54,7 +56,19 @@ export class EnableCameraScene extends Phaser.Scene {
|
|||||||
create() {
|
create() {
|
||||||
this.textField = new TextField(this, this.game.renderer.width / 2, 20, 'Turn on your camera and microphone');
|
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, 'Press enter to start');
|
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');
|
||||||
|
// For mobile purposes - we need a big enough touchable area.
|
||||||
|
this.mobileTapRectangle = this.add
|
||||||
|
.rectangle(
|
||||||
|
this.game.renderer.width / 2,
|
||||||
|
this.game.renderer.height - 30,
|
||||||
|
200,
|
||||||
|
50,
|
||||||
|
)
|
||||||
|
.setInteractive()
|
||||||
|
.on("pointerdown", () => {
|
||||||
|
this.login();
|
||||||
|
});
|
||||||
|
|
||||||
this.cameraNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 60, '');
|
this.cameraNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 60, '');
|
||||||
|
|
||||||
@ -195,6 +209,7 @@ export class EnableCameraScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.textField.x = this.game.renderer.width / 2;
|
this.textField.x = this.game.renderer.width / 2;
|
||||||
|
this.mobileTapRectangle.x = this.game.renderer.width / 2;
|
||||||
this.cameraNameField.x = this.game.renderer.width / 2;
|
this.cameraNameField.x = this.game.renderer.width / 2;
|
||||||
this.microphoneNameField.x = this.game.renderer.width / 2;
|
this.microphoneNameField.x = this.game.renderer.width / 2;
|
||||||
this.pressReturnField.x = this.game.renderer.width / 2;
|
this.pressReturnField.x = this.game.renderer.width / 2;
|
||||||
|
@ -5,6 +5,8 @@ import Image = Phaser.GameObjects.Image;
|
|||||||
import {SelectCharacterSceneName} from "./SelectCharacterScene";
|
import {SelectCharacterSceneName} from "./SelectCharacterScene";
|
||||||
import {ResizableScene} from "./ResizableScene";
|
import {ResizableScene} from "./ResizableScene";
|
||||||
import {isUserNameValid, maxUserNameLength} from "../../Connexion/LocalUser";
|
import {isUserNameValid, maxUserNameLength} from "../../Connexion/LocalUser";
|
||||||
|
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||||
|
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||||
|
|
||||||
//todo: put this constants in a dedicated file
|
//todo: put this constants in a dedicated file
|
||||||
export const LoginSceneName = "LoginScene";
|
export const LoginSceneName = "LoginScene";
|
||||||
@ -20,6 +22,7 @@ export class LoginScene extends ResizableScene {
|
|||||||
private pressReturnField!: TextField;
|
private pressReturnField!: TextField;
|
||||||
private logo!: Image;
|
private logo!: Image;
|
||||||
private name: string = '';
|
private name: string = '';
|
||||||
|
private mobileTapRectangle!: Rectangle;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
@ -37,17 +40,36 @@ export class LoginScene extends ResizableScene {
|
|||||||
|
|
||||||
create() {
|
create() {
|
||||||
|
|
||||||
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:');
|
|
||||||
this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, maxUserNameLength, this.name,(text: string) => {
|
this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, maxUserNameLength, this.name,(text: string) => {
|
||||||
this.name = text;
|
this.name = text;
|
||||||
});
|
localUserStore.setName(text);
|
||||||
|
})
|
||||||
|
.setInteractive()
|
||||||
|
.on('pointerdown', () => {
|
||||||
|
this.nameInput.focus();
|
||||||
|
})
|
||||||
|
|
||||||
this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 130, 'Press enter to start');
|
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:')
|
||||||
|
.setInteractive()
|
||||||
|
.on('pointerdown', () => {
|
||||||
|
this.nameInput.focus();
|
||||||
|
})
|
||||||
|
// For mobile purposes - we need a big enough touchable area.
|
||||||
|
this.mobileTapRectangle = this.add.rectangle(
|
||||||
|
this.game.renderer.width / 2,
|
||||||
|
130,
|
||||||
|
this.game.renderer.width / 2,
|
||||||
|
60,
|
||||||
|
).setInteractive()
|
||||||
|
.on('pointerdown', () => {
|
||||||
|
this.login(this.name)
|
||||||
|
})
|
||||||
|
this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 130, 'Touch here\n\n or \n\nPress enter to start')
|
||||||
|
|
||||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon);
|
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon);
|
||||||
this.add.existing(this.logo);
|
this.add.existing(this.logo);
|
||||||
|
|
||||||
const infoText = "Commands: \n - Arrows or Z,Q,S,D to move\n - SHIFT to run";
|
const infoText = "Commands: \n - Arrows or W, A, S, D to move\n - SHIFT to run";
|
||||||
this.infoTextField = new TextField(this, 10, this.game.renderer.height - 35, infoText, false);
|
this.infoTextField = new TextField(this, 10, this.game.renderer.height - 35, infoText, false);
|
||||||
|
|
||||||
this.input.keyboard.on('keyup-ENTER', () => {
|
this.input.keyboard.on('keyup-ENTER', () => {
|
||||||
@ -66,6 +88,7 @@ export class LoginScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private login(name: string): void {
|
private login(name: string): void {
|
||||||
|
if (this.name === '') return
|
||||||
gameManager.setPlayerName(name);
|
gameManager.setPlayerName(name);
|
||||||
|
|
||||||
this.scene.stop(LoginSceneName)
|
this.scene.stop(LoginSceneName)
|
||||||
@ -77,6 +100,7 @@ export class LoginScene extends ResizableScene {
|
|||||||
this.textField.x = this.game.renderer.width / 2;
|
this.textField.x = this.game.renderer.width / 2;
|
||||||
this.nameInput.setX(this.game.renderer.width / 2 - 64);
|
this.nameInput.setX(this.game.renderer.width / 2 - 64);
|
||||||
this.pressReturnField.x = this.game.renderer.width / 2;
|
this.pressReturnField.x = this.game.renderer.width / 2;
|
||||||
|
this.mobileTapRectangle.x = this.game.renderer.width / 2;
|
||||||
this.logo.x = this.game.renderer.width - 30;
|
this.logo.x = this.game.renderer.width - 30;
|
||||||
this.logo.y = this.game.renderer.height - 20;
|
this.logo.y = this.game.renderer.height - 20;
|
||||||
this.infoTextField.y = this.game.renderer.height - 35;
|
this.infoTextField.y = this.game.renderer.height - 35;
|
||||||
|
@ -36,6 +36,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
|||||||
private selectedRectangleYPos = 0; // Number of the character selected in the columns
|
private selectedRectangleYPos = 0; // Number of the character selected in the columns
|
||||||
private selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option
|
private selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option
|
||||||
private players: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
private players: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
||||||
|
private mobileTapRectangle!: Rectangle;
|
||||||
private playerModels!: BodyResourceDescriptionInterface[];
|
private playerModels!: BodyResourceDescriptionInterface[];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -69,8 +70,20 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
|||||||
this.pressReturnField = new TextField(
|
this.pressReturnField = new TextField(
|
||||||
this,
|
this,
|
||||||
this.game.renderer.width / 2,
|
this.game.renderer.width / 2,
|
||||||
90 + 32 * Math.ceil( this.playerModels.length / this.nbCharactersPerRow) + 40,
|
90 + 32 * Math.ceil( this.playerModels.length / this.nbCharactersPerRow) + 60,
|
||||||
'Press enter to start');
|
'Touch here\n\n or \n\nPress enter to start');
|
||||||
|
// For mobile purposes - we need a big enough touchable area.
|
||||||
|
this.mobileTapRectangle = this.add
|
||||||
|
.rectangle(
|
||||||
|
this.game.renderer.width / 2,
|
||||||
|
275,
|
||||||
|
this.game.renderer.width / 2,
|
||||||
|
50,
|
||||||
|
)
|
||||||
|
.setInteractive()
|
||||||
|
.on("pointerdown", () => {
|
||||||
|
this.nextScene();
|
||||||
|
});
|
||||||
|
|
||||||
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16;
|
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16;
|
||||||
|
|
||||||
@ -193,6 +206,10 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
|||||||
this.customizeButton.setInteractive().on("pointerdown", () => {
|
this.customizeButton.setInteractive().on("pointerdown", () => {
|
||||||
this.selectedRectangleYPos = Math.ceil(this.playerModels.length / this.nbCharactersPerRow);
|
this.selectedRectangleYPos = Math.ceil(this.playerModels.length / this.nbCharactersPerRow);
|
||||||
this.updateSelectedPlayer();
|
this.updateSelectedPlayer();
|
||||||
|
this.nextScene();
|
||||||
|
});
|
||||||
|
this.customizeButtonSelected.setInteractive().on("pointerdown", () => {
|
||||||
|
this.nextScene();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.selectedPlayer = this.players[0];
|
this.selectedPlayer = this.players[0];
|
||||||
|
@ -29,6 +29,8 @@ export class SelectCompanionScene extends ResizableScene {
|
|||||||
private companions: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
private companions: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
||||||
private companionModels: Array<CompanionResourceDescriptionInterface|null> = [null];
|
private companionModels: Array<CompanionResourceDescriptionInterface|null> = [null];
|
||||||
|
|
||||||
|
private confirmTouchArea!: Rectangle;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
key: SelectCompanionSceneName
|
key: SelectCompanionSceneName
|
||||||
@ -54,12 +56,17 @@ export class SelectCompanionScene extends ResizableScene {
|
|||||||
create() {
|
create() {
|
||||||
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your companion');
|
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);
|
||||||
this.pressReturnField = new TextField(
|
this.pressReturnField = new TextField(
|
||||||
this,
|
this,
|
||||||
this.game.renderer.width / 2,
|
this.game.renderer.width / 2,
|
||||||
90 + 32 * Math.ceil(this.companionModels.length / this.nbCharactersPerRow),
|
confirmTouchAreaY,
|
||||||
'Press enter to start'
|
'Touch here\n\n or \n\n press enter to start'
|
||||||
);
|
);
|
||||||
|
this.confirmTouchArea = this.add
|
||||||
|
.rectangle(this.game.renderer.width / 2, confirmTouchAreaY, 200, 50)
|
||||||
|
.setInteractive()
|
||||||
|
.on("pointerdown", this.nextScene.bind(this));
|
||||||
|
|
||||||
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16;
|
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16;
|
||||||
this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xFFFFFF);
|
this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xFFFFFF);
|
||||||
|
@ -291,6 +291,9 @@ export class MenuScene extends Phaser.Scene {
|
|||||||
case 'editGameSettingsButton':
|
case 'editGameSettingsButton':
|
||||||
this.openGameSettingsMenu();
|
this.openGameSettingsMenu();
|
||||||
break;
|
break;
|
||||||
|
case 'toggleFullscreen':
|
||||||
|
this.toggleFullscreen();
|
||||||
|
break;
|
||||||
case 'adminConsoleButton':
|
case 'adminConsoleButton':
|
||||||
gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.activeMessageConsole();
|
gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.activeMessageConsole();
|
||||||
break;
|
break;
|
||||||
@ -328,4 +331,15 @@ export class MenuScene extends Phaser.Scene {
|
|||||||
this.closeGameShare();
|
this.closeGameShare();
|
||||||
this.gameReportElement.close();
|
this.gameReportElement.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private toggleFullscreen() {
|
||||||
|
const body = document.querySelector('body')
|
||||||
|
if (body) {
|
||||||
|
if (document.fullscreenElement ?? document.fullscreen) {
|
||||||
|
document.exitFullscreen()
|
||||||
|
} else {
|
||||||
|
body.requestFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ export class Player extends Character implements CurrentGamerInterface {
|
|||||||
let x = 0;
|
let x = 0;
|
||||||
let y = 0;
|
let y = 0;
|
||||||
if (activeEvents.get(UserInputEvent.MoveUp)) {
|
if (activeEvents.get(UserInputEvent.MoveUp)) {
|
||||||
y = - moveAmount;
|
y = -moveAmount;
|
||||||
direction = PlayerAnimationDirections.Up;
|
direction = PlayerAnimationDirections.Up;
|
||||||
moving = true;
|
moving = true;
|
||||||
} else if (activeEvents.get(UserInputEvent.MoveDown)) {
|
} else if (activeEvents.get(UserInputEvent.MoveDown)) {
|
||||||
@ -64,17 +64,19 @@ export class Player extends Character implements CurrentGamerInterface {
|
|||||||
direction = PlayerAnimationDirections.Right;
|
direction = PlayerAnimationDirections.Right;
|
||||||
moving = true;
|
moving = true;
|
||||||
}
|
}
|
||||||
|
moving = moving || activeEvents.get(UserInputEvent.JoystickMove);
|
||||||
|
|
||||||
if (x !== 0 || y !== 0) {
|
if (x !== 0 || y !== 0) {
|
||||||
this.move(x, y);
|
this.move(x, y);
|
||||||
this.emit(hasMovedEventName, {moving, direction, x: this.x, y: this.y});
|
this.emit(hasMovedEventName, {moving, direction, x: this.x, y: this.y});
|
||||||
} else {
|
} else if (this.wasMoving && moving) {
|
||||||
if (this.wasMoving) {
|
// slow joystick movement
|
||||||
//direction = PlayerAnimationNames.None;
|
this.move(0, 0);
|
||||||
|
this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y});
|
||||||
|
} else if (this.wasMoving && !moving) {
|
||||||
this.stop();
|
this.stop();
|
||||||
this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y});
|
this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (direction !== null) {
|
if (direction !== null) {
|
||||||
this.previousDirection = direction;
|
this.previousDirection = direction;
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
|
import { Direction, IVirtualJoystick } from "../../types";
|
||||||
import {GameScene} from "../Game/GameScene";
|
import {GameScene} from "../Game/GameScene";
|
||||||
|
const {
|
||||||
|
default: VirtualJoystick,
|
||||||
|
} = require("phaser3-rex-plugins/plugins/virtualjoystick.js");
|
||||||
|
|
||||||
interface UserInputManagerDatum {
|
interface UserInputManagerDatum {
|
||||||
keyInstance: Phaser.Input.Keyboard.Key;
|
keyInstance: Phaser.Input.Keyboard.Key;
|
||||||
@ -13,17 +17,24 @@ export enum UserInputEvent {
|
|||||||
SpeedUp,
|
SpeedUp,
|
||||||
Interact,
|
Interact,
|
||||||
Shout,
|
Shout,
|
||||||
|
JoystickMove,
|
||||||
}
|
}
|
||||||
|
|
||||||
//we cannot the map structure so we have to create a replacment
|
//we cannot use a map structure so we have to create a replacment
|
||||||
export class ActiveEventList {
|
export class ActiveEventList {
|
||||||
private KeysCode : Map<UserInputEvent, boolean> = new Map<UserInputEvent, boolean>();
|
private eventMap : Map<UserInputEvent, boolean> = new Map<UserInputEvent, boolean>();
|
||||||
|
|
||||||
get(event: UserInputEvent): boolean {
|
get(event: UserInputEvent): boolean {
|
||||||
return this.KeysCode.get(event) || false;
|
return this.eventMap.get(event) || false;
|
||||||
}
|
}
|
||||||
set(event: UserInputEvent, value: boolean): void {
|
set(event: UserInputEvent, value: boolean): void {
|
||||||
this.KeysCode.set(event, value);
|
this.eventMap.set(event, value);
|
||||||
|
}
|
||||||
|
forEach(callback: (value: boolean, key: UserInputEvent) => void): void {
|
||||||
|
this.eventMap.forEach(callback);
|
||||||
|
}
|
||||||
|
any(): boolean {
|
||||||
|
return Array.from(this.eventMap.values()).reduce((accu, curr) => accu || curr, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,10 +43,40 @@ export class UserInputManager {
|
|||||||
private KeysCode!: UserInputManagerDatum[];
|
private KeysCode!: UserInputManagerDatum[];
|
||||||
private Scene: GameScene;
|
private Scene: GameScene;
|
||||||
private isInputDisabled : boolean;
|
private isInputDisabled : boolean;
|
||||||
constructor(Scene : GameScene) {
|
|
||||||
|
private joystick : IVirtualJoystick;
|
||||||
|
private joystickEvents = new ActiveEventList();
|
||||||
|
private joystickForceThreshold = 60;
|
||||||
|
private joystickForceAccuX = 0;
|
||||||
|
private joystickForceAccuY = 0;
|
||||||
|
|
||||||
|
constructor(Scene: GameScene, virtualJoystick: IVirtualJoystick) {
|
||||||
this.Scene = Scene;
|
this.Scene = Scene;
|
||||||
this.initKeyBoardEvent();
|
|
||||||
this.isInputDisabled = false;
|
this.isInputDisabled = false;
|
||||||
|
this.initKeyBoardEvent();
|
||||||
|
this.joystick = virtualJoystick;
|
||||||
|
this.joystick.on("update", () => {
|
||||||
|
this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0;
|
||||||
|
this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0;
|
||||||
|
const cursorKeys = this.joystick.createCursorKeys();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initKeyBoardEvent(){
|
initKeyBoardEvent(){
|
||||||
@ -78,11 +119,33 @@ export class UserInputManager {
|
|||||||
if (this.isInputDisabled) {
|
if (this.isInputDisabled) {
|
||||||
return eventsMap;
|
return eventsMap;
|
||||||
}
|
}
|
||||||
|
this.joystickEvents.forEach((value, key) => {
|
||||||
|
if (value) {
|
||||||
|
switch (key) {
|
||||||
|
case UserInputEvent.MoveUp:
|
||||||
|
case UserInputEvent.MoveDown:
|
||||||
|
this.joystickForceAccuY += this.joystick.forceY;
|
||||||
|
if (Math.abs(this.joystickForceAccuY) > this.joystickForceThreshold) {
|
||||||
|
eventsMap.set(key, value);
|
||||||
|
this.joystickForceAccuY = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UserInputEvent.MoveLeft:
|
||||||
|
case UserInputEvent.MoveRight:
|
||||||
|
this.joystickForceAccuX += this.joystick.forceX;
|
||||||
|
if (Math.abs(this.joystickForceAccuX) > this.joystickForceThreshold) {
|
||||||
|
eventsMap.set(key, value);
|
||||||
|
this.joystickForceAccuX = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
eventsMap.set(UserInputEvent.JoystickMove, this.joystickEvents.any());
|
||||||
this.KeysCode.forEach(d => {
|
this.KeysCode.forEach(d => {
|
||||||
if (d. keyInstance.isDown) {
|
if (d.keyInstance.isDown) {
|
||||||
eventsMap.set(d.event, true);
|
eventsMap.set(d.event, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
return eventsMap;
|
return eventsMap;
|
||||||
}
|
}
|
||||||
|
@ -340,14 +340,10 @@ class LayoutManager {
|
|||||||
const mainContainer = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-container');
|
const mainContainer = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-container');
|
||||||
mainContainer.appendChild(div);
|
mainContainer.appendChild(div);
|
||||||
|
|
||||||
const callBackFunctionTrigger = (() => {
|
|
||||||
console.log('user click on space => ', id);
|
|
||||||
callBack();
|
|
||||||
});
|
|
||||||
|
|
||||||
//add trigger action
|
//add trigger action
|
||||||
this.actionButtonTrigger.set(id, callBackFunctionTrigger);
|
div.onpointerdown = () => callBack();
|
||||||
userInputManager.addSpaceEventListner(callBackFunctionTrigger);
|
this.actionButtonTrigger.set(id, callBack);
|
||||||
|
userInputManager.addSpaceEventListner(callBack);
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeActionButton(id: string, userInputManager: UserInputManager){
|
public removeActionButton(id: string, userInputManager: UserInputManager){
|
||||||
|
24
front/src/types.ts
Normal file
24
front/src/types.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import Phaser from "phaser";
|
||||||
|
|
||||||
|
export type CursorKey = {
|
||||||
|
isDown: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Direction = 'left' | 'right' | 'up' | 'down'
|
||||||
|
|
||||||
|
export interface CursorKeys extends Record<Direction, CursorKey> {
|
||||||
|
left: CursorKey;
|
||||||
|
right: CursorKey;
|
||||||
|
up: CursorKey;
|
||||||
|
down: CursorKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVirtualJoystick extends Phaser.GameObjects.GameObject {
|
||||||
|
y: number;
|
||||||
|
x: number;
|
||||||
|
forceX: number;
|
||||||
|
forceY: number;
|
||||||
|
visible: boolean;
|
||||||
|
createCursorKeys: () => CursorKeys;
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@
|
|||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
|
||||||
"strict": true, /* Enable all strict type-checking options. */
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
|
@ -1819,6 +1819,11 @@ eventemitter3@^2.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
|
||||||
integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=
|
integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=
|
||||||
|
|
||||||
|
eventemitter3@^3.1.2:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
|
||||||
|
integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==
|
||||||
|
|
||||||
eventemitter3@^4.0.0, eventemitter3@^4.0.4:
|
eventemitter3@^4.0.0, eventemitter3@^4.0.4:
|
||||||
version "4.0.7"
|
version "4.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||||
@ -3064,6 +3069,11 @@ loglevel@^1.6.8:
|
|||||||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0"
|
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0"
|
||||||
integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==
|
integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==
|
||||||
|
|
||||||
|
lokijs@^1.5.11:
|
||||||
|
version "1.5.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/lokijs/-/lokijs-1.5.11.tgz#2b2ea82ec66050e4b112c6cfc588dac22d362b13"
|
||||||
|
integrity sha512-YYyuBPxMn/oS0tFznQDbIX5XL1ltMcwFqCboDr8voYE4VCDzR5vAsrvQDhlnua4lBeqMqHmLvUXRTmRUzUKH1Q==
|
||||||
|
|
||||||
lower-case@^2.0.1:
|
lower-case@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7"
|
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7"
|
||||||
@ -3625,6 +3635,11 @@ pako@~1.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||||
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||||
|
|
||||||
|
papaparse@^5.3.0:
|
||||||
|
version "5.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.3.0.tgz#ab1702feb96e79ab4309652f36db9536563ad05a"
|
||||||
|
integrity sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg==
|
||||||
|
|
||||||
parallel-transform@^1.1.0:
|
parallel-transform@^1.1.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc"
|
resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc"
|
||||||
@ -3752,6 +3767,16 @@ pbkdf2@^3.0.3:
|
|||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
sha.js "^2.4.8"
|
sha.js "^2.4.8"
|
||||||
|
|
||||||
|
phaser3-rex-plugins@^1.1.42:
|
||||||
|
version "1.1.44"
|
||||||
|
resolved "https://registry.yarnpkg.com/phaser3-rex-plugins/-/phaser3-rex-plugins-1.1.44.tgz#83588ab2801c5b3a80a18be4f0ae37f1f32096b0"
|
||||||
|
integrity sha512-JPr3+UQv4+uv4etZr80aFhYxw2dk4UYG9/gQ4uMSSXQuc8gXQkXCr3J3zm8LJqH+1FXeK8nncpC7TKa4uKLgQw==
|
||||||
|
dependencies:
|
||||||
|
eventemitter3 "^3.1.2"
|
||||||
|
lokijs "^1.5.11"
|
||||||
|
papaparse "^5.3.0"
|
||||||
|
webfontloader "^1.6.28"
|
||||||
|
|
||||||
phaser@3.24.1:
|
phaser@3.24.1:
|
||||||
version "3.24.1"
|
version "3.24.1"
|
||||||
resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.24.1.tgz#376e0c965d2a35af37c06ee78627dafbde5be017"
|
resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.24.1.tgz#376e0c965d2a35af37c06ee78627dafbde5be017"
|
||||||
@ -5144,6 +5169,11 @@ wbuf@^1.1.0, wbuf@^1.7.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimalistic-assert "^1.0.0"
|
minimalistic-assert "^1.0.0"
|
||||||
|
|
||||||
|
webfontloader@^1.6.28:
|
||||||
|
version "1.6.28"
|
||||||
|
resolved "https://registry.yarnpkg.com/webfontloader/-/webfontloader-1.6.28.tgz#db786129253cb6e8eae54c2fb05f870af6675bae"
|
||||||
|
integrity sha1-23hhKSU8tujq5UwvsF+HCvZnW64=
|
||||||
|
|
||||||
webpack-cli@^3.3.11:
|
webpack-cli@^3.3.11:
|
||||||
version "3.3.12"
|
version "3.3.12"
|
||||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a"
|
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a"
|
||||||
|
Loading…
Reference in New Issue
Block a user