Performance fixes and debug features
All checks were successful
Build / build (push) Successful in 1m45s
All checks were successful
Build / build (push) Successful in 1m45s
- Fix duplicate render loops causing 50% FPS drop (70→40) - Add stopRenderLoop() before runRenderLoop() in level1.ts and levelSelectedHandler.ts - Add ?loglevel=debug|info|warn|error query parameter - Add Y button to toggle inspector in XR - Throttle scoreboard updates to every 10 frames - Throttle game-end condition checks to every 30 frames - Remove per-frame logging from explosion animations - Reduce background stars from 5000 to 2500 - Freeze asteroid material after loading - Reduce physics substeps from 5 to 2 - Disable autoClear for Quest 2 performance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
cf3a74ff0b
commit
b46f44e32d
@ -97,6 +97,7 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
|
|||||||
if (canvas) {
|
if (canvas) {
|
||||||
canvas.style.display = 'block';
|
canvas.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
engine.stopRenderLoop();
|
||||||
engine.runRenderLoop(() => {
|
engine.runRenderLoop(() => {
|
||||||
DefaultScene.MainScene.render();
|
DefaultScene.MainScene.render();
|
||||||
});
|
});
|
||||||
@ -164,6 +165,7 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
|
|||||||
if (canvas) {
|
if (canvas) {
|
||||||
canvas.style.display = 'block';
|
canvas.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
engine.stopRenderLoop();
|
||||||
engine.runRenderLoop(() => {
|
engine.runRenderLoop(() => {
|
||||||
DefaultScene.MainScene.render();
|
DefaultScene.MainScene.render();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
|
|
||||||
|
// Check URL query parameter for log level override
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const queryLevel = urlParams.get('loglevel');
|
||||||
|
if (queryLevel && ['debug', 'info', 'warn', 'error'].includes(queryLevel)) {
|
||||||
|
localStorage.setItem('log-level', queryLevel);
|
||||||
|
}
|
||||||
|
|
||||||
// Check localStorage for custom level (enables production debugging)
|
// Check localStorage for custom level (enables production debugging)
|
||||||
const storedLevel = localStorage.getItem('log-level');
|
const storedLevel = localStorage.getItem('log-level');
|
||||||
|
|
||||||
|
|||||||
@ -59,6 +59,11 @@ function createMainScene(engine: Engine): void {
|
|||||||
DefaultScene.MainScene = new Scene(engine);
|
DefaultScene.MainScene = new Scene(engine);
|
||||||
DefaultScene.MainScene.ambientColor = new Color3(.2, .2, .2);
|
DefaultScene.MainScene.ambientColor = new Color3(.2, .2, .2);
|
||||||
DefaultScene.MainScene.clearColor = new Color3(0, 0, 0).toColor4();
|
DefaultScene.MainScene.clearColor = new Color3(0, 0, 0).toColor4();
|
||||||
|
|
||||||
|
// Performance optimizations for Quest 2
|
||||||
|
//DefaultScene.MainScene.performancePriority = ScenePerformancePriority.Intermediate;
|
||||||
|
DefaultScene.MainScene.autoClear = false;
|
||||||
|
DefaultScene.MainScene.autoClearDepthAndStencil = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setupPhysics(): Promise<void> {
|
async function setupPhysics(): Promise<void> {
|
||||||
@ -66,7 +71,7 @@ async function setupPhysics(): Promise<void> {
|
|||||||
const havokPlugin = new HavokPlugin(true, havok);
|
const havokPlugin = new HavokPlugin(true, havok);
|
||||||
DefaultScene.MainScene.enablePhysics(new Vector3(0, 0, 0), havokPlugin);
|
DefaultScene.MainScene.enablePhysics(new Vector3(0, 0, 0), havokPlugin);
|
||||||
DefaultScene.MainScene.getPhysicsEngine()!.setTimeStep(1/60);
|
DefaultScene.MainScene.getPhysicsEngine()!.setTimeStep(1/60);
|
||||||
DefaultScene.MainScene.getPhysicsEngine()!.setSubTimeStep(5);
|
DefaultScene.MainScene.getPhysicsEngine()!.setSubTimeStep(2);
|
||||||
DefaultScene.MainScene.collisionsEnabled = true;
|
DefaultScene.MainScene.collisionsEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -299,17 +299,6 @@ export class ExplosionManager {
|
|||||||
|
|
||||||
frameCount++;
|
frameCount++;
|
||||||
|
|
||||||
// Log every 15 frames (approximately every 250ms at 60fps)
|
|
||||||
if (frameCount % 15 === 0 || frameCount === 1) {
|
|
||||||
log.debug(`[ExplosionManager] Animation frame ${frameCount}:`, {
|
|
||||||
elapsed: `${elapsed}ms`,
|
|
||||||
progress: progress.toFixed(3),
|
|
||||||
currentValue: currentValue.toFixed(2),
|
|
||||||
scale: scale.toFixed(3),
|
|
||||||
piecesAlive: meshPieces.filter(p => !p.isDisposed()).length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue animation if not complete
|
// Continue animation if not complete
|
||||||
if (progress >= 1.0) {
|
if (progress >= 1.0) {
|
||||||
// Animation complete - remove observer and clean up
|
// Animation complete - remove observer and clean up
|
||||||
|
|||||||
@ -93,6 +93,8 @@ export class RockFactory {
|
|||||||
log.debug('loading mesh');
|
log.debug('loading mesh');
|
||||||
const asset = await loadAsset("asteroid.glb");
|
const asset = await loadAsset("asteroid.glb");
|
||||||
this._asteroidMesh = asset.meshes.get('Asteroid') || null;
|
this._asteroidMesh = asset.meshes.get('Asteroid') || null;
|
||||||
|
this._asteroidMesh.material.backFaceCulling = true;
|
||||||
|
this._asteroidMesh.material.freeze();
|
||||||
if (this._asteroidMesh) {
|
if (this._asteroidMesh) {
|
||||||
this._asteroidMesh.setEnabled(false);
|
this._asteroidMesh.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,9 +27,9 @@ export class BackgroundStars {
|
|||||||
private scene: Scene;
|
private scene: Scene;
|
||||||
private config: Required<BackgroundStarsConfig>;
|
private config: Required<BackgroundStarsConfig>;
|
||||||
|
|
||||||
// Default configuration
|
// Default configuration (reduced from 5000 for Quest 2 performance)
|
||||||
private static readonly DEFAULT_CONFIG: Required<BackgroundStarsConfig> = {
|
private static readonly DEFAULT_CONFIG: Required<BackgroundStarsConfig> = {
|
||||||
count: 5000,
|
count: 2500,
|
||||||
radius: 5000,
|
radius: 5000,
|
||||||
minBrightness: 0.3,
|
minBrightness: 0.3,
|
||||||
maxBrightness: 1.0,
|
maxBrightness: 1.0,
|
||||||
|
|||||||
@ -112,8 +112,9 @@ export class Level1 implements Level {
|
|||||||
canvas.style.display = 'block';
|
canvas.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure render loop is running
|
// Ensure render loop is running (stop first to prevent duplicates)
|
||||||
const engine = DefaultScene.MainScene.getEngine();
|
const engine = DefaultScene.MainScene.getEngine();
|
||||||
|
engine.stopRenderLoop();
|
||||||
engine.runRenderLoop(() => {
|
engine.runRenderLoop(() => {
|
||||||
DefaultScene.MainScene.render();
|
DefaultScene.MainScene.render();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -58,6 +58,7 @@ export class ControllerInput {
|
|||||||
private _onCameraAdjustObservable: Observable<CameraAdjustment> =
|
private _onCameraAdjustObservable: Observable<CameraAdjustment> =
|
||||||
new Observable<CameraAdjustment>();
|
new Observable<CameraAdjustment>();
|
||||||
private _onStatusScreenToggleObservable: Observable<void> = new Observable<void>();
|
private _onStatusScreenToggleObservable: Observable<void> = new Observable<void>();
|
||||||
|
private _onInspectorToggleObservable: Observable<void> = new Observable<void>();
|
||||||
private _enabled: boolean = true;
|
private _enabled: boolean = true;
|
||||||
private _mappingConfig: ControllerMappingConfig;
|
private _mappingConfig: ControllerMappingConfig;
|
||||||
|
|
||||||
@ -87,6 +88,13 @@ export class ControllerInput {
|
|||||||
return this._onStatusScreenToggleObservable;
|
return this._onStatusScreenToggleObservable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get observable that fires when Y button is pressed on left controller
|
||||||
|
*/
|
||||||
|
public get onInspectorToggleObservable(): Observable<void> {
|
||||||
|
return this._onInspectorToggleObservable;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current input state (stick positions)
|
* Get current input state (stick positions)
|
||||||
* Applies controller mapping configuration to translate raw input to actions
|
* Applies controller mapping configuration to translate raw input to actions
|
||||||
@ -329,6 +337,12 @@ export class ControllerInput {
|
|||||||
this._onStatusScreenToggleObservable.notifyObservers();
|
this._onStatusScreenToggleObservable.notifyObservers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (controllerEvent.component.id === "y-button" && controllerEvent.hand === "left") {
|
||||||
|
// Only trigger on button press, not release
|
||||||
|
if (controllerEvent.pressed) {
|
||||||
|
this._onInspectorToggleObservable.notifyObservers();
|
||||||
|
}
|
||||||
|
}
|
||||||
log.info(controllerEvent);
|
log.info(controllerEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -341,5 +355,6 @@ export class ControllerInput {
|
|||||||
this._controllerObservable.clear();
|
this._controllerObservable.clear();
|
||||||
this._onShootObservable.clear();
|
this._onShootObservable.clear();
|
||||||
this._onCameraAdjustObservable.clear();
|
this._onCameraAdjustObservable.clear();
|
||||||
|
this._onInspectorToggleObservable.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -309,6 +309,18 @@ export class Ship {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Wire up inspector toggle event (Y button)
|
||||||
|
this._controllerInput.onInspectorToggleObservable.add(() => {
|
||||||
|
import('@babylonjs/inspector').then(() => {
|
||||||
|
const scene = DefaultScene.MainScene;
|
||||||
|
if (scene.debugLayer.isVisible()) {
|
||||||
|
scene.debugLayer.hide();
|
||||||
|
} else {
|
||||||
|
scene.debugLayer.show({ overlay: true, showExplorer: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Wire up camera adjustment events
|
// Wire up camera adjustment events
|
||||||
this._keyboardInput.onCameraChangeObservable.add((cameraKey) => {
|
this._keyboardInput.onCameraChangeObservable.add((cameraKey) => {
|
||||||
if (cameraKey === 1) {
|
if (cameraKey === 1) {
|
||||||
@ -335,16 +347,22 @@ export class Ship {
|
|||||||
this._physics.setGameStats(this._gameStats);
|
this._physics.setGameStats(this._gameStats);
|
||||||
|
|
||||||
// Setup physics update loop (every 10 frames)
|
// Setup physics update loop (every 10 frames)
|
||||||
|
let p = 0;
|
||||||
this._physicsObserver = DefaultScene.MainScene.onAfterPhysicsObservable.add(() => {
|
this._physicsObserver = DefaultScene.MainScene.onAfterPhysicsObservable.add(() => {
|
||||||
this.updatePhysics();
|
|
||||||
|
this.updatePhysics();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
let renderFrameCount = 0;
|
||||||
this._renderObserver = DefaultScene.MainScene.onAfterRenderObservable.add(() => {
|
this._renderObserver = DefaultScene.MainScene.onAfterRenderObservable.add(() => {
|
||||||
// Update voice audio system (checks for completed sounds and plays next in queue)
|
// Update voice audio system (checks for completed sounds and plays next in queue)
|
||||||
if (this._voiceAudio) {
|
if (this._voiceAudio) {
|
||||||
this._voiceAudio.update();
|
this._voiceAudio.update();
|
||||||
}
|
}
|
||||||
// Check game end conditions every frame (but only acts once)
|
// Check game end conditions every 30 frames (~0.5 sec at 60fps)
|
||||||
this.checkGameEndConditions();
|
if (renderFrameCount++ % 30 === 0) {
|
||||||
|
this.checkGameEndConditions();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Setup camera
|
// Setup camera
|
||||||
|
|||||||
@ -189,6 +189,8 @@ export class Scoreboard {
|
|||||||
advancedTexture.addControl(panel);
|
advancedTexture.addControl(panel);
|
||||||
let i = 0;
|
let i = 0;
|
||||||
const _afterRender = scene.onAfterRenderObservable.add(() => {
|
const _afterRender = scene.onAfterRenderObservable.add(() => {
|
||||||
|
if (i++ % 10 !== 0) return;
|
||||||
|
|
||||||
scoreText.text = `Score: ${this.calculateScore()}`;
|
scoreText.text = `Score: ${this.calculateScore()}`;
|
||||||
remainingText.text = `Remaining: ${this._remaining}`;
|
remainingText.text = `Remaining: ${this._remaining}`;
|
||||||
|
|
||||||
@ -201,7 +203,7 @@ export class Scoreboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const elapsed = Date.now() - this._startTime;
|
const elapsed = Date.now() - this._startTime;
|
||||||
if (this._active && i++%30 == 0) {
|
if (this._active) {
|
||||||
timeRemainingText.text = `Time: ${Math.floor(elapsed/60000).toString().padStart(2,"0")}:${(Math.floor(elapsed/1000)%60).toString().padStart(2,"0")}`;
|
timeRemainingText.text = `Time: ${Math.floor(elapsed/60000).toString().padStart(2,"0")}:${(Math.floor(elapsed/1000)%60).toString().padStart(2,"0")}`;
|
||||||
fpsText.text = `FPS: ${Math.floor(scene.getEngine().getFps())}`;
|
fpsText.text = `FPS: ${Math.floor(scene.getEngine().getFps())}`;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user