import { HandLandmarker, FilesetResolver } from '@mediapipe/tasks-vision';
import { Player } from './entities/player';
import { ObstacleField } from './entities/obstacle';
import { AudioBus } from './core/audio';
export interface GameConfig {
gravity: number;
glideGravity: number;
jumpVelocity: number;
}
export const CONFIG: GameConfig = {
gravity: 1500,
glideGravity: 330,
jumpVelocity: -440,
};
export class Game {
private player = new Player();
private field = new ObstacleField();
private score = 0;
private last = 0;
constructor(private readonly canvas: HTMLCanvasElement) {}
async boot(): Promise<void> {
try {
const fileset = await FilesetResolver.forVisionTasks(WASM_ROOT);
this.hands = await HandLandmarker.createFromOptions(fileset, opts);
} catch (e) {
// 摄像头或模型不可用时静默降级,保留键鼠与触屏
this.input.disableGesture();
}
requestAnimationFrame(this.tick);
}
private tick = (t: number): void => {
const dt = Math.min((t - this.last) / 1000, 0.05);
this.last = t;
this.player.integrate(dt, CONFIG);
this.field.update(dt, this.score);
this.score += this.field.collect(this.player);
if (this.field.hit(this.player)) this.over();
requestAnimationFrame(this.tick);
};
}