import { Application, Text, TextStyle } from 'pixi.js' import { Time } from './Time' import { EntityManager } from './EntityManager' import { eventBus } from './EventBus' import { getApp, layers } from '@/game/rendering/PixiRoot' import { LevelScene } from '@/game/LevelScene' import type { LevelDef } from '@/data/levels' export class GameEngine { private readonly _app: Application readonly entities = new EntityManager() private _running = false private _lastTime = 0 private _frameCount = 0 private _fpsTime = 0 private _fpsText: Text | null = null private _showFps = false private _scene: LevelScene | null = null private _canvas: HTMLElement | null = null constructor(app: Application) { this._app = app } loadLevel(def: LevelDef, canvas: HTMLElement): void { this._scene?.destroy() this._canvas = canvas this._scene = new LevelScene(def) this._scene.attachInput(canvas) } getScene(): LevelScene | null { return this._scene } start(): void { this._running = true this._lastTime = performance.now() this._fpsTime = performance.now() this._app.ticker.add(this._tick, this) this._setupFpsCounter() } stop(): void { this._running = false this._app.ticker.remove(this._tick, this) } setShowFps(show: boolean): void { this._showFps = show if (this._fpsText) this._fpsText.visible = show } private _tick(): void { if (!this._running) return const now = performance.now() Time.delta = Math.min((now - this._lastTime) / 1000, 0.05) Time.elapsed += Time.delta this._lastTime = now this._scene?.update(Time.delta) this._updateFps(now) } private _setupFpsCounter(): void { const style = new TextStyle({ fill: 'rgba(201, 161, 74, 0.7)', fontSize: 12, fontFamily: 'monospace', }) this._fpsText = new Text({ text: 'FPS: --', style }) this._fpsText.visible = this._showFps this._fpsText.x = 8 this._fpsText.y = 8 this._fpsText.zIndex = 9999 layers?.ui.addChild(this._fpsText) } private _updateFps(now: number): void { this._frameCount++ if (now - this._fpsTime >= 500) { const fps = Math.round((this._frameCount * 1000) / (now - this._fpsTime)) if (this._fpsText) this._fpsText.text = `FPS: ${fps}` this._frameCount = 0 this._fpsTime = now } } destroy(): void { if (this._canvas && this._scene) { this._scene.detachInput(this._canvas) } this._scene?.destroy() this._scene = null this.stop() this.entities.clear() eventBus.clear() this._fpsText?.destroy() this._fpsText = null } } let _engine: GameEngine | null = null export function createEngine(): GameEngine { _engine?.destroy() _engine = new GameEngine(getApp()) return _engine } export function getEngine(): GameEngine | null { return _engine }