diff --git a/index.html b/index.html
index 2fe9e75..a0c3459 100644
--- a/index.html
+++ b/index.html
@@ -12,10 +12,12 @@
}
});
+
+
+
diff --git a/public/styles.css b/public/styles.css
index c2c988f..6217ed7 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -8,31 +8,7 @@ body {
font-family: Roboto, sans-serif;
font-size: large;
}
-#startGame {
- position: absolute;
- display: block;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- padding: 48px;
- border-radius: 12px;
- background-color: #000;
- color: #fff;
- z-index: 1000;
-}
-#music {
- position: absolute;
- display: block;
- top: 75%;
- left: 50%;
- width: 50%;
- transform: translate(-50%, -50%);
- padding: 48px;
- border-radius: 12px;
- background-color: #000;
- color: #fff;
- z-index: 1000;
-}
+
#startButton {
background-color: #000;
color: #fff;
@@ -41,11 +17,25 @@ body {
cursor: pointer;
font-size: xxx-large;
display: none;
+ z-index: 1000;
}
#startButton.ready {
display: block;
background-color: red;
}
+#loadingDiv {
+ z-index: 1000;
+ color: #fff;
+}
+#mainDiv {
+ position: absolute;
+ display: block;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 1000;
+}
+
#gameCanvas {
width: 100%;
height: 100%;
diff --git a/src/defaultScene.ts b/src/defaultScene.ts
index 8136b4c..75b563d 100644
--- a/src/defaultScene.ts
+++ b/src/defaultScene.ts
@@ -2,5 +2,6 @@ import {Scene, WebXRDefaultExperience} from "@babylonjs/core";
export class DefaultScene {
public static MainScene: Scene;
+ public static DemoScene: Scene;
public static XR: WebXRDefaultExperience;
}
\ No newline at end of file
diff --git a/src/demo.ts b/src/demo.ts
new file mode 100644
index 0000000..e0ed967
--- /dev/null
+++ b/src/demo.ts
@@ -0,0 +1,18 @@
+import {DefaultScene} from "./defaultScene";
+import {ArcRotateCamera, MeshBuilder, PointerEventTypes, Vector3} from "@babylonjs/core";
+import {Main} from "./main";
+
+export default class Demo {
+ private _main: Main;
+ constructor(main: Main) {
+ this._main = main;
+ this.initialize();
+ }
+ private async initialize() {
+ if (!DefaultScene.DemoScene) {
+ return;
+ }
+ const scene = DefaultScene.DemoScene;
+ const camera = new ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2, 5, new Vector3(0, 0, 0), scene);
+ }
+}
\ No newline at end of file
diff --git a/src/level.ts b/src/level.ts
new file mode 100644
index 0000000..88d478b
--- /dev/null
+++ b/src/level.ts
@@ -0,0 +1,8 @@
+import {Observable} from "@babylonjs/core";
+
+export default interface Level {
+ initialize(): void;
+ dispose(): void;
+ play(): void;
+ getReadyObservable(): Observable
;
+}
\ No newline at end of file
diff --git a/src/level1.ts b/src/level1.ts
index c6e4bde..d384e09 100644
--- a/src/level1.ts
+++ b/src/level1.ts
@@ -1,35 +1,59 @@
import {DefaultScene} from "./defaultScene";
import {
AbstractMesh,
- Color3, DistanceConstraint, InstancedMesh, Mesh,
+ Color3, DistanceConstraint, Engine, InstancedMesh, Mesh,
MeshBuilder,
Observable,
ParticleHelper,
PhysicsAggregate,
PhysicsMotionType,
- PhysicsShapeType,
+ PhysicsShapeType, Sound,
StandardMaterial, TransformNode,
Vector3
} from "@babylonjs/core";
import {Ship} from "./ship";
import {ScoreEvent} from "./scoreEvent";
import {RockFactory} from "./starfield";
+import Level from "./level";
-export class Level1 {
+export class Level1 implements Level {
private _ship: Ship;
+ private _onReadyObservable: Observable = new Observable();
private _initialized: boolean = false;
private _startBase: AbstractMesh;
private _endBase: AbstractMesh;
public onScoreObservable: Observable = new Observable();
- constructor(ship: Ship) {
- this._ship = ship;
+ constructor() {
+ this._ship = new Ship();
+ const xr = DefaultScene.XR;
+ xr.baseExperience.onInitialXRPoseSetObservable.add(() => {
+ xr.baseExperience.camera.parent = this._ship.transformNode;
+ xr.baseExperience.camera.position = new Vector3(0, 0, 0);
+ });
+ xr.input.onControllerAddedObservable.add((controller) => {
+ this._ship.addController(controller);
+ });
this.createStartBase();
- this.createEndBase();
+
+ this.initialize();
+
+ }
+
+ getReadyObservable(): Observable {
+ return this._onReadyObservable;
}
private scored: Set = new Set();
-
+ public play() {
+ const background = new Sound("background", "/background.mp3", DefaultScene.MainScene, () => {
+ }, {loop: true, autoplay: true, volume: .2});
+ DefaultScene.XR.baseExperience.enterXRAsync('immersive-vr', 'local-floor');
+ }
+ public dispose() {
+ this._startBase.dispose();
+ this._endBase.dispose();
+ }
public async initialize() {
if (this._initialized) {
return;
@@ -37,43 +61,18 @@ export class Level1 {
this._initialized = true;
ParticleHelper.BaseAssetsUrl = window.location.href;
this._ship.position = new Vector3(0, 1, 0);
-
await RockFactory.init();
- const distance = Vector3.Distance(this._startBase.getAbsolutePosition(), this._endBase.getAbsolutePosition());
const baseTransform = new TransformNode("baseTransform", DefaultScene.MainScene);
- baseTransform.position = this._endBase.getAbsolutePosition();
-
- for (let i = 0; i < 20; i++) {
- //const constraintDistance = distance - 20;
- const dist = (Math.random() * 80) + 20;
- const startPos = this._endBase.getAbsolutePosition().add(new Vector3(dist,dist,dist));
- const startTrans = new TransformNode("startTransform", DefaultScene.MainScene);
- startTrans.position = startPos;
- startTrans.setParent(baseTransform);
- baseTransform.rotation = Vector3.Random(0, Math.PI * 2);
- const rock = await RockFactory.createRock(i, startTrans.getAbsolutePosition(), Vector3.Random(1, 5))
- startTrans.dispose();
-
-
-
-
+ baseTransform.position = this._startBase.getAbsolutePosition();
+ for (let i = 0; i < 50; i++) {
+ const dist = (Math.random() * 200) + 190;
+ const rock = await RockFactory.createRock(i, new Vector3(Math.random() * 200 +50 * Math.sign(Math.random() -.5),200,200), Vector3.Random(1, 5))
const constraint = new DistanceConstraint(dist, DefaultScene.MainScene);
- rock.physicsBody.addConstraint(this._endBase.physicsBody, constraint);
- rock.physicsBody.applyImpulse(Vector3.Random(-1, 1).scale(1000), rock.getAbsolutePosition());
- rock.physicsBody.setAngularVelocity(Vector3.Random(-.5, .5));
- /* const material = new StandardMaterial("material", DefaultScene.MainScene);
- material.emissiveColor = Color3.Random();
- const sphere = MeshBuilder.CreateSphere("sphere", {diameter: 1}, DefaultScene.MainScene);
- sphere.material = material;
-
- window.setInterval(() => {
- const track = new InstancedMesh("track", sphere);
- track.position = rock.physicsBody.transformNode.getAbsolutePosition();
-
- }, 200);
-
- */
+ //rock.physicsBody.addConstraint(this._endBase.physicsBody, constraint);
+ this._startBase.physicsBody.addConstraint(rock.physicsBody, constraint);
+ rock.physicsBody.applyForce(Vector3.Random(-1, 1).scale(50000000), rock.getAbsolutePosition());
+ //rock.physicsBody.setAngularVelocity(Vector3.Random(-.5, .5));
}
}
@@ -97,7 +96,7 @@ export class Level1 {
height: 1,
tessellation: 72
}, DefaultScene.MainScene);
- mesh.position = new Vector3(0, 5, 200);
+ mesh.position = new Vector3(0, 5, 500);
const material = new StandardMaterial("material", DefaultScene.MainScene);
material.diffuseColor = new Color3(0, 1, 0);
mesh.material = material;
diff --git a/src/main.ts b/src/main.ts
index 501c861..7fcf961 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -6,21 +6,42 @@ import {DefaultScene} from "./defaultScene";
import {Ship} from "./ship";
import {Level1} from "./level1";
import {Scoreboard} from "./scoreboard";
+import Demo from "./demo";
+import Level from "./level";
const webGpu = false;
const canvas = (document.querySelector('#gameCanvas') as HTMLCanvasElement);
-
+enum GameState {
+ PLAY,
+ DEMO
+}
export class Main {
-
+ private _loadingDiv: HTMLElement;
+ private _currentLevel: Level;
+ private _gameState: GameState = GameState.DEMO;
constructor() {
-
+ this._loadingDiv = document.querySelector('#loadingDiv');
this.initialize();
- }
+ document.querySelector('#startButton').addEventListener('click', () => {
+ Engine.audioEngine.unlock();
+ this.play();
+ document.querySelector('#mainDiv').remove();
+ });
+ }
+ private _started = false;
+ public play() {
+ this._gameState = GameState.PLAY;
+ this._currentLevel.play();
+ }
+ public demo() {
+ this._gameState = GameState.DEMO;
+ }
private async initialize() {
+ this._loadingDiv.innerText = "Initializing.";
await this.setupScene();
- const xr = await WebXRDefaultExperience.CreateAsync(DefaultScene.MainScene, {
+ DefaultScene.XR = await WebXRDefaultExperience.CreateAsync(DefaultScene.MainScene, {
disablePointerSelection: true,
disableTeleportation: true,
disableNearInteraction: true,
@@ -28,29 +49,17 @@ export class Main {
disableDefaultUI: true,
});
- const ship = new Ship();
- const scoreboard = new Scoreboard();
- const level = new Level1(ship);
+ this.setLoadingMessage("Get Ready!");
+ this.setLoadingMessage("Initializing Level...");
+ this._currentLevel = new Level1();
+ this._currentLevel.getReadyObservable().add(() => {
+
+ });
const photoDome = new PhotoDome("testdome", '/8192.webp', {}, DefaultScene.MainScene);
-
-
-
- xr.baseExperience.onInitialXRPoseSetObservable.add(() => {
- xr.baseExperience.camera.parent = ship.transformNode;
- xr.baseExperience.camera.position = new Vector3(0, 0, 0);
-
- level.onScoreObservable.add((score) => {
- scoreboard.onscoreObservable.notifyObservers(score);
- });
-
- });
- xr.input.onControllerAddedObservable.add((controller) => {
- ship.addController(controller);
- });
- DefaultScene.XR = xr;
-
}
-
+ private setLoadingMessage(message: string) {
+ this._loadingDiv.innerText = message;
+ }
private async setupScene() {
let engine: WebGPUEngine | Engine = null;
@@ -64,30 +73,36 @@ export class Main {
window.onresize = () => {
engine.resize();
}
+ DefaultScene.DemoScene = new Scene(engine);
DefaultScene.MainScene = new Scene(engine);
-
+ this.setLoadingMessage("Initializing Physics Engine..");
await this.setupPhysics();
-
this.setupInspector();
-
engine.runRenderLoop(() => {
- DefaultScene.MainScene.render();
+ if (!this._started) {
+ this._started = true;
+ this._loadingDiv.remove();
+ const start = document.querySelector('#startButton');
+ start.classList.add('ready');
+ }
+ if (this._gameState == GameState.PLAY) {
+ DefaultScene.MainScene.render();
+ } else {
+ DefaultScene.DemoScene.render();
+ }
});
}
private async setupPhysics() {
const havok = await HavokPhysics();
-
const havokPlugin = new HavokPlugin(true, havok);
DefaultScene.MainScene.enablePhysics(new Vector3(0, 0, 0), havokPlugin);
-
DefaultScene.MainScene.collisionsEnabled = true;
-
-
}
private setupInspector() {
+ this.setLoadingMessage("Initializing Inspector...");
window.addEventListener("keydown", (ev) => {
if (ev.key == 'i') {
import ("@babylonjs/inspector").then((inspector) => {
@@ -102,7 +117,7 @@ export class Main {
}
const main = new Main();
-
+const demo = new Demo(main);
diff --git a/src/ship.ts b/src/ship.ts
index 8a8a2fc..4058851 100644
--- a/src/ship.ts
+++ b/src/ship.ts
@@ -22,7 +22,7 @@ import {
WebXRInputSource
} from "@babylonjs/core";
import {DefaultScene} from "./defaultScene";
-import {Radar} from "./radar";
+
import {ShipEngine} from "./shipEngine";
import {Level1} from "./level1";
@@ -71,6 +71,45 @@ export class Ship {
private _camera: FreeCamera;
constructor() {
+
+ this.setup();
+ this.initialize();
+ }
+
+ private shoot() {
+ this._shot.play();
+ const ammo = MeshBuilder.CreateCapsule("bullet", {radius: .1, height: 2.5}, DefaultScene.MainScene);
+ ammo.parent = this._ship
+ ammo.position.y = 2;
+ ammo.rotation.x = Math.PI / 2;
+ ammo.setParent(null);
+ const ammoAggregate = new PhysicsAggregate(ammo, PhysicsShapeType.CONVEX_HULL, {
+ mass: 1000,
+ restitution: 0
+ }, DefaultScene.MainScene);
+
+
+ ammo.material = this._ammoMaterial;
+ ammoAggregate.body.setMotionType(PhysicsMotionType.DYNAMIC);
+
+ ammoAggregate.body.setLinearVelocity(this._ship.forward.scale(200).add(this._ship.physicsBody.getLinearVelocity()));
+
+ window.setTimeout(() => {
+ ammoAggregate.dispose();
+ ammo.dispose()
+ }, 1500)
+ }
+
+ public set position(newPosition: Vector3) {
+ const body = this._ship.physicsBody;
+ body.disablePreStep = false;
+ body.transformNode.position.copyFrom(newPosition);
+ DefaultScene.MainScene.onAfterRenderObservable.addOnce(() => {
+ body.disablePreStep = true;
+ })
+
+ }
+ private setup() {
this._ship = new TransformNode("ship", DefaultScene.MainScene);
this._glowLayer = new GlowLayer('bullets', DefaultScene.MainScene);
this._glowLayer.intensity = 1;
@@ -87,65 +126,12 @@ export class Ship {
{loop: false, autoplay: false, volume: .5});
this._ammoMaterial = new StandardMaterial("ammoMaterial", DefaultScene.MainScene);
this._ammoMaterial.emissiveColor = new Color3(1, 1, 0);
- this.initialize();
-
- }
-
- private shoot() {
- this._shot.play();
- const ammo = MeshBuilder.CreateCapsule("bullet", {radius: .05, height: 1.5}, DefaultScene.MainScene);
- ammo.parent = this._ship
- ammo.position.y = 2;
- ammo.rotation.x = Math.PI / 2;
- ammo.setParent(null);
- const ammoAggregate = new PhysicsAggregate(ammo, PhysicsShapeType.CONVEX_HULL, {
- mass: 1000,
- restitution: 0
- }, DefaultScene.MainScene);
-
-
- ammo.material = this._ammoMaterial;
- ammoAggregate.body.setMotionType(PhysicsMotionType.DYNAMIC);
-
- ammoAggregate.body.setLinearVelocity(this._ship.forward.scale(150).add(this._ship.physicsBody.getLinearVelocity()));
-
- window.setTimeout(() => {
- ammoAggregate.dispose();
- ammo.dispose()
- }, 5000)
- }
-
- public set position(newPosition: Vector3) {
- const body = this._ship.physicsBody;
- body.disablePreStep = false;
- body.transformNode.position.copyFrom(newPosition);
- DefaultScene.MainScene.onAfterRenderObservable.addOnce(() => {
- body.disablePreStep = true;
- })
-
- }
-
- private async initialize() {
const light = new DirectionalLight("light", new Vector3(.1, -1, 0), DefaultScene.MainScene);
- const ship = this._ship;
- const landingLight = new SpotLight("landingLight", new Vector3(0, 0, 0), new Vector3(0, -.5, .5), 1.5, .5, DefaultScene.MainScene);
- landingLight.parent = ship;
- landingLight.position.z = 5;
- const importMesh = await SceneLoader.ImportMeshAsync(null, "./", "cockpit3.glb", DefaultScene.MainScene);
- const shipMesh = importMesh.meshes[0];
- shipMesh.id = "shipMesh";
- shipMesh.name = "shipMesh";
- shipMesh.parent = ship;
- shipMesh.rotation.y = Math.PI;
- shipMesh.position.y = 1;
- shipMesh.position.z = -1;
- DefaultScene.MainScene.getMaterialById('glass_mat.002').alpha = .7;
- this._camera = new FreeCamera("Flat Camera",
- new Vector3(0, .5, 0),
- DefaultScene.MainScene);
- this._camera.parent = ship;
- const agg = new PhysicsAggregate(ship, PhysicsShapeType.BOX, {
+ const landingLight = new SpotLight("landingLight", new Vector3(0, 0, 0), new Vector3(0, -.5, .5), 1.5, .5, DefaultScene.MainScene);
+ landingLight.parent = this._ship;
+ landingLight.position.z = 5;
+ const agg = new PhysicsAggregate(this._ship, PhysicsShapeType.BOX, {
mass: 100,
extents: new Vector3(4, 4, 7.4),
center: new Vector3(0, 1, 1.8)
@@ -155,8 +141,7 @@ export class Ship {
agg.body.setLinearDamping(.1);
agg.body.setAngularDamping(.2);
agg.body.setAngularVelocity(new Vector3(0, 0, 0));
- agg.body.setCollisionCallbackEnabled(true)
- DefaultScene.MainScene.setActiveCameraByName("Flat Camera");
+ agg.body.setCollisionCallbackEnabled(true);
this.setupKeyboard();
this.setupMouse();
this._controllerObservable.add(this.controllerCallback);
@@ -164,6 +149,13 @@ export class Ship {
this._rotationNode = new TransformNode("rotation", DefaultScene.MainScene);
this._forwardNode.parent = this._ship;
this._rotationNode.parent = this._ship;
+ this._camera = new FreeCamera("Flat Camera",
+ new Vector3(0, .5, 0),
+ DefaultScene.MainScene);
+ this._camera.parent = this._ship;
+
+ DefaultScene.MainScene.setActiveCameraByName("Flat Camera");
+
//const sightPos = this._forwardNode.position.scale(30);
const sight = MeshBuilder.CreateSphere("sight", {diameter: 1}, DefaultScene.MainScene);
sight.parent = this._ship
@@ -171,30 +163,21 @@ export class Ship {
signtMaterial.emissiveColor = Color3.Yellow();
sight.material = signtMaterial;
sight.position = new Vector3(0, 2, 125);
+
window.setInterval(() => {
this.applyForce();
}, 50);
- this.onReadyObservable.notifyObservers(true);
- //const mirror = new Mirror(this._ship);
- const radar = new Radar(this._ship);
-
- document.querySelector('#loadingDiv').remove();
- //const startButton = document.querySelector('#startButton');
- //startButton.classList.add('ready');
- const level = new Level1(this);
- const background = new Sound("background", "/background.mp3", DefaultScene.MainScene, () => {
- const startButton = document.querySelector('#startButton');
- startButton.classList.add('ready');
- startButton.addEventListener('click', async () => {
- if (!Engine.audioEngine.unlocked) {
- Engine.audioEngine.unlock();
- }
- console.log('start background');
- await DefaultScene.XR.baseExperience.enterXRAsync('immersive-vr', 'local-floor');
- await level.initialize();
- });
- }, {loop: true, autoplay: true, volume: .2});
-
+ }
+ private async initialize() {
+ const importMesh = await SceneLoader.ImportMeshAsync(null, "./", "cockpit3.glb", DefaultScene.MainScene);
+ const shipMesh = importMesh.meshes[0];
+ shipMesh.id = "shipMesh";
+ shipMesh.name = "shipMesh";
+ shipMesh.parent = this._ship;
+ shipMesh.rotation.y = Math.PI;
+ shipMesh.position.y = 1;
+ shipMesh.position.z = -1;
+ DefaultScene.MainScene.getMaterialById('glass_mat.002').alpha = .7;
}
@@ -239,7 +222,7 @@ export class Ship {
this._thrust.play();
}
this._thrust.setVolume(Math.abs(this._leftStickVector.y));
- this._forwardValue += this._leftStickVector.y * .4;
+ this._forwardValue += this._leftStickVector.y * .8;
} else {
if (this._thrust.isPlaying) {
this._thrust.pause();