Refactor scoreboard to use GLB mesh and improve ship mechanics
All checks were successful
Build / build (push) Successful in 1m19s
All checks were successful
Build / build (push) Successful in 1m19s
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
37128d8fbd
commit
f11005fdb6
BIN
public/base.glb
BIN
public/base.glb
Binary file not shown.
BIN
public/ship1.glb
BIN
public/ship1.glb
Binary file not shown.
BIN
public/ship2.glb
BIN
public/ship2.glb
Binary file not shown.
@ -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...");
|
||||
|
||||
@ -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<ScoreEvent> = new Observable<ScoreEvent>();
|
||||
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 {
|
||||
|
||||
27
src/ship.ts
27
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<ControllerEvent> = new Observable<ControllerEvent>();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user