Files
naddie-jump/src/main.js
AnRil fc1f12bb7e Phase A: stability and performance
A1 Pooled particle system
- New ParticleManager owns 9 reusable Phaser emitters created once per scene
  (jump, explosion, powerup, puff, sparks, flame, wind, spring, speedline)
- BootScene generates reusable white textures (px_square/soft/streak/ring)
- GameScene burst helpers + EffectsManager flow effects now delegate to the
  pool instead of allocating rectangles + tweens every frame
- Quality auto-detect (low on mobile/small screens) cuts particle counts

A2 Fix high-speed landing tunneling
- Cap downward velocity at PHYSICS.maxFallSpeed (boosts unaffected)
- platformCollisionFilter now uses a swept deltaY one-way check so a fast
  fall can never be wrongly rejected or passed through

A3 Safe storage
- New utils/storage.js wraps localStorage with in-memory fallback so private
  mode / quota errors cannot crash the game; all access routed through it

A4-A6 Lifecycle and robustness
- Global error / unhandledrejection guard recovers to the menu
- GameScene auto-pauses on tab hidden and cleans up the cross-scene listener
  on shutdown; fps pacing + disableContextMenu in game config
- Moving platforms use a proper dynamic body instead of destroy()+new Body

Verified in-browser: menu + game load with zero console errors, all emitters
active, normal bouncing works, fall speed capped.
2026-05-29 13:10:08 +07:00

55 lines
1.4 KiB
JavaScript

import { Game, AUTO, Scale } from 'phaser';
import { BootScene } from './scenes/BootScene.js';
import { MenuScene } from './scenes/MenuScene.js';
import { GameScene } from './scenes/GameScene.js';
import { GameOverScene } from './scenes/GameOverScene.js';
import { GAME_WIDTH, GAME_HEIGHT, GRAVITY } from './config/game.config.js';
const config = {
type: AUTO,
parent: 'game-container',
width: GAME_WIDTH,
height: GAME_HEIGHT,
backgroundColor: '#0d001a',
scale: {
mode: Scale.FIT,
autoCenter: Scale.CENTER_BOTH,
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: GRAVITY },
debug: false,
},
},
fps: {
target: 60,
min: 30,
},
disableContextMenu: true,
scene: [BootScene, MenuScene, GameScene, GameOverScene],
pixelArt: false,
antialias: true,
};
const game = new Game(config);
window.game = game;
// Global crash guard: if an uncaught error happens during gameplay, recover to
// the menu instead of leaving a frozen canvas.
function recoverToMenu() {
try {
if (!game || !game.scene) return;
const inGame = game.scene.isActive('GameScene');
if (inGame) {
game.scene.stop('GameScene');
game.scene.start('MenuScene');
}
} catch (_) {
/* swallow — last-resort guard */
}
}
window.addEventListener('error', recoverToMenu);
window.addEventListener('unhandledrejection', recoverToMenu);