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) {
|
||||
canvas.style.display = 'block';
|
||||
}
|
||||
engine.stopRenderLoop();
|
||||
engine.runRenderLoop(() => {
|
||||
DefaultScene.MainScene.render();
|
||||
});
|
||||
@ -164,6 +165,7 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
|
||||
if (canvas) {
|
||||
canvas.style.display = 'block';
|
||||
}
|
||||
engine.stopRenderLoop();
|
||||
engine.runRenderLoop(() => {
|
||||
DefaultScene.MainScene.render();
|
||||
});
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
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)
|
||||
const storedLevel = localStorage.getItem('log-level');
|
||||
|
||||
|
||||
@ -59,6 +59,11 @@ function createMainScene(engine: Engine): void {
|
||||
DefaultScene.MainScene = new Scene(engine);
|
||||
DefaultScene.MainScene.ambientColor = new Color3(.2, .2, .2);
|
||||
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> {
|
||||
@ -66,7 +71,7 @@ async function setupPhysics(): Promise<void> {
|
||||
const havokPlugin = new HavokPlugin(true, havok);
|
||||
DefaultScene.MainScene.enablePhysics(new Vector3(0, 0, 0), havokPlugin);
|
||||
DefaultScene.MainScene.getPhysicsEngine()!.setTimeStep(1/60);
|
||||
DefaultScene.MainScene.getPhysicsEngine()!.setSubTimeStep(5);
|
||||
DefaultScene.MainScene.getPhysicsEngine()!.setSubTimeStep(2);
|
||||
DefaultScene.MainScene.collisionsEnabled = true;
|
||||
}
|
||||
|
||||
|
||||
@ -299,17 +299,6 @@ export class ExplosionManager {
|
||||
|
||||
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
|
||||
if (progress >= 1.0) {
|
||||
// Animation complete - remove observer and clean up
|
||||
|
||||
@ -93,6 +93,8 @@ export class RockFactory {
|
||||
log.debug('loading mesh');
|
||||
const asset = await loadAsset("asteroid.glb");
|
||||
this._asteroidMesh = asset.meshes.get('Asteroid') || null;
|
||||
this._asteroidMesh.material.backFaceCulling = true;
|
||||
this._asteroidMesh.material.freeze();
|
||||
if (this._asteroidMesh) {
|
||||
this._asteroidMesh.setEnabled(false);
|
||||
}
|
||||
|
||||
@ -27,9 +27,9 @@ export class BackgroundStars {
|
||||
private scene: Scene;
|
||||
private config: Required<BackgroundStarsConfig>;
|
||||
|
||||
// Default configuration
|
||||
// Default configuration (reduced from 5000 for Quest 2 performance)
|
||||
private static readonly DEFAULT_CONFIG: Required<BackgroundStarsConfig> = {
|
||||
count: 5000,
|
||||
count: 2500,
|
||||
radius: 5000,
|
||||
minBrightness: 0.3,
|
||||
maxBrightness: 1.0,
|
||||
|
||||
@ -112,8 +112,9 @@ export class Level1 implements Level {
|
||||
canvas.style.display = 'block';
|
||||
}
|
||||
|
||||
// Ensure render loop is running
|
||||
// Ensure render loop is running (stop first to prevent duplicates)
|
||||
const engine = DefaultScene.MainScene.getEngine();
|
||||
engine.stopRenderLoop();
|
||||
engine.runRenderLoop(() => {
|
||||
DefaultScene.MainScene.render();
|
||||
});
|
||||
|
||||
@ -58,6 +58,7 @@ export class ControllerInput {
|
||||
private _onCameraAdjustObservable: Observable<CameraAdjustment> =
|
||||
new Observable<CameraAdjustment>();
|
||||
private _onStatusScreenToggleObservable: Observable<void> = new Observable<void>();
|
||||
private _onInspectorToggleObservable: Observable<void> = new Observable<void>();
|
||||
private _enabled: boolean = true;
|
||||
private _mappingConfig: ControllerMappingConfig;
|
||||
|
||||
@ -87,6 +88,13 @@ export class ControllerInput {
|
||||
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)
|
||||
* Applies controller mapping configuration to translate raw input to actions
|
||||
@ -329,6 +337,12 @@ export class ControllerInput {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -341,5 +355,6 @@ export class ControllerInput {
|
||||
this._controllerObservable.clear();
|
||||
this._onShootObservable.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
|
||||
this._keyboardInput.onCameraChangeObservable.add((cameraKey) => {
|
||||
if (cameraKey === 1) {
|
||||
@ -335,16 +347,22 @@ export class Ship {
|
||||
this._physics.setGameStats(this._gameStats);
|
||||
|
||||
// Setup physics update loop (every 10 frames)
|
||||
let p = 0;
|
||||
this._physicsObserver = DefaultScene.MainScene.onAfterPhysicsObservable.add(() => {
|
||||
this.updatePhysics();
|
||||
|
||||
this.updatePhysics();
|
||||
|
||||
});
|
||||
let renderFrameCount = 0;
|
||||
this._renderObserver = DefaultScene.MainScene.onAfterRenderObservable.add(() => {
|
||||
// Update voice audio system (checks for completed sounds and plays next in queue)
|
||||
if (this._voiceAudio) {
|
||||
this._voiceAudio.update();
|
||||
}
|
||||
// Check game end conditions every frame (but only acts once)
|
||||
this.checkGameEndConditions();
|
||||
// Check game end conditions every 30 frames (~0.5 sec at 60fps)
|
||||
if (renderFrameCount++ % 30 === 0) {
|
||||
this.checkGameEndConditions();
|
||||
}
|
||||
});
|
||||
|
||||
// Setup camera
|
||||
|
||||
@ -189,6 +189,8 @@ export class Scoreboard {
|
||||
advancedTexture.addControl(panel);
|
||||
let i = 0;
|
||||
const _afterRender = scene.onAfterRenderObservable.add(() => {
|
||||
if (i++ % 10 !== 0) return;
|
||||
|
||||
scoreText.text = `Score: ${this.calculateScore()}`;
|
||||
remainingText.text = `Remaining: ${this._remaining}`;
|
||||
|
||||
@ -201,7 +203,7 @@ export class Scoreboard {
|
||||
}
|
||||
|
||||
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")}`;
|
||||
fpsText.text = `FPS: ${Math.floor(scene.getEngine().getFps())}`;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user