Some checks failed
Build / build (push) Failing after 20s
Implemented a toggleable status screen that displays game statistics, activated by pressing the X button on the left VR controller. **New File: src/gameStats.ts** - GameStats class to track game statistics - Tracks: game time, asteroids destroyed, hull damage taken, shots fired, shots hit, fuel consumed - Methods: startTimer(), recordAsteroidDestroyed(), recordHullDamage(), recordShotFired(), recordShotHit(), recordFuelConsumed() - Calculated stats: getGameTime(), getFormattedGameTime() (MM:SS), getAccuracy() (percentage) - getStats() returns formatted statistics object - reset() method to reset all statistics **New File: src/statusScreen.ts** - StatusScreen class for visual display of game statistics - Creates a 1.5x1.0 meter plane mesh with AdvancedDynamicTexture (1024x768) - Dark blue background (#1a1a2e) with green title (#00ff88) - Displays 6 statistics: - Game Time (MM:SS format) - Asteroids Destroyed (count) - Hull Damage Taken (percentage) - Shots Fired (count) - Accuracy (percentage) - Fuel Consumed (percentage) - toggle() method to show/hide screen - Positions 2 meters in front of camera when shown - Automatically faces camera with proper orientation - updateStatistics() refreshes displayed values from GameStats **Modified: src/controllerInput.ts** - Added _onStatusScreenToggleObservable for X button events - Added onStatusScreenToggleObservable getter - Added X button handler in handleControllerEvent() - Checks for "x-button" on left controller only - Fires observable on button press (not release) **Modified: src/ship.ts** - Added StatusScreen and GameStats imports - Added _statusScreen and _gameStats properties - Initialize GameStats in constructor - Initialize StatusScreen with camera reference after camera creation - Wired up controller observable to toggle status screen - Added statusScreen cleanup in dispose() method Features: - Toggle display with X button on left controller - Floats 2 meters in front of user's face - Always faces the camera - Clean, readable statistics layout - Currently displays placeholder data (statistics tracking integration pending) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
233 lines
6.5 KiB
TypeScript
233 lines
6.5 KiB
TypeScript
import {
|
|
AdvancedDynamicTexture,
|
|
Control,
|
|
Rectangle,
|
|
StackPanel,
|
|
TextBlock
|
|
} from "@babylonjs/gui";
|
|
import {
|
|
Mesh,
|
|
MeshBuilder,
|
|
Scene,
|
|
StandardMaterial,
|
|
TransformNode,
|
|
Vector3
|
|
} from "@babylonjs/core";
|
|
import { GameStats } from "./gameStats";
|
|
|
|
/**
|
|
* Status screen that displays game statistics
|
|
* Floats in front of the user and can be toggled on/off
|
|
*/
|
|
export class StatusScreen {
|
|
private _scene: Scene;
|
|
private _gameStats: GameStats;
|
|
private _screenMesh: Mesh | null = null;
|
|
private _texture: AdvancedDynamicTexture | null = null;
|
|
private _isVisible: boolean = false;
|
|
private _camera: TransformNode | null = null;
|
|
|
|
// Text blocks for statistics
|
|
private _gameTimeText: TextBlock;
|
|
private _asteroidsText: TextBlock;
|
|
private _hullDamageText: TextBlock;
|
|
private _shotsFiredText: TextBlock;
|
|
private _accuracyText: TextBlock;
|
|
private _fuelConsumedText: TextBlock;
|
|
|
|
constructor(scene: Scene, gameStats: GameStats) {
|
|
this._scene = scene;
|
|
this._gameStats = gameStats;
|
|
}
|
|
|
|
/**
|
|
* Initialize the status screen mesh and UI
|
|
*/
|
|
public initialize(camera: TransformNode): void {
|
|
this._camera = camera;
|
|
|
|
// Create a plane mesh for the status screen
|
|
this._screenMesh = MeshBuilder.CreatePlane(
|
|
"statusScreen",
|
|
{ width: 1.5, height: 1.0 },
|
|
this._scene
|
|
);
|
|
|
|
// Create material
|
|
const material = new StandardMaterial("statusScreenMaterial", this._scene);
|
|
this._screenMesh.material = material;
|
|
|
|
// Create AdvancedDynamicTexture
|
|
this._texture = AdvancedDynamicTexture.CreateForMesh(
|
|
this._screenMesh,
|
|
1024,
|
|
768
|
|
);
|
|
this._texture.background = "#1a1a2e";
|
|
|
|
// Create main container
|
|
const mainPanel = new StackPanel("mainPanel");
|
|
mainPanel.width = "100%";
|
|
mainPanel.height = "100%";
|
|
mainPanel.isVertical = true;
|
|
mainPanel.paddingTop = "40px";
|
|
mainPanel.paddingBottom = "40px";
|
|
mainPanel.paddingLeft = "60px";
|
|
mainPanel.paddingRight = "60px";
|
|
|
|
// Title
|
|
const title = this.createTitleText("GAME STATISTICS");
|
|
mainPanel.addControl(title);
|
|
|
|
// Add spacing
|
|
const spacer1 = this.createSpacer(40);
|
|
mainPanel.addControl(spacer1);
|
|
|
|
// Create statistics display
|
|
this._gameTimeText = this.createStatText("Game Time: 00:00");
|
|
mainPanel.addControl(this._gameTimeText);
|
|
|
|
this._asteroidsText = this.createStatText("Asteroids Destroyed: 0");
|
|
mainPanel.addControl(this._asteroidsText);
|
|
|
|
this._hullDamageText = this.createStatText("Hull Damage Taken: 0%");
|
|
mainPanel.addControl(this._hullDamageText);
|
|
|
|
this._shotsFiredText = this.createStatText("Shots Fired: 0");
|
|
mainPanel.addControl(this._shotsFiredText);
|
|
|
|
this._accuracyText = this.createStatText("Accuracy: 0%");
|
|
mainPanel.addControl(this._accuracyText);
|
|
|
|
this._fuelConsumedText = this.createStatText("Fuel Consumed: 0%");
|
|
mainPanel.addControl(this._fuelConsumedText);
|
|
|
|
this._texture.addControl(mainPanel);
|
|
|
|
// Initially hide the screen
|
|
this._screenMesh.setEnabled(false);
|
|
this._isVisible = false;
|
|
}
|
|
|
|
/**
|
|
* Create title text block
|
|
*/
|
|
private createTitleText(text: string): TextBlock {
|
|
const textBlock = new TextBlock();
|
|
textBlock.text = text;
|
|
textBlock.color = "#00ff88";
|
|
textBlock.fontSize = "80px";
|
|
textBlock.height = "100px";
|
|
textBlock.fontWeight = "bold";
|
|
textBlock.textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
|
|
return textBlock;
|
|
}
|
|
|
|
/**
|
|
* Create stat text block
|
|
*/
|
|
private createStatText(text: string): TextBlock {
|
|
const textBlock = new TextBlock();
|
|
textBlock.text = text;
|
|
textBlock.color = "#ffffff";
|
|
textBlock.fontSize = "50px";
|
|
textBlock.height = "70px";
|
|
textBlock.textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
|
|
textBlock.paddingTop = "10px";
|
|
textBlock.paddingBottom = "10px";
|
|
return textBlock;
|
|
}
|
|
|
|
/**
|
|
* Create spacer for layout
|
|
*/
|
|
private createSpacer(height: number): Rectangle {
|
|
const spacer = new Rectangle();
|
|
spacer.height = `${height}px`;
|
|
spacer.thickness = 0;
|
|
return spacer;
|
|
}
|
|
|
|
/**
|
|
* Toggle visibility of status screen
|
|
*/
|
|
public toggle(): void {
|
|
if (this._isVisible) {
|
|
this.hide();
|
|
} else {
|
|
this.show();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show the status screen
|
|
*/
|
|
public show(): void {
|
|
if (!this._screenMesh || !this._camera) {
|
|
return;
|
|
}
|
|
|
|
// Update statistics before showing
|
|
this.updateStatistics();
|
|
|
|
// Position screen 2 meters in front of camera
|
|
const cameraForward = this._camera.forward;
|
|
const offset = cameraForward.scale(2.0);
|
|
this._screenMesh.position = this._camera.position.add(offset);
|
|
|
|
// Make screen face the camera
|
|
this._screenMesh.lookAt(this._camera.position);
|
|
|
|
// Rotate 180 degrees to face the right way
|
|
this._screenMesh.rotate(Vector3.Up(), Math.PI);
|
|
|
|
this._screenMesh.setEnabled(true);
|
|
this._isVisible = true;
|
|
}
|
|
|
|
/**
|
|
* Hide the status screen
|
|
*/
|
|
public hide(): void {
|
|
if (!this._screenMesh) {
|
|
return;
|
|
}
|
|
|
|
this._screenMesh.setEnabled(false);
|
|
this._isVisible = false;
|
|
}
|
|
|
|
/**
|
|
* Update displayed statistics
|
|
*/
|
|
public updateStatistics(): void {
|
|
const stats = this._gameStats.getStats();
|
|
|
|
this._gameTimeText.text = `Game Time: ${stats.gameTime}`;
|
|
this._asteroidsText.text = `Asteroids Destroyed: ${stats.asteroidsDestroyed}`;
|
|
this._hullDamageText.text = `Hull Damage Taken: ${stats.hullDamageTaken}%`;
|
|
this._shotsFiredText.text = `Shots Fired: ${stats.shotsFired}`;
|
|
this._accuracyText.text = `Accuracy: ${stats.accuracy}%`;
|
|
this._fuelConsumedText.text = `Fuel Consumed: ${stats.fuelConsumed}%`;
|
|
}
|
|
|
|
/**
|
|
* Check if status screen is visible
|
|
*/
|
|
public get isVisible(): boolean {
|
|
return this._isVisible;
|
|
}
|
|
|
|
/**
|
|
* Dispose of status screen resources
|
|
*/
|
|
public dispose(): void {
|
|
if (this._texture) {
|
|
this._texture.dispose();
|
|
}
|
|
if (this._screenMesh) {
|
|
this._screenMesh.dispose();
|
|
}
|
|
}
|
|
}
|