From f11005fdb65b1574d7685d940498505fd07478fa Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Tue, 4 Nov 2025 13:41:17 -0600 Subject: [PATCH] Refactor scoreboard to use GLB mesh and improve ship mechanics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move scoreboard ownership from Level1 to Ship class for better encapsulation - Refactor scoreboard.initialize() to accept GLB submesh (Screen material mesh) - Dispose original material when applying AdvancedDynamicTexture to mesh - Change scoreboard background to green for visibility testing - Increase ship velocities: MAX_LINEAR_VELOCITY to 200, LINEAR_FORCE_MULTIPLIER to 1200 - Adjust ammo spawn position (y: 0.5, z: 7.1) and velocity (200000) - Update sight reticle position to match new ammo spawn point (y: 0.5) - Fix sight circle rotation and rendering group assignment - Update ship2.glb, ship1.glb, and base.glb models - Comment out ship position override in level initialization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- public/base.glb | Bin 6784196 -> 6783428 bytes public/ship1.glb | Bin 10416 -> 19812 bytes public/ship2.glb | Bin 9848 -> 19812 bytes src/level1.ts | 9 ++++----- src/scoreboard.ts | 40 ++++++++++++++++++++++++++++------------ src/ship.ts | 27 ++++++++++++++++++++------- src/sight.ts | 3 ++- 7 files changed, 54 insertions(+), 25 deletions(-) diff --git a/public/base.glb b/public/base.glb index a3df2df388b6e4a18a63ac3d6e2188cb0c271e52..c332c493083fadcaac17a2799b7d812c6c7b5bf5 100644 GIT binary patch delta 4797 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 Uc@=pLDL`f*Gmjs1EmW5L4}77O+yDRo diff --git a/public/ship1.glb b/public/ship1.glb index af17b15354d749960496f746bbc6e82f0fc36f65..a95912d6547833e87656b6ceec065fa6f68b5b3c 100644 GIT binary patch literal 19812 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 W{ZY)w=kI%j4#RqgJGS__NB$4AfRES! literal 10416 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 X<^562$mj2Sgbu@chUxkvsFxD1cj literal 9848 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 diff --git a/src/level1.ts b/src/level1.ts index 8901360..6abfb66 100644 --- a/src/level1.ts +++ b/src/level1.ts @@ -27,7 +27,6 @@ export class Level1 implements Level { private _initialized: boolean = false; private _startBase: AbstractMesh; private _endBase: AbstractMesh; - private _scoreboard: Scoreboard; private _levelConfig: LevelConfig; private _audioEngine: AudioEngineV2; private _deserializer: LevelDeserializer; @@ -38,7 +37,7 @@ export class Level1 implements Level { this._audioEngine = audioEngine; this._deserializer = new LevelDeserializer(levelConfig); this._ship = new Ship(audioEngine); - this._scoreboard = new Scoreboard(); + const xr = DefaultScene.XR; debugLog('Level1 constructor - Setting up XR observables'); @@ -105,18 +104,18 @@ export class Level1 implements Level { setLoadingMessage("Loading level from configuration..."); // Use deserializer to create all entities from config - const entities = await this._deserializer.deserialize(this._scoreboard.onScoreObservable); + const entities = await this._deserializer.deserialize(this._ship.scoreboard.onScoreObservable); this._startBase = entities.startBase; // sun and planets are already created by deserializer // Initialize scoreboard with total asteroid count - this._scoreboard.setRemainingCount(entities.asteroids.length); + this._ship.scoreboard.setRemainingCount(entities.asteroids.length); debugLog(`Initialized scoreboard with ${entities.asteroids.length} asteroids`); // Position ship from config const shipConfig = this._deserializer.getShipConfig(); - this._ship.position = new Vector3(shipConfig.position[0], shipConfig.position[1], shipConfig.position[2]); + //this._ship.position = new Vector3(shipConfig.position[0], shipConfig.position[1], shipConfig.position[2]); // Add distance constraints to asteroids (if physics enabled) setLoadingMessage("Configuring physics constraints..."); diff --git a/src/scoreboard.ts b/src/scoreboard.ts index 506def4..b34f82f 100644 --- a/src/scoreboard.ts +++ b/src/scoreboard.ts @@ -1,6 +1,7 @@ import {AdvancedDynamicTexture, Control, StackPanel, TextBlock} from "@babylonjs/gui"; import {DefaultScene} from "./defaultScene"; import { + AbstractMesh, Mesh, MeshBuilder, Observable, StandardMaterial, Vector3, @@ -21,7 +22,7 @@ export class Scoreboard { private _done = false; public readonly onScoreObservable: Observable = new Observable(); constructor() { - this.initialize(); + } public get done() { return this._done; @@ -32,26 +33,40 @@ export class Scoreboard { public setRemainingCount(count: number) { this._remaining = count; } - private initialize() { + public initialize(baseMesh: Mesh) { const scene = DefaultScene.MainScene; const parent = scene.getNodeById('ship'); debugLog('Scoreboard parent:', parent); debugLog('Initializing scoreboard'); - const scoreboard = MeshBuilder.CreatePlane("scoreboard", {width: 1, height: 1}, scene); + let scoreboard = null; + + if (baseMesh) { + scoreboard = baseMesh; + + scoreboard.material.dispose(); + //scoreboard.material = new StandardMaterial("scoreboard", scene); + + } else { + scoreboard = MeshBuilder.CreatePlane("scoreboard", {width: 1, height: 1}, scene); + scoreboard.parent =parent; + + scoreboard.position.y = 1.05; + scoreboard.position.z = 2.1; + scoreboard.visibility = .5; + scoreboard.scaling = new Vector3(.4, .4, .4); + } + // scoreboard.renderingGroupId = 3; - const material = new StandardMaterial("scoreboard", scene); - scoreboard.parent =parent; - scoreboard.position.y = 1.05; - scoreboard.position.z = 2.1; - scoreboard.visibility = .5; - scoreboard.scaling = new Vector3(.4, .4, .4); + + + const advancedTexture = AdvancedDynamicTexture.CreateForMesh(scoreboard, 512, 512); - advancedTexture.background = "black"; + advancedTexture.background = "green"; advancedTexture.hasAlpha = false; const scoreText = this.createText(); @@ -69,8 +84,8 @@ export class Scoreboard { const panel = new StackPanel(); panel.isVertical = true; - panel.height = 1; - panel.isVertical = true; + //panel.height = .5; + //panel.isVertical = true; panel.addControl(scoreText); panel.addControl(remainingText); panel.addControl(fpsText); @@ -92,6 +107,7 @@ export class Scoreboard { this._score += score.score; this._remaining += score.remaining; }); + this._active = true; } private createText(): TextBlock { diff --git a/src/ship.ts b/src/ship.ts index 89be482..70358e8 100644 --- a/src/ship.ts +++ b/src/ship.ts @@ -22,9 +22,10 @@ import {DefaultScene} from "./defaultScene"; import { GameConfig } from "./gameConfig"; import { Sight } from "./sight"; import debugLog from './debug'; -const MAX_LINEAR_VELOCITY = 80; +import {Scoreboard} from "./scoreboard"; +const MAX_LINEAR_VELOCITY = 200; const MAX_ANGULAR_VELOCITY = 1.8; -const LINEAR_FORCE_MULTIPLIER = 800; +const LINEAR_FORCE_MULTIPLIER = 1200; const ANGULAR_FORCE_MULTIPLIER = 20; const controllerComponents = [ @@ -52,6 +53,7 @@ type ControllerEvent = { export class Ship { private _ship: TransformNode; + private _scoreboard: Scoreboard; private _controllerObservable: Observable = new Observable(); private _ammoMaterial: StandardMaterial; private _primaryThrustVectorSound: StaticSound; @@ -87,7 +89,9 @@ export class Ship { volume: 0.5 }); } - + public get scoreboard(): Scoreboard { + return this._scoreboard; + } private shoot() { // Only allow shooting if physics is enabled const config = GameConfig.getInstance(); @@ -98,8 +102,9 @@ export class Ship { this._shot?.play(); const ammo = new InstancedMesh("ammo", this._ammoBaseMesh as Mesh); ammo.parent = this._ship; - ammo.position.y = 2; - ammo.rotation.x = Math.PI / 2; + ammo.position.y = .5; + ammo.position.z = 7.1; + //ammo.rotation.x = Math.PI / 2; ammo.setParent(null); const ammoAggregate = new PhysicsAggregate(ammo, PhysicsShapeType.SPHERE, { mass: 1000, @@ -110,7 +115,7 @@ export class Ship { ammoAggregate.body.setMotionType(PhysicsMotionType.DYNAMIC); - ammoAggregate.body.setLinearVelocity(this._ship.forward.scale(100000)) + ammoAggregate.body.setLinearVelocity(this._ship.forward.scale(200000)) //.add(this._ship.physicsBody.getLinearVelocity())); window.setTimeout(() => { @@ -170,7 +175,7 @@ export class Ship { // Create sight reticle this._sight = new Sight(DefaultScene.MainScene, this._ship, { - position: new Vector3(0, 2, 125), + position: new Vector3(0, .5, 125), circleRadius: 2, crosshairLength: 1.5, lineThickness: 0.1, @@ -183,11 +188,16 @@ export class Ship { private async initialize() { + this._scoreboard = new Scoreboard(); + const importMesh = await SceneLoader.ImportMeshAsync(null, "./", "ship2.glb", DefaultScene.MainScene); + const shipMesh = importMesh.meshes[0]; shipMesh.id = "shipMesh"; shipMesh.name = "shipMesh"; + debugLog(shipMesh.position); shipMesh.parent = this._ship; + debugLog(shipMesh.position); // Create physics aggregate based on the loaded mesh (if physics enabled) const config = GameConfig.getInstance(); @@ -242,6 +252,9 @@ export class Ship { } light.parent = this._ship;*/ //DefaultScene.MainScene.getMaterialById('glass_mat.002').alpha = .4; + const screenMesh = DefaultScene.MainScene.getMaterialById('Screen')?.getBindedMeshes()[0]; + this._scoreboard.initialize(screenMesh as Mesh); + //this._scoreboard.initialize(null); } diff --git a/src/sight.ts b/src/sight.ts index c51d924..a556766 100644 --- a/src/sight.ts +++ b/src/sight.ts @@ -78,8 +78,9 @@ export class Sight { tessellation: 64 }, this.scene); this.circle.parent = this.reticleGroup; + this.circle.rotation.x = -Math.PI / 2; this.circle.material = material; - // this.circle.renderingGroupId = this.config.renderingGroupId; + this.circle.renderingGroupId = this.config.renderingGroupId; // Create crosshair lines (4 lines extending from center gap) this.createCrosshairLines(material);