module Omni { /** * Base game class, will handle the game loop, rendering, */ export class Game implements Tickable { private renderer:THREE.WebGLRenderer; private camera:THREE.PerspectiveCamera; private blockLoader:BlockLoader; private cube:Cube; private ticks:number = 0; private delta:number = 0; private lastFrame:number = 0; private timestep:number = 1000 / 60; private maxFPS:number = 60; private keepRunning:boolean; static CAMERA_FOV:number = 55; static CAMERA_NEAR:number = 1; static CAMERA_FAR:number = 1000; /** * */ constructor() { this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.renderer.setClearColor(0xbababa); this.renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(this.renderer.domElement); window.addEventListener("resize", this.onWindowResize, false); window.addEventListener("visibilitychange", this.onVisibilityChange, false); this.camera = new THREE.PerspectiveCamera(Game.CAMERA_FOV, window.innerWidth / window.innerHeight, Game.CAMERA_NEAR, Game.CAMERA_FAR); this.blockLoader = new BlockLoader(BlockLoader.BLOCKS); } /** * */ init():void { console.debug("Game: init"); this.blockLoader.load(); } /** * */ start = ():void => { console.debug("Game: start"); //wait around till block_loader is done loading assets if (!this.blockLoader.isLoaded()) { setTimeout(this.start, 10); return; } //build the level here this.cube.init(0); this.unpause(); }; /** * */ unpause():void { console.debug("Game: unpause"); this.keepRunning = true; this.run(); } /** * * @param delta */ tick(delta:number):void { this.ticks++; this.cube.tick(delta); } /** * */ render():void { this.renderer.render(this.cube, this.camera); } /** * * @param timestamp */ run(timestamp?:number):void { if (!timestamp) { timestamp = performance.now(); } if (timestamp < this.lastFrame + (1000 / this.maxFPS)) { if (this.keepRunning) { requestAnimationFrame(() => this.run()); } return; } this.delta += timestamp - this.lastFrame; this.lastFrame = timestamp; var numUpdateSteps = 0; while (this.delta >= this.timestep) { this.tick(this.timestep); this.delta -= this.timestep; if (++numUpdateSteps >= 240) { // panic here, reset delta this.delta = 0; break; } } this.render(); if (this.keepRunning) { requestAnimationFrame((time) => this.run(time)); } } /** * When releasing pointer lock/on menu. Menu is HTML based. */ pause():void { console.debug("Game: pause"); this.keepRunning = false; } /** * When leaving the page. */ stop = ():void => { this.pause(); console.debug("Game: stop"); this.blockLoader.dispose(); this.renderer.dispose(); }; onWindowResize = () => { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); }; onVisibilityChange = () => { console.debug("visibility change"); }; } } let game:Omni.Game; let locker:Omni.PointerLocker; window.onload = () => { game = new Omni.Game(); locker = new Omni.PointerLocker(game.start, game.stop); game.init(); locker.enable(); }; window.onunload = () => { locker.disable(); game.stop(); };