From 4a10b811cc951174c9e320aab2fd291e52e1024d Mon Sep 17 00:00:00 2001 From: Abbondanzo Date: Fri, 5 Jul 2019 18:56:45 -0400 Subject: [PATCH 1/9] Update package lock --- docs/package-lock.json | 41 +++++++++++++++++++++++++++++----------- package-lock.json | 43 ++++++++++++++++++++++++++++++------------ 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 40b3f8a..a050803 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -1377,7 +1377,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -1398,12 +1399,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1418,17 +1421,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -1545,7 +1551,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -1557,6 +1564,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1571,6 +1579,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1578,12 +1587,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -1602,6 +1613,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -1682,7 +1694,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -1694,6 +1707,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -1779,7 +1793,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -1815,6 +1830,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1834,6 +1850,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1877,12 +1894,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/package-lock.json b/package-lock.json index 1194412..04baf6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "replay-viewer", - "version": "0.2.8", + "version": "0.4.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2898,7 +2898,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2919,12 +2920,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2939,17 +2942,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3066,7 +3072,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3078,6 +3085,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3092,6 +3100,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3099,12 +3108,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3123,6 +3134,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3203,7 +3215,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3215,6 +3228,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3300,7 +3314,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3336,6 +3351,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3355,6 +3371,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3398,12 +3415,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, From 4118c32c6738f947756c377484fe2b01ebf76b5b Mon Sep 17 00:00:00 2001 From: Abbondanzo Date: Fri, 5 Jul 2019 18:57:00 -0400 Subject: [PATCH 2/9] Add free camera to valid cameras --- src/builders/field/addCameras.ts | 7 +++++++ src/constants/gameObjectNames.ts | 1 + 2 files changed, 8 insertions(+) diff --git a/src/builders/field/addCameras.ts b/src/builders/field/addCameras.ts index 9c247e8..7a75e9e 100644 --- a/src/builders/field/addCameras.ts +++ b/src/builders/field/addCameras.ts @@ -4,6 +4,7 @@ import { DEFAULT_CAMERA_OPTIONS } from "../../constants/defaultCameraOptions" import { ABOVE_FIELD_CAMERA, BLUE_GOAL_CAMERA, + FREE_CAMERA, ORANGE_GOAL_CAMERA, ORTHOGRAPHIC, } from "../../constants/gameObjectNames" @@ -24,6 +25,11 @@ export const addCameras = (scene: Scene) => { aboveFieldCamera.position.set(0, 2000, 0) scene.add(aboveFieldCamera) + const freeCamera = new PerspectiveCamera(...DEFAULT_CAMERA_OPTIONS) + freeCamera.name = FREE_CAMERA + freeCamera.position.set(0, 1000, 0) + scene.add(freeCamera) + const generateOrthographicCamera = () => { const camera = new OrthographicCamera(-320, 320, 240, -240, 0.1, 20000) camera.zoom = 0.05 @@ -68,6 +74,7 @@ export const addCameras = (scene: Scene) => { blueGoalCamera, orangeGoalCamera, aboveFieldCamera, + freeCamera, ...orthographicCameras, ] } diff --git a/src/constants/gameObjectNames.ts b/src/constants/gameObjectNames.ts index 9554daf..e86a66f 100644 --- a/src/constants/gameObjectNames.ts +++ b/src/constants/gameObjectNames.ts @@ -6,6 +6,7 @@ export const GROUP_SUFFIX = "-group" export const BLUE_GOAL_CAMERA = "Blue Goal Camera" export const ORANGE_GOAL_CAMERA = "Orange Goal Camera" export const ABOVE_FIELD_CAMERA = "Above Field Camera" +export const FREE_CAMERA = "Free Camera" export const ORTHOGRAPHIC = { ABOVE_FIELD: "ORTHOGRAPHIC_ABOVE_FIELD", BLUE_LEFT: "ORTHOGRAPHIC_BLUE_LEFT", From 633f16200107b1806102a752b6ac4f1e17726b03 Mon Sep 17 00:00:00 2001 From: Abbondanzo Date: Fri, 5 Jul 2019 18:57:11 -0400 Subject: [PATCH 3/9] Support switching to free camera --- src/managers/CameraManager.ts | 49 ++++++++++++------- src/operators/isOrthographicCamera.ts | 4 ++ src/viewer/components/FieldCameraControls.tsx | 2 + 3 files changed, 36 insertions(+), 19 deletions(-) create mode 100644 src/operators/isOrthographicCamera.ts diff --git a/src/managers/CameraManager.ts b/src/managers/CameraManager.ts index dc2c1db..c03e162 100644 --- a/src/managers/CameraManager.ts +++ b/src/managers/CameraManager.ts @@ -3,6 +3,7 @@ import { Camera, OrthographicCamera, PerspectiveCamera } from "three" import { ABOVE_FIELD_CAMERA, BLUE_GOAL_CAMERA, + FREE_CAMERA, ORANGE_GOAL_CAMERA, ORTHOGRAPHIC, } from "../constants/gameObjectNames" @@ -14,14 +15,9 @@ import { removeCanvasResizeListener, } from "../eventbus/events/canvasResize" import { addFrameListener, removeFrameListener } from "../eventbus/events/frame" +import { isOrthographicCamera } from "../operators/isOrthographicCamera" import SceneManager from "./SceneManager" -const ORTHOGRAPHIC_CAMERA_NAMES: string[] = Object.keys(ORTHOGRAPHIC).map( - (key: string) => { - return (ORTHOGRAPHIC as any)[key] as string - } -) - class CameraManager { activeCamera: Camera @@ -32,7 +28,7 @@ class CameraManager { private constructor() { this.activeCamera = SceneManager.getInstance().field.getCamera( ORANGE_GOAL_CAMERA - ) as any + )! this.defaultCamera = this.activeCamera this.width = 640 this.height = 480 @@ -58,7 +54,7 @@ class CameraManager { isUsingBoost: false, }) - if (!ORTHOGRAPHIC_CAMERA_NAMES.includes(this.activeCamera.name)) { + if (!isOrthographicCamera(this.activeCamera)) { this.activeCamera.lookAt(position) } } @@ -73,30 +69,41 @@ class CameraManager { } else if (fieldLocation) { switch (fieldLocation) { case "orange": - this.setActiveCamera(field.getCamera(ORANGE_GOAL_CAMERA) as any) + this.setActiveCamera(field.getCamera(ORANGE_GOAL_CAMERA)) break case "blue": - this.setActiveCamera(field.getCamera(BLUE_GOAL_CAMERA) as any) + this.setActiveCamera(field.getCamera(BLUE_GOAL_CAMERA)) break case "center": - this.setActiveCamera(field.getCamera(ABOVE_FIELD_CAMERA) as any) + this.setActiveCamera(field.getCamera(ABOVE_FIELD_CAMERA)) + break + case "freecam": + const freecam = field.getCamera(FREE_CAMERA) as PerspectiveCamera + if (!isOrthographicCamera(this.activeCamera)) { + if (freecam.parent) { + freecam.parent.updateMatrixWorld() + } + freecam.position.setFromMatrixPosition( + this.activeCamera.matrixWorld + ) + freecam.rotation.fromArray(this.activeCamera.rotation.toArray()) + } + this.setActiveCamera(freecam) break case "orthographic-above-field": - this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.ABOVE_FIELD) as any) + this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.ABOVE_FIELD)) break case "orthographic-orange-left": - this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.ORANGE_LEFT) as any) + this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.ORANGE_LEFT)) break case "orthographic-orange-right": - this.setActiveCamera(field.getCamera( - ORTHOGRAPHIC.ORANGE_RIGHT - ) as any) + this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.ORANGE_RIGHT)) break case "orthographic-blue-left": - this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.BLUE_LEFT) as any) + this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.BLUE_LEFT)) break case "orthographic-blue-right": - this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.BLUE_RIGHT) as any) + this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.BLUE_RIGHT)) break default: this.setActiveCamera(this.defaultCamera) @@ -141,7 +148,10 @@ class CameraManager { } } - private setActiveCamera(camera: Camera) { + private setActiveCamera(camera?: Camera) { + if (!camera) { + return + } this.activeCamera = camera this.updateCameraSize() this.update() @@ -179,6 +189,7 @@ export interface CameraLocationOptions { | "orange" | "blue" | "center" + | "freecam" | "orthographic-blue-right" | "orthographic-blue-left" | "orthographic-orange-right" diff --git a/src/operators/isOrthographicCamera.ts b/src/operators/isOrthographicCamera.ts new file mode 100644 index 0000000..3b63ff2 --- /dev/null +++ b/src/operators/isOrthographicCamera.ts @@ -0,0 +1,4 @@ +import { Camera, OrthographicCamera } from "three" + +export const isOrthographicCamera = (camera: Camera) => + !!(camera as OrthographicCamera).isOrthographicCamera diff --git a/src/viewer/components/FieldCameraControls.tsx b/src/viewer/components/FieldCameraControls.tsx index 33a5b2f..6e44f9a 100644 --- a/src/viewer/components/FieldCameraControls.tsx +++ b/src/viewer/components/FieldCameraControls.tsx @@ -14,11 +14,13 @@ const options: CameraLocationOptions["fieldLocation"][] = [ "blue", "orange", "center", + "freecam", ] const optionNames = { blue: "Blue Goal", orange: "Orange Goal", center: "Above Field", + freecam: "Free Cam", } const orthographicOptions: CameraLocationOptions["fieldLocation"][] = [ "orthographic-above-field", From f2bb7da48437ee241f8a8450a251f4e67c1d6f64 Mon Sep 17 00:00:00 2001 From: Abbondanzo Date: Fri, 5 Jul 2019 19:39:38 -0400 Subject: [PATCH 4/9] Add key manager and key event dispatching --- src/builders/GameBuilder.ts | 2 + src/constants/eventNames.ts | 1 + src/eventbus/events/keyControl.ts | 16 +++++ src/managers/GameManager.ts | 2 + src/managers/KeyManager.ts | 104 ++++++++++++++++++++++++++++++ 5 files changed, 125 insertions(+) create mode 100644 src/eventbus/events/keyControl.ts create mode 100644 src/managers/KeyManager.ts diff --git a/src/builders/GameBuilder.ts b/src/builders/GameBuilder.ts index 37a3b7e..63ddc29 100644 --- a/src/builders/GameBuilder.ts +++ b/src/builders/GameBuilder.ts @@ -3,6 +3,7 @@ import { LoadingManager } from "three" import CameraManager from "../managers/CameraManager" import DataManager from "../managers/DataManager" import { GameManager } from "../managers/GameManager" +import KeyManager from "../managers/KeyManager" import { ReplayData } from "../models/ReplayData" import { ReplayMetadata } from "../models/ReplayMetadata" import FPSClock from "../utils/FPSClock" @@ -31,6 +32,7 @@ const defaultGameBuilder = async ({ defaultAnimationBuilder(replayData, sceneManager.players, sceneManager.ball) DataManager.init({ replayData, replayMetadata }) CameraManager.init() + KeyManager.init() return GameManager.init({ clock, diff --git a/src/constants/eventNames.ts b/src/constants/eventNames.ts index 250251f..d0cbfd2 100644 --- a/src/constants/eventNames.ts +++ b/src/constants/eventNames.ts @@ -3,3 +3,4 @@ export const CAMERA_FRAME_UPDATE = "CAMERA_FRAME_UPDATE" export const PLAY_PAUSE = "PLAY_PAUSE" export const FRAME = "FRAME" export const CANVAS_RESIZE = "CANVAS_RESIZE" +export const KEY_CONTROL = "KEY_CONTROL" diff --git a/src/eventbus/events/keyControl.ts b/src/eventbus/events/keyControl.ts new file mode 100644 index 0000000..565f39e --- /dev/null +++ b/src/eventbus/events/keyControl.ts @@ -0,0 +1,16 @@ +import { KEY_CONTROL } from "../../constants/eventNames" +import EventBus from "../EventBus" + +/** + * Fires when the keys are pressed in a certain manner. + */ +export interface KeyControlEvent { + direction: "forward" | "backward" | "left" | "right" | "up" | "down" + speed: boolean +} + +export const { + addEventListener: addKeyControlListener, + removeEventListener: removeKeyControlListener, + dispatch: dispatchKeyControlEvent, +} = EventBus.buildEvent(KEY_CONTROL) diff --git a/src/managers/GameManager.ts b/src/managers/GameManager.ts index 5dd508c..f081ee8 100644 --- a/src/managers/GameManager.ts +++ b/src/managers/GameManager.ts @@ -24,6 +24,7 @@ import { import FPSClock from "../utils/FPSClock" import AnimationManager from "./AnimationManager" import CameraManager from "./CameraManager" +import KeyManager from "./KeyManager" import SceneManager from "./SceneManager" interface GameManagerOptions { @@ -100,6 +101,7 @@ export class GameManager { // Destruct other managers SceneManager.destruct() CameraManager.destruct() + KeyManager.destruct() // Handle destruction of the existing game const { instance } = GameManager diff --git a/src/managers/KeyManager.ts b/src/managers/KeyManager.ts new file mode 100644 index 0000000..efd880c --- /dev/null +++ b/src/managers/KeyManager.ts @@ -0,0 +1,104 @@ +import { FREE_CAMERA } from "../constants/gameObjectNames" +import { + addCameraChangeListener, + CameraChangeEvent, + removeCameraChangeListener, +} from "../eventbus/events/cameraChange" +import { + dispatchKeyControlEvent, + KeyControlEvent, +} from "../eventbus/events/keyControl" + +class KeyManager { + private listening: boolean + + private constructor() { + this.listening = false + + addCameraChangeListener(this.onCameraChange) + } + + onCameraChange = ({ camera }: CameraChangeEvent) => { + const isFreeCam = camera.name === FREE_CAMERA + if (isFreeCam !== this.listening) { + this.toggleKeyListener() + } + } + + toggleKeyListener() { + this.listening = !this.listening + if (this.listening) { + document.addEventListener("keydown", this.onKeyEvent) + } else { + document.removeEventListener("keydown", this.onKeyEvent) + } + + return this.listening + } + + onKeyEvent = ({ key, shiftKey, ctrlKey }: KeyboardEvent) => { + let direction: KeyControlEvent["direction"] | undefined + switch (key.toLowerCase()) { + case "a": + case "arrowleft": + direction = "left" + break + case "s": + case "arrowdown": + direction = "backward" + break + case "w": + case "arrowup": + direction = "forward" + break + case "d": + case "arrowright": + direction = "right" + break + case " ": + direction = "up" + break + case "escape": + if (this.listening) { + this.toggleKeyListener() + } + break + default: + if (ctrlKey) { + direction = "down" + } + } + if (direction) { + dispatchKeyControlEvent({ + direction, + speed: shiftKey, + }) + } + } + + /** + * ======================================== + * Managers are singletons + * ======================================== + */ + private static instance?: KeyManager + static getInstance() { + if (!KeyManager.instance) { + throw new Error("KeyManager not initialized with call to `init`") + } + return KeyManager.instance + } + static init() { + KeyManager.instance = new KeyManager() + return KeyManager.instance + } + static destruct() { + const { instance } = KeyManager + if (instance) { + removeCameraChangeListener(instance.onCameraChange) + KeyManager.instance = undefined + } + } +} + +export default KeyManager From 53f55010c4f21f99b962f75474ad74a6044423b2 Mon Sep 17 00:00:00 2001 From: Abbondanzo Date: Sat, 6 Jul 2019 02:30:02 -0400 Subject: [PATCH 5/9] Add working keycode dispatches that cancel after 2s --- src/eventbus/events/keyControl.ts | 45 +++++++++++- src/managers/CameraManager.ts | 50 ++++++++++++- src/managers/KeyManager.ts | 115 ++++++++++++++++++++---------- 3 files changed, 171 insertions(+), 39 deletions(-) diff --git a/src/eventbus/events/keyControl.ts b/src/eventbus/events/keyControl.ts index 565f39e..20749f0 100644 --- a/src/eventbus/events/keyControl.ts +++ b/src/eventbus/events/keyControl.ts @@ -1,11 +1,15 @@ +import { Vector3 } from "three" + import { KEY_CONTROL } from "../../constants/eventNames" import EventBus from "../EventBus" +type Direction = "forward" | "backward" | "left" | "right" | "up" | "down" + /** * Fires when the keys are pressed in a certain manner. */ export interface KeyControlEvent { - direction: "forward" | "backward" | "left" | "right" | "up" | "down" + directions: Direction[] speed: boolean } @@ -14,3 +18,42 @@ export const { removeEventListener: removeKeyControlListener, dispatch: dispatchKeyControlEvent, } = EventBus.buildEvent(KEY_CONTROL) + +export const applyDirections = ( + cameraDirection: Vector3, + directions: Direction[], + multiplier: number +) => { + const newVector = new Vector3() + directions.forEach(direction => { + const localCameraDirection = new Vector3() + localCameraDirection.copy(cameraDirection) + switch (direction) { + case "forward": + newVector.add(localCameraDirection.multiplyScalar(multiplier)) + break + case "backward": + newVector.sub(localCameraDirection.multiplyScalar(multiplier)) + break + case "up": + newVector.add(new Vector3(0, multiplier, 0)) + break + case "down": + newVector.add(new Vector3(0, -multiplier, 0)) + break + case "left": + localCameraDirection.setY(0) + localCameraDirection.setZ(-cameraDirection.x) + localCameraDirection.setX(cameraDirection.z) + newVector.add(localCameraDirection.multiplyScalar(multiplier)) + break + case "right": + localCameraDirection.setY(0) + localCameraDirection.setZ(cameraDirection.x) + localCameraDirection.setX(-cameraDirection.z) + newVector.add(localCameraDirection.multiplyScalar(multiplier)) + break + } + }) + return newVector +} diff --git a/src/managers/CameraManager.ts b/src/managers/CameraManager.ts index c03e162..8d33583 100644 --- a/src/managers/CameraManager.ts +++ b/src/managers/CameraManager.ts @@ -1,4 +1,4 @@ -import { Camera, OrthographicCamera, PerspectiveCamera } from "three" +import { Camera, OrthographicCamera, PerspectiveCamera, Vector3 } from "three" import { ABOVE_FIELD_CAMERA, @@ -15,6 +15,12 @@ import { removeCanvasResizeListener, } from "../eventbus/events/canvasResize" import { addFrameListener, removeFrameListener } from "../eventbus/events/frame" +import { + addKeyControlListener, + applyDirections, + KeyControlEvent, + removeKeyControlListener, +} from "../eventbus/events/keyControl" import { isOrthographicCamera } from "../operators/isOrthographicCamera" import SceneManager from "./SceneManager" @@ -24,6 +30,7 @@ class CameraManager { private readonly defaultCamera: Camera private width: number private height: number + private ballCam: boolean private constructor() { this.activeCamera = SceneManager.getInstance().field.getCamera( @@ -32,12 +39,19 @@ class CameraManager { this.defaultCamera = this.activeCamera this.width = 640 this.height = 480 + this.ballCam = true this.activeCamera.position.z = 5000 this.activeCamera.position.y = 750 addFrameListener(this.update) addCanvasResizeListener(this.updateSize) + addKeyControlListener(this.onKeyControl) + } + + toggleBallCam() { + this.ballCam = !this.ballCam + this.update() } private readonly updateSize = ({ width, height }: CanvasResizeEvent) => { @@ -50,7 +64,7 @@ class CameraManager { const { position } = SceneManager.getInstance().ball.ball dispatchCameraFrameUpdate({ ballPosition: position, - ballCam: true, + ballCam: this.ballCam, isUsingBoost: false, }) @@ -114,6 +128,37 @@ class CameraManager { dispatchCameraChange({ camera: this.activeCamera }) } + private readonly onKeyControl = ({ directions, speed }: KeyControlEvent) => { + if (this.activeCamera.name === FREE_CAMERA) { + const cameraDirection = new Vector3() + this.activeCamera.getWorldDirection(cameraDirection) + const multiplier = speed ? 75 : 15 + const newDirection = applyDirections( + cameraDirection, + directions, + multiplier + ) + const newPosition = new Vector3() + newPosition.copy(this.activeCamera.position) + newPosition.add(newDirection) + + // Don't allow phasing inside of the ball + const { position: ballPosition } = SceneManager.getInstance().ball.ball + if (ballPosition.distanceTo(newPosition) < 200) { + return + } + // Don't allow cameras under the field + if (newPosition.y < 10) { + newPosition.setY(10) + } + + // If all checks pass, set the new position + this.activeCamera.position.copy(newPosition) + this.update() + dispatchCameraChange({ camera: this.activeCamera }) + } + } + private updateCameraSize() { const { activeCamera: camera, width, height } = this @@ -178,6 +223,7 @@ class CameraManager { if (instance) { removeFrameListener(instance.update) removeCanvasResizeListener(instance.updateSize) + removeKeyControlListener(instance.onKeyControl) CameraManager.instance = undefined } } diff --git a/src/managers/KeyManager.ts b/src/managers/KeyManager.ts index efd880c..af23aee 100644 --- a/src/managers/KeyManager.ts +++ b/src/managers/KeyManager.ts @@ -11,9 +11,14 @@ import { class KeyManager { private listening: boolean + private keysPressed: number[] + private interval?: number + private lastKeyPress: number private constructor() { this.listening = false + this.keysPressed = [] + this.lastKeyPress = 0 addCameraChangeListener(this.onCameraChange) } @@ -28,50 +33,88 @@ class KeyManager { toggleKeyListener() { this.listening = !this.listening if (this.listening) { - document.addEventListener("keydown", this.onKeyEvent) + document.addEventListener("keyup", this.onKeyUpEvent) + document.addEventListener("keydown", this.onKeyDownEvent) + // If we don't remove these keys on blur/focus, they get "stuck" when refocusing the document + document.addEventListener("focus", this.resetKeyCodes) + document.addEventListener("blur", this.resetKeyCodes) + this.interval = setInterval(this.sendDispatch, 1000 / 30) as any } else { - document.removeEventListener("keydown", this.onKeyEvent) + document.removeEventListener("keyup", this.onKeyUpEvent) + document.removeEventListener("keydown", this.onKeyDownEvent) + document.removeEventListener("focus", this.resetKeyCodes) + document.removeEventListener("blur", this.resetKeyCodes) + if (this.interval) { + clearInterval(this.interval) + } } return this.listening } - onKeyEvent = ({ key, shiftKey, ctrlKey }: KeyboardEvent) => { - let direction: KeyControlEvent["direction"] | undefined - switch (key.toLowerCase()) { - case "a": - case "arrowleft": - direction = "left" - break - case "s": - case "arrowdown": - direction = "backward" - break - case "w": - case "arrowup": - direction = "forward" - break - case "d": - case "arrowright": - direction = "right" - break - case " ": - direction = "up" - break - case "escape": - if (this.listening) { - this.toggleKeyListener() - } - break - default: - if (ctrlKey) { - direction = "down" - } + private readonly onKeyUpEvent = ({ keyCode }: KeyboardEvent) => { + this.keysPressed = this.keysPressed.filter(code => keyCode !== code) + } + + private readonly onKeyDownEvent = ({ keyCode }: KeyboardEvent) => { + // Kill listeners on escape key + if (keyCode === 27 && this.listening) { + this.toggleKeyListener() + } + + this.lastKeyPress = performance.now() + + if (!this.keysPressed.includes(keyCode)) { + this.keysPressed.push(keyCode) } - if (direction) { + } + + private readonly resetKeyCodes = () => { + this.keysPressed = [] + } + + private readonly sendDispatch = () => { + // The last key press was detected 2000ms ago, shut off dispatch + if (performance.now() - this.lastKeyPress > 2000) { + this.keysPressed = [] + return + } + + if (this.keysPressed.length) { + const directions: KeyControlEvent["directions"] = [] + let speed = false + this.keysPressed.forEach(keyCode => { + switch (keyCode) { + case 17: // Control + directions.push("down") + break + case 37: // Left arrow + case 65: // A + directions.push("left") + break + case 38: // Up arrow + case 87: // W + directions.push("forward") + break + case 39: // Right arrow + case 68: // D + directions.push("right") + break + case 40: // Down arrow + case 83: // S + directions.push("backward") + break + case 32: // Space + directions.push("up") + break + case 16: // Shift + speed = true + break + } + }) dispatchKeyControlEvent({ - direction, - speed: shiftKey, + directions, + speed, }) } } From 86a00d903821e3ba5310f522386045987a0a1ab3 Mon Sep 17 00:00:00 2001 From: Abbondanzo Date: Sat, 6 Jul 2019 03:41:53 -0400 Subject: [PATCH 6/9] Take the cross product to determine left right rotation --- src/eventbus/events/keyControl.ts | 8 ++------ src/managers/KeyManager.ts | 2 ++ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/eventbus/events/keyControl.ts b/src/eventbus/events/keyControl.ts index 20749f0..f9f38db 100644 --- a/src/eventbus/events/keyControl.ts +++ b/src/eventbus/events/keyControl.ts @@ -42,15 +42,11 @@ export const applyDirections = ( newVector.add(new Vector3(0, -multiplier, 0)) break case "left": - localCameraDirection.setY(0) - localCameraDirection.setZ(-cameraDirection.x) - localCameraDirection.setX(cameraDirection.z) + localCameraDirection.cross(new Vector3(0, -1, 0)) newVector.add(localCameraDirection.multiplyScalar(multiplier)) break case "right": - localCameraDirection.setY(0) - localCameraDirection.setZ(cameraDirection.x) - localCameraDirection.setX(-cameraDirection.z) + localCameraDirection.cross(new Vector3(0, 1, 0)) newVector.add(localCameraDirection.multiplyScalar(multiplier)) break } diff --git a/src/managers/KeyManager.ts b/src/managers/KeyManager.ts index af23aee..d2979bd 100644 --- a/src/managers/KeyManager.ts +++ b/src/managers/KeyManager.ts @@ -75,6 +75,8 @@ class KeyManager { private readonly sendDispatch = () => { // The last key press was detected 2000ms ago, shut off dispatch + // TODO: This will shut off dispatches if you press a new key and then release the new key + // without keyup on the first. Should fix for edge case. if (performance.now() - this.lastKeyPress > 2000) { this.keysPressed = [] return From efb55de1b9a5380e2404a7ae81e5a01927dc4e4d Mon Sep 17 00:00:00 2001 From: Abbondanzo Date: Sat, 12 Oct 2019 13:09:54 -0400 Subject: [PATCH 7/9] Update package lock --- docs/package-lock.json | 41 +++++++++++----------------------------- package-lock.json | 43 ++++++++++++------------------------------ 2 files changed, 23 insertions(+), 61 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index e432eb3..ea7df54 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -1470,8 +1470,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -1492,14 +1491,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1514,20 +1511,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -1644,8 +1638,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -1657,7 +1650,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1672,7 +1664,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1680,14 +1671,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -1706,7 +1695,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -1787,8 +1775,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -1800,7 +1787,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -1886,8 +1872,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -1923,7 +1908,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1943,7 +1927,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1987,14 +1970,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, diff --git a/package-lock.json b/package-lock.json index a8be9d1..c9336ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "replay-viewer", - "version": "0.5.1", + "version": "0.5.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2887,8 +2887,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -2909,14 +2908,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2931,20 +2928,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -3061,8 +3055,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -3074,7 +3067,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3089,7 +3081,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3097,14 +3088,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3123,7 +3112,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -3204,8 +3192,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3217,7 +3204,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3303,8 +3289,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -3340,7 +3325,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3360,7 +3344,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3404,14 +3387,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, From 0384f9de98886de9d17660f713a392dc685426c6 Mon Sep 17 00:00:00 2001 From: Abbondanzo Date: Sat, 12 Oct 2019 13:20:53 -0400 Subject: [PATCH 8/9] Make F press slow camera --- src/managers/KeyManager.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/managers/KeyManager.ts b/src/managers/KeyManager.ts index d2979bd..8f55df5 100644 --- a/src/managers/KeyManager.ts +++ b/src/managers/KeyManager.ts @@ -84,11 +84,11 @@ class KeyManager { if (this.keysPressed.length) { const directions: KeyControlEvent["directions"] = [] - let speed = false + let speed = true this.keysPressed.forEach(keyCode => { switch (keyCode) { - case 17: // Control - directions.push("down") + case 70: // F + speed = false break case 37: // Left arrow case 65: // A @@ -110,7 +110,8 @@ class KeyManager { directions.push("up") break case 16: // Shift - speed = true + // speed = true + directions.push("down") break } }) From a5cd50100a5cc3405a6491cbc00501748385f56f Mon Sep 17 00:00:00 2001 From: Abbondanzo Date: Sat, 12 Oct 2019 13:22:09 -0400 Subject: [PATCH 9/9] Bump patch version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a90fa8a..a525ce6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "replay-viewer", - "version": "0.5.2", + "version": "0.5.3", "description": "Rocket League replay viewer React component and tooling", "main": "./lib/index.js", "types": "./lib/index.d.ts",