import {h} from "preact" import * as preact from "preact" import * as cannon from "cannon" import * as babylon from "babylonjs" import {GameOptions, GameContext} from "./game-interfaces" import {Ticker} from "../ticker" import {Manager} from "../manager" import {Viewport} from "../viewport" import {Conductor} from "../conductor" import {Overlay} from "../overlay/components/overlay" import {Service} from "../toolbox/toolbox-interfaces" import {OverlayStore} from "../overlay/stores/overlay-store" import {StatisticsStore} from "../overlay/stores/statistics-store" /** * Standard monarch game */ export class Game implements Service { readonly manager: Manager private readonly viewport: Viewport private readonly logicTicker: Ticker constructor(options: GameOptions) { const {overlayElement, canvas, mode, entityClasses, gravity} = options // babylon engine as the foundation const engine = new babylon.Engine(canvas, true, undefined, true) const scene = new babylon.Scene(engine) // cannon physics enabled const physicsPlugin = new babylon.CannonJSPlugin() const physicsWorld: cannon.World = physicsPlugin.world physicsWorld.allowSleep = true physicsWorld.broadphase = new cannon.SAPBroadphase(physicsWorld) scene.enablePhysics(babylon.Vector3.FromArray(gravity), physicsPlugin) // viewport handles render loop and pointer lock const viewport = this.viewport = new Viewport({ window, canvas, scene, engine }) // 2d overlay const statisticsStore = new StatisticsStore() const overlayStore = new OverlayStore({statisticsStore}) const {mainMenuStore} = overlayStore preact.render( , undefined, overlayElement ) // conductor keeps gamestate and entity replication const conductor = new Conductor({ mode, entityClasses, context: { scene, window, canvas, engine, physicsWorld, overlayStore, mainMenuStore } }) // make the logic ticker this.logicTicker = this.makeLogicTicker({ viewport, conductor, statisticsStore, durationBetweenTicks: 0, timeBetweenStatRecordings: 0 }) // expose the state manager this.manager = conductor.manager } start() { this.logicTicker.start() this.viewport.start() } stop() { this.logicTicker.stop() this.viewport.stop() } destructor() { this.logicTicker.destructor() this.viewport.destructor() } private makeLogicTicker({ viewport, conductor, statisticsStore, durationBetweenTicks, timeBetweenStatRecordings }: { viewport: Viewport conductor: Conductor statisticsStore: StatisticsStore durationBetweenTicks?: number timeBetweenStatRecordings?: number }) { let statmark = 0 let ticker return ticker = new Ticker({ tickAction: tick => { conductor.logic(tick) if (ticker && tick.timeline - statmark > timeBetweenStatRecordings) { statisticsStore.recordTickerStats(ticker) statisticsStore.recordViewportStats(viewport) statmark = tick.timeline } }, durationBetweenTicks }) } }