import 'babel-polyfill'; import Config from './config'; // Systems import ApplyForceSystem from './systems/apply_force'; import CreateCouplingLineSystem from './systems/create_coupling_line'; import ControlMapperSystem from './systems/control_mapper'; import DashSystem from './systems/dash'; import DetectPointsCollisionSystem from './systems/detect_points_collision'; import DetectWinnerSystem from './systems/detect_winner'; import DrawDashSystem from './systems/draw_dash'; import DrawGrabSystem from './systems/draw_grab'; import ElasticSystem from './systems/elastic'; import GrabSystem from './systems/grab'; import PhysicsWorldControlSystem from './systems/physics_world_control'; import PhysicsToAttributesSystem from './systems/physics_to_attributes'; import RenderPointsSystem from './systems/render_points'; import RenderSystem from './systems/render'; import RenderWinnerSystem from './systems/render_winner'; import AttributesToRenderableSystem from './systems/attributes_to_renderable'; // Factories import SumoFactory from './factories/sumo'; // External Dependencies import Serpentity from '@serpentity/serpentity'; import { Application } from 'pixi.js'; import { Engine } from 'matter-js'; const internals = { kBackgroundColor: 0xd8c590, kNoElementError: 'No element found. Cannot render.', // Handler for the window load event. Initializes and runs the app. onLoad() { const sumo = new internals.Sumo(Object.assign({ element: document.getElementById('sumo-app-entry-point') }, Config)); sumo.startLoop(); window.sumo = sumo; } }; /** * Sumo - main entry point. Attached to window->load * * @class Sumo * * @param {object} config the configuration to extend the object * * @property {HTMLElement} [element=null] the element in which to render. * Required, will throw if not provided * @property {Number} [fps=60] the fps target to maintain * @property {Number} [verticalResolution=224] how many pixels to render in the vertical * axis (gets scaled if the canvas is larger) * @property {Array} [aspectRatio=[2.76, 1]] the aspect ratio experssed as * an array of two numbers, where aspect ratio x:y is [x, y] (eg. [16, 9]) */ internals.Sumo = class Sumo { constructor(config) { // These defaults can get overridden by config this.fps = 60; this.aspectRatio = [2.76, 1]; this.verticalResolution = 224; Object.assign(this, config); if (!this.element) { throw new Error(internals.kNoElementError); } this._engine = new Serpentity(); this._previousTime = 0; this._looping = false; // Initialization functions this._initializeCanvas(); this._initializeMatter(); this._initializePixi(); this._initializeSystems(); this._initializeEntities(); } /** * Starts the main loop. Resets the FPS (if you change it it won't go * live until after you stop and start the loop) * * @function startLoop * @instance * @memberof Sumo */ startLoop() { this._looping = true; this._frameDuration = 1000 / this.fps; window.requestAnimationFrame(this._loop.bind(this)); } /** * Pauses the loop * * @function pauseLoop * @instance * @memberof Sumo */ pauseLoop() { this._looping = false; } // The main loop used above. Runs the serpentity update process and // attempts to maintain FPS. The rest is handled by the engine. _loop(currentTime) { if (!this._looping) { return; } window.requestAnimationFrame(this._loop.bind(this)); const currentFrameDuration = currentTime - this._previousTime; if (currentFrameDuration > this._frameDuration) { // We're sending the currentTime since it gives better results for // this type of renderer, though usually we expect the delta this._engine.update(currentFrameDuration); this._previousTime = currentTime; } } // Creates a canvas for rendering _initializeCanvas() { this._canvas = document.createElement('canvas'); this.element.appendChild(this._canvas); this._resizeCanvas(); window.addEventListener('resize', this._resizeCanvas.bind(this)); } // Initialize MatterJs _initializeMatter() { this._matterJs = Engine.create(); this._matterJs.world.gravity.y = 0; } // Initialize Pixi _initializePixi() { this._pixi = new Application({ backgroundColor: internals.kBackgroundColor, view: this._canvas, width: this._canvas.width, height: this._canvas.height }); } // Resizes the canvas to a square the size of the smallest magnitude // of the window. _resizeCanvas() { let width = window.innerWidth; let height = Math.round(width * this.aspectRatio[1] / this.aspectRatio[0]); if (window.innerHeight < height) { height = window.innerHeight; width = Math.round(height * this.aspectRatio[0] / this.aspectRatio[1]); } this._canvas.style.width = `${width}px`; this._canvas.style.height = `${height}px`; this._canvas.width = Math.round(this.verticalResolution * this.aspectRatio[0] / this.aspectRatio[1]); this._canvas.height = this.verticalResolution; } // Initializes the serpentity systems _initializeSystems() { this._engine.addSystem(new ControlMapperSystem()); this._engine.addSystem(new DashSystem()); this._engine.addSystem(new GrabSystem({ engine: this._matterJs })); this._engine.addSystem(new ApplyForceSystem()); this._engine.addSystem(new PhysicsWorldControlSystem({ engine: this._matterJs })); this._engine.addSystem(new DetectPointsCollisionSystem()); this._engine.addSystem(new DetectWinnerSystem()); this._engine.addSystem(new RenderPointsSystem({ application: this._pixi })); this._engine.addSystem(new RenderWinnerSystem({ application: this._pixi })); this._engine.addSystem(new ElasticSystem()); this._engine.addSystem(new PhysicsToAttributesSystem()); this._engine.addSystem(new AttributesToRenderableSystem()); this._engine.addSystem(new CreateCouplingLineSystem()); this._engine.addSystem(new DrawDashSystem()); this._engine.addSystem(new DrawGrabSystem()); this._engine.addSystem(new RenderSystem({ application: this._pixi })); } // Initializes the serpentity entities _initializeEntities() { SumoFactory.createArena(this._engine, { position: { x: this.horizontalResolution / 2, y: this.verticalResolution / 2 } }); const sumoA = SumoFactory.createPlayer1Sumo(null, { position: { x: this.horizontalResolution / 2 - 100, y: this.verticalResolution / 2 } }); const sumoB = SumoFactory.createPlayer2Sumo(null, { position: { x: this.horizontalResolution / 2 + 100, y: this.verticalResolution / 2 } }); const harness = SumoFactory.createHarness(null, { position: { x: this.horizontalResolution / 2, y: this.verticalResolution / 2 } }); SumoFactory.createRubberBand(this._engine, { entityA: sumoA, entityB: harness }); SumoFactory.createRubberBand(this._engine, { entityA: sumoB, entityB: harness }); // Walls SumoFactory.createInvisibleBlock(this._engine, { width: this.horizontalResolution * 2, height: this.verticalResolution * 0.1, position: { x: this.horizontalResolution / 2, y: -this.verticalResolution * 0.1 } }); SumoFactory.createInvisibleBlock(this._engine, { width: this.horizontalResolution * 2, height: this.verticalResolution * 0.1, position: { x: this.horizontalResolution / 2, y: this.verticalResolution + this.verticalResolution * 0.1 } }); // Points Detector SumoFactory.createPointsCollider(this._engine, { collisionTarget: sumoA, pointsTarget: 'red', height: this.verticalResolution, width: this.horizontalResolution, position: { x: this.horizontalResolution + this.horizontalResolution / 2, y: this.verticalResolution / 2 } }); SumoFactory.createPointsCollider(this._engine, { collisionTarget: sumoB, pointsTarget: 'blue', height: this.verticalResolution, width: this.horizontalResolution, position: { x: -this.horizontalResolution / 2, y: this.verticalResolution / 2 } }); // The game state SumoFactory.createGameState(this._engine); // To keep the coupling behind, we'll manually add the sumos later this._engine.addEntity(sumoA); this._engine.addEntity(sumoB); this._engine.addEntity(harness); } }; export default internals.exports = {}; // autorun.bat window.addEventListener('load', internals.onLoad);