From ee88ec504a9db5189361d56edccb1b268816b26f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Barth=C3=A9lemy?=
<31370477+BarthPaleologue@users.noreply.github.com>
Date: Fri, 22 Sep 2023 11:57:21 +0200
Subject: [PATCH] added pause menu
+ continued to replacce register by Observable
---
src/html/pauseMenu.html | 17 ++++++++
src/styles/index.scss | 2 +
src/styles/pauseMenu/index.scss | 63 ++++++++++++++++++++++++++++
src/ts/blackHoleDemo.ts | 1 +
src/ts/controller/inputs/mouse.ts | 16 +++----
src/ts/controller/spaceEngine.ts | 40 +++++++++++++++++-
src/ts/index.ts | 6 +--
src/ts/randomizer.ts | 3 ++
src/ts/spaceship/abstractThruster.ts | 2 +-
src/ts/starmap/starMap.ts | 12 +++++-
src/ts/ui/pauseMenu.ts | 41 ++++++++++++++++++
11 files changed, 183 insertions(+), 20 deletions(-)
create mode 100644 src/html/pauseMenu.html
create mode 100644 src/styles/pauseMenu/index.scss
create mode 100644 src/ts/ui/pauseMenu.ts
diff --git a/src/html/pauseMenu.html b/src/html/pauseMenu.html
new file mode 100644
index 000000000..0140583fd
--- /dev/null
+++ b/src/html/pauseMenu.html
@@ -0,0 +1,17 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/styles/index.scss b/src/styles/index.scss
index ffc73348e..e837e0312 100644
--- a/src/styles/index.scss
+++ b/src/styles/index.scss
@@ -16,6 +16,8 @@ body {
@import "helmetOverlay/helmetOverlay";
+ @import "pauseMenu/index.scss";
+
#renderer {
z-index: 1;
float: left;
diff --git a/src/styles/pauseMenu/index.scss b/src/styles/pauseMenu/index.scss
new file mode 100644
index 000000000..5a8ae5a95
--- /dev/null
+++ b/src/styles/pauseMenu/index.scss
@@ -0,0 +1,63 @@
+#pauseMask {
+ z-index: 9;
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ background: rgba(0, 0, 0, 0.5);
+ backdrop-filter: blur(5px);
+}
+
+#pauseMenu {
+ position: absolute;
+ width: 300px;
+ height: 300px;
+ top: calc(50% - 150px);
+ left: calc(50% - 150px);
+ background: rgba(0, 0, 0, 0.9);
+
+ z-index: 10;
+
+ display: grid;
+ grid-template-rows: 100px auto;
+
+ padding-bottom: 20px;
+
+ box-shadow: 0 0 50px rgba(255, 255, 255, 0.8);
+
+ font-family: sans-serif;
+
+ h1 {
+ color: white;
+ text-align: center;
+ }
+
+ .buttons {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-end;
+ row-gap: 10px;
+
+ .button {
+ text-align: center;
+ padding: 10px 20px;
+ background: white;
+ color: black;
+ width: 120px;
+ cursor: pointer;
+ transition: .2s;
+
+ &:hover {
+ background: darken(white, 20%);
+ color: black;
+ }
+
+ &:active {
+ background: darken(white, 40%);
+ color: black;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ts/blackHoleDemo.ts b/src/ts/blackHoleDemo.ts
index 50310777b..016a5c619 100644
--- a/src/ts/blackHoleDemo.ts
+++ b/src/ts/blackHoleDemo.ts
@@ -39,6 +39,7 @@ spaceshipController.addInput(gamepad);
scene.setActiveController(spaceshipController);
engine.registerStarSystemUpdateCallback(() => {
+ if (engine.isPaused()) return;
if (scene.getActiveController() != spaceshipController) return;
const shipPosition = spaceshipController.getTransform().getAbsolutePosition();
diff --git a/src/ts/controller/inputs/mouse.ts b/src/ts/controller/inputs/mouse.ts
index ff5738abc..e76203ebd 100644
--- a/src/ts/controller/inputs/mouse.ts
+++ b/src/ts/controller/inputs/mouse.ts
@@ -1,3 +1,4 @@
+import { Observable } from "@babylonjs/core/Misc/observable";
import { clamp } from "../../utils/math";
import { Input, InputType } from "./input";
@@ -16,8 +17,8 @@ export class Mouse implements Input {
deadAreaRadius = 100;
private canvas: HTMLCanvasElement;
- private onMouseEnterListeners: (() => void)[] = [];
- private onMouseLeaveListeners: (() => void)[] = [];
+ readonly onMouseEnterObservable: Observable = new Observable();
+ readonly onMouseLeaveObservable: Observable = new Observable();
constructor(canvas: HTMLCanvasElement, deadAreaRadius = 50) {
this.deadAreaRadius = deadAreaRadius;
@@ -35,20 +36,13 @@ export class Mouse implements Input {
});
document.addEventListener("mouseenter", () => {
- this.onMouseEnterListeners.forEach((listener) => listener());
+ this.onMouseEnterObservable.notifyObservers();
});
document.addEventListener("mouseleave", () => {
- this.onMouseLeaveListeners.forEach((listener) => listener());
+ this.onMouseLeaveObservable.notifyObservers();
});
}
- addOnMouseEnterListener(listener: () => void) {
- this.onMouseEnterListeners.push(listener);
- }
- addOnMouseLeaveListener(listener: () => void) {
- this.onMouseLeaveListeners.push(listener);
- }
-
getRoll() {
const d2 = this.dxToCenter ** 2 + this.dyToCenter ** 2;
const adaptedLength = Math.max(Math.log(d2 / this.deadAreaRadius ** 2), 0) / 3;
diff --git a/src/ts/controller/spaceEngine.ts b/src/ts/controller/spaceEngine.ts
index 646845997..fa5e22571 100644
--- a/src/ts/controller/spaceEngine.ts
+++ b/src/ts/controller/spaceEngine.ts
@@ -30,6 +30,7 @@ import { Animation } from "@babylonjs/core/Animations/animation";
import { Observable } from "@babylonjs/core/Misc/observable";
import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight";
import { OrbitRenderer } from "../view/orbitRenderer";
+import { PauseMenu } from "../ui/pauseMenu";
enum EngineState {
RUNNING,
@@ -40,6 +41,7 @@ export class SpaceEngine {
// UI
private readonly helmetOverlay: HelmetOverlay;
readonly bodyEditor: BodyEditor;
+ private readonly pauseMenu: PauseMenu;
readonly canvas: HTMLCanvasElement;
private isFullscreen = false;
private videoRecorder: VideoRecorder | null = null;
@@ -68,6 +70,15 @@ export class SpaceEngine {
constructor() {
this.helmetOverlay = new HelmetOverlay();
this.bodyEditor = new BodyEditor();
+ this.pauseMenu = new PauseMenu();
+
+ this.pauseMenu.onResume.add(() => this.resume());
+ this.pauseMenu.onScreenshot.add(() => this.takeScreenshot());
+ this.pauseMenu.onShare.add(() => {
+ const seed = this.getStarSystem().model.seed;
+ const url = new URL(`https://barthpaleologue.github.io/CosmosJourneyer/dist/random.html?seed=${seed}`);
+ navigator.clipboard.writeText(url.toString());
+ });
this.canvas = document.getElementById("renderer") as HTMLCanvasElement;
this.canvas.width = window.innerWidth;
@@ -86,11 +97,15 @@ export class SpaceEngine {
}
]);
+ window.addEventListener("blur", () => {
+ if (!this.isPaused()) this.pause();
+ });
+
//TODO: use the keyboard class
document.addEventListener("keydown", (e) => {
if (e.key === "o") OverlayPostProcess.ARE_ENABLED = !OverlayPostProcess.ARE_ENABLED;
if (e.key === "n") this.orbitRenderer.setVisibility(!this.orbitRenderer.isVisible());
- if (e.key === "p") Tools.CreateScreenshot(this.getEngine(), this.getStarSystemScene().getActiveController().getActiveCamera(), { precision: 4 });
+ if (e.key === "p") this.takeScreenshot();
if (e.key === "v") {
if (!VideoRecorder.IsSupported(this.getEngine())) console.warn("Your browser does not support video recording!");
if (this.videoRecorder === null) {
@@ -121,15 +136,36 @@ export class SpaceEngine {
// when pressing f11, the ui is hidden when the browser is in fullscreen mode
if (e.key === "F11") this.isFullscreen = !this.isFullscreen;
+
+ if (e.key === "Escape") {
+ if (this.state === EngineState.RUNNING) this.pause();
+ else this.resume();
+ }
});
}
+ takeScreenshot(): void {
+ const camera = this.getActiveScene().activeCamera;
+ if (camera === null) throw new Error("Cannot take screenshot: camera is null");
+ Tools.CreateScreenshot(this.getEngine(), camera, { precision: 4 });
+ }
+
pause(): void {
this.state = EngineState.PAUSED;
+ this.pauseMenu.setVisibility(true);
+ this.getStarSystemScene().physicsEnabled = false;
+ this.getStarMap().setRunning(false);
}
resume(): void {
this.state = EngineState.RUNNING;
+ this.pauseMenu.setVisibility(false);
+ this.getStarSystemScene().physicsEnabled = true;
+ this.getStarMap().setRunning(true);
+ }
+
+ isPaused(): boolean {
+ return this.state === EngineState.PAUSED;
}
/**
@@ -209,7 +245,7 @@ export class SpaceEngine {
});
this.starSystemScene.registerBeforeRender(() => {
- if (this.state === EngineState.PAUSED) return;
+ if (this.isPaused()) return;
const starSystemScene = this.getStarSystemScene();
const starSystem = this.getStarSystem();
diff --git a/src/ts/index.ts b/src/ts/index.ts
index c6ddcd707..f544e9098 100644
--- a/src/ts/index.ts
+++ b/src/ts/index.ts
@@ -51,16 +51,14 @@ spaceshipController.addInput(mouse);
const physicsViewer = new PhysicsViewer();
//physicsViewer.showBody(spaceshipController.aggregate.body);
-mouse.addOnMouseEnterListener(() => {
- if (scene.getActiveController() === spaceshipController) engine.resume();
-});
-mouse.addOnMouseLeaveListener(() => {
+mouse.onMouseLeaveObservable.add(() => {
if (scene.getActiveController() === spaceshipController) engine.pause();
});
scene.setActiveController(spaceshipController);
engine.registerStarSystemUpdateCallback(() => {
+ if (engine.isPaused()) return;
if (scene.getActiveController() != spaceshipController) return;
const shipPosition = spaceshipController.getTransform().getAbsolutePosition();
diff --git a/src/ts/randomizer.ts b/src/ts/randomizer.ts
index ea0b9c4c5..01d387784 100644
--- a/src/ts/randomizer.ts
+++ b/src/ts/randomizer.ts
@@ -41,6 +41,7 @@ spaceshipController.addInput(gamepad);
scene.setActiveController(spaceshipController);
engine.registerStarSystemUpdateCallback(() => {
+ if (engine.isPaused()) return;
if (scene.getActiveController() != spaceshipController) return;
const shipPosition = spaceshipController.getTransform().getAbsolutePosition();
@@ -99,3 +100,5 @@ const nbRadius = starSystem.model.getBodyTypeOfStar(0) === BODY_TYPE.BLACK_HOLE
positionNearObject(scene.getActiveController(), starSystem.planets.length > 0 ? starSystem.getBodies()[1] : starSystem.stellarObjects[0], starSystem, nbRadius);
engine.bodyEditor.setVisibility(EditorVisibility.NAVBAR);
+
+engine.toggleStarMap();
diff --git a/src/ts/spaceship/abstractThruster.ts b/src/ts/spaceship/abstractThruster.ts
index a335afc0c..fcb4284d0 100644
--- a/src/ts/spaceship/abstractThruster.ts
+++ b/src/ts/spaceship/abstractThruster.ts
@@ -1,5 +1,5 @@
import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
-import { Axis, Vector3 } from "@babylonjs/core/Maths/math";
+import { Vector3 } from "@babylonjs/core/Maths/math";
import { Color3 } from "@babylonjs/core/Maths/math.color";
import { AbstractMesh, MeshBuilder } from "@babylonjs/core/Meshes";
import { DirectionnalParticleSystem } from "../utils/particleSystem";
diff --git a/src/ts/starmap/starMap.ts b/src/ts/starmap/starMap.ts
index 536fd363b..2c6fbddf5 100644
--- a/src/ts/starmap/starMap.ts
+++ b/src/ts/starmap/starMap.ts
@@ -39,6 +39,8 @@ export class StarMap {
readonly scene: Scene;
private readonly controller: PlayerController;
+ private isRunning: boolean = true;
+
private rotationAnimation: TransformRotationAnimation | null = null;
private translationAnimation: TransformTranslationAnimation | null = null;
@@ -218,7 +220,9 @@ export class StarMap {
});
this.densityRNG = (x: number, y: number, z: number) => (1.0 - Math.abs(perlinRNG(x * 0.2, y * 0.2, z * 0.2))) ** 8;
- this.scene.registerBeforeRender(() => {
+ this.scene.onBeforeRenderObservable.add(() => {
+ if(!this.isRunning) return;
+
const deltaTime = this.scene.getEngine().getDeltaTime() / 1000;
if (this.rotationAnimation !== null) this.rotationAnimation.update(deltaTime);
@@ -251,7 +255,11 @@ export class StarMap {
});
}
- public dispatchWarpCallbacks() {
+ public setRunning(running: boolean): void {
+ this.isRunning = running;
+ }
+
+ private dispatchWarpCallbacks() {
if (this.selectedSystemSeed === null) throw new Error("No system selected!");
this.onWarpObservable.notifyObservers(this.selectedSystemSeed);
}
diff --git a/src/ts/ui/pauseMenu.ts b/src/ts/ui/pauseMenu.ts
new file mode 100644
index 000000000..058e18468
--- /dev/null
+++ b/src/ts/ui/pauseMenu.ts
@@ -0,0 +1,41 @@
+import { Observable } from "@babylonjs/core/Misc/observable";
+import pauseMenuHTML from "../../html/pauseMenu.html";
+
+export class PauseMenu {
+ private readonly rootNode: HTMLElement;
+ private readonly mask: HTMLElement;
+
+ private readonly screenshotButton: HTMLElement;
+ private readonly shareButton: HTMLElement;
+ private readonly resumeButton: HTMLElement;
+
+ readonly onScreenshot = new Observable();
+ readonly onShare = new Observable();
+ readonly onResume = new Observable();
+
+ constructor() {
+ document.body.insertAdjacentHTML("beforeend", pauseMenuHTML);
+ this.rootNode = document.getElementById("pauseMenu") as HTMLElement;
+ this.mask = document.getElementById("pauseMask") as HTMLElement;
+
+ this.screenshotButton = document.getElementById("screenshotButton") as HTMLElement;
+ this.screenshotButton.addEventListener("click", () => this.onScreenshot.notifyObservers());
+
+ this.shareButton = document.getElementById("shareButton") as HTMLElement;
+ this.shareButton.addEventListener("click", () => this.onShare.notifyObservers());
+
+ this.resumeButton = document.getElementById("resumeButton") as HTMLElement;
+ this.resumeButton.addEventListener("click", () => this.onResume.notifyObservers());
+
+ this.setVisibility(false);
+ }
+
+ public setVisibility(visible: boolean) {
+ this.rootNode.style.display = visible ? "grid" : "none";
+ this.mask.style.display = visible ? "block" : "none";
+ }
+
+ public isVisible(): boolean {
+ return this.rootNode.style.visibility !== "none";
+ }
+}