281 lines
10 KiB
TypeScript
281 lines
10 KiB
TypeScript
import { touchScreenManager } from "../../Touch/TouchScreenManager";
|
|
import { MobileJoystick } from "../Components/MobileJoystick";
|
|
import { enableUserInputsStore } from "../../Stores/UserInputStore";
|
|
import type { Direction } from "phaser3-rex-plugins/plugins/virtualjoystick.js";
|
|
import type { UserInputHandlerInterface } from "../../Interfaces/UserInputHandlerInterface";
|
|
|
|
interface UserInputManagerDatum {
|
|
keyInstance: Phaser.Input.Keyboard.Key;
|
|
event: UserInputEvent;
|
|
}
|
|
|
|
export enum UserInputEvent {
|
|
MoveLeft = 1,
|
|
MoveUp,
|
|
MoveRight,
|
|
MoveDown,
|
|
SpeedUp,
|
|
Interact,
|
|
Follow,
|
|
Shout,
|
|
JoystickMove,
|
|
}
|
|
|
|
//we cannot use a map structure so we have to create a replacement
|
|
export class ActiveEventList {
|
|
private eventMap: Map<UserInputEvent, boolean> = new Map<UserInputEvent, boolean>();
|
|
|
|
get(event: UserInputEvent): boolean {
|
|
return this.eventMap.get(event) || false;
|
|
}
|
|
set(event: UserInputEvent, value: boolean): void {
|
|
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);
|
|
}
|
|
anyExcept(...exceptions: UserInputEvent[]): boolean {
|
|
const userInputEvents = Array.from(this.eventMap);
|
|
for (const event of userInputEvents) {
|
|
if (event[1] && !exceptions.includes(event[0])) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//this class is responsible for catching user inputs and listing all active user actions at every game tick events.
|
|
export class UserInputManager {
|
|
private keysCode!: UserInputManagerDatum[];
|
|
private scene: Phaser.Scene;
|
|
private isInputDisabled: boolean;
|
|
|
|
private joystick?: MobileJoystick;
|
|
private joystickEvents = new ActiveEventList();
|
|
private joystickForceThreshold = 60;
|
|
private joystickForceAccuX = 0;
|
|
private joystickForceAccuY = 0;
|
|
|
|
private userInputHandler: UserInputHandlerInterface;
|
|
|
|
constructor(scene: Phaser.Scene, userInputHandler: UserInputHandlerInterface) {
|
|
this.scene = scene;
|
|
this.userInputHandler = userInputHandler;
|
|
|
|
this.isInputDisabled = false;
|
|
this.initKeyBoardEvent();
|
|
this.bindInputEventHandlers();
|
|
if (touchScreenManager.supportTouchScreen) {
|
|
this.initVirtualJoystick();
|
|
}
|
|
|
|
enableUserInputsStore.subscribe((enable) => {
|
|
enable ? this.restoreControls() : this.disableControls();
|
|
});
|
|
}
|
|
|
|
initVirtualJoystick() {
|
|
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;
|
|
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() {
|
|
this.keysCode = [
|
|
{
|
|
event: UserInputEvent.MoveUp,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z, false),
|
|
},
|
|
{
|
|
event: UserInputEvent.MoveUp,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W, false),
|
|
},
|
|
{
|
|
event: UserInputEvent.MoveLeft,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q, false),
|
|
},
|
|
{
|
|
event: UserInputEvent.MoveLeft,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A, false),
|
|
},
|
|
{
|
|
event: UserInputEvent.MoveDown,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S, false),
|
|
},
|
|
{
|
|
event: UserInputEvent.MoveRight,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D, false),
|
|
},
|
|
|
|
{
|
|
event: UserInputEvent.MoveUp,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP, false),
|
|
},
|
|
{
|
|
event: UserInputEvent.MoveLeft,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT, false),
|
|
},
|
|
{
|
|
event: UserInputEvent.MoveDown,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN, false),
|
|
},
|
|
{
|
|
event: UserInputEvent.MoveRight,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT, false),
|
|
},
|
|
|
|
{
|
|
event: UserInputEvent.SpeedUp,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT, false),
|
|
},
|
|
|
|
{
|
|
event: UserInputEvent.Interact,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E, false),
|
|
},
|
|
{
|
|
event: UserInputEvent.Interact,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE, false),
|
|
},
|
|
{
|
|
event: UserInputEvent.Follow,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false),
|
|
},
|
|
{
|
|
event: UserInputEvent.Shout,
|
|
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false),
|
|
},
|
|
];
|
|
}
|
|
|
|
clearAllListeners() {
|
|
this.scene.input.keyboard.removeAllListeners();
|
|
}
|
|
|
|
//todo: should we also disable the joystick?
|
|
disableControls() {
|
|
this.scene.input.keyboard.removeAllKeys();
|
|
this.isInputDisabled = true;
|
|
}
|
|
|
|
restoreControls() {
|
|
this.initKeyBoardEvent();
|
|
this.isInputDisabled = false;
|
|
}
|
|
getEventListForGameTick(): ActiveEventList {
|
|
const eventsMap = new ActiveEventList();
|
|
if (this.isInputDisabled) {
|
|
return eventsMap;
|
|
}
|
|
this.joystickEvents.forEach((value, key) => {
|
|
if (value && this.joystick) {
|
|
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) => {
|
|
if (d.keyInstance.isDown) {
|
|
eventsMap.set(d.event, true);
|
|
}
|
|
});
|
|
return eventsMap;
|
|
}
|
|
|
|
addSpaceEventListner(callback: Function) {
|
|
this.scene.input.keyboard.addListener("keyup-SPACE", callback);
|
|
}
|
|
removeSpaceEventListner(callback: Function) {
|
|
this.scene.input.keyboard.removeListener("keyup-SPACE", callback);
|
|
}
|
|
|
|
destroy(): void {
|
|
this.joystick?.destroy();
|
|
}
|
|
|
|
private bindInputEventHandlers() {
|
|
this.scene.input.on(
|
|
Phaser.Input.Events.POINTER_WHEEL,
|
|
(
|
|
pointer: Phaser.Input.Pointer,
|
|
gameObjects: Phaser.GameObjects.GameObject[],
|
|
deltaX: number,
|
|
deltaY: number,
|
|
deltaZ: number
|
|
) => {
|
|
if (this.isInputDisabled) {
|
|
return;
|
|
}
|
|
this.userInputHandler.handleMouseWheelEvent(pointer, gameObjects, deltaX, deltaY, deltaZ);
|
|
}
|
|
);
|
|
|
|
this.scene.input.on(
|
|
Phaser.Input.Events.POINTER_UP,
|
|
(pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]) => {
|
|
this.joystick?.hide();
|
|
this.userInputHandler.handlePointerUpEvent(pointer, gameObjects);
|
|
}
|
|
);
|
|
|
|
this.scene.input.on(
|
|
Phaser.Input.Events.POINTER_DOWN,
|
|
(pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]) => {
|
|
if (!pointer.wasTouch) {
|
|
return;
|
|
}
|
|
this.userInputHandler.handlePointerDownEvent(pointer, gameObjects);
|
|
// Let's only display the joystick if there is one finger on the screen
|
|
if ((pointer.event as TouchEvent).touches.length === 1) {
|
|
this.joystick?.showAt(pointer.x, pointer.y);
|
|
} else {
|
|
this.joystick?.hide();
|
|
}
|
|
}
|
|
);
|
|
|
|
this.scene.input.keyboard.on("keyup-SPACE", (event: Event) => {
|
|
this.userInputHandler.handleSpaceKeyUpEvent(event);
|
|
});
|
|
}
|
|
}
|