]> git.r.bdr.sh - rbdr/sumo/blob - lib/sumo.js
Use Parcel (#5)
[rbdr/sumo] / lib / sumo.js
1 import 'babel-polyfill';
2 import PhysicsWorldControlSystem from './systems/physics_world_control';
3 import PhysicsToAttributesSystem from './systems/physics_to_attributes';
4 import CreateCouplingLineSystem from './systems/create_coupling_line';
5 import RenderSystem from './systems/render';
6 import AttributesToRenderableSystem from './systems/attributes_to_renderable';
7 import SumoFactory from './factories/sumo';
8 import Serpentity from '@serpentity/serpentity';
9 import { Application } from 'pixi.js';
10 import { Engine } from 'matter-js';
11
12 /* global window document */
13
14 const internals = {
15 kBackgroundColor: 0xd8c590,
16 kNoElementError: 'No element found. Cannot render.',
17
18 // Handler for the window load event. Initializes and runs the app.
19
20 onLoad() {
21
22 const sumo = new internals.Sumo({
23 element: document.getElementById('sumo-app-entry-point')
24 });
25
26 sumo.startLoop();
27
28 internals.exports.sumo = sumo;
29 }
30 };
31
32 /**
33 * Sumo - main entry point. Attached to window->load
34 *
35 * @class Sumo
36 *
37 * @param {object} config the configuration to extend the object
38 *
39 * @property {HTMLElement} [element=null] the element in which to render.
40 * Required, will throw if not provided
41 * @property {Number} [fps=60] the fps target to maintain
42 * @property {Number} [verticalResolution=224] how many pixels to render in the vertical
43 * axis (gets scaled if the canvas is larger)
44 * @property {Array<Number>} [aspectRatio=[2.76, 1]] the aspect ratio experssed as
45 * an array of two numbers, where aspect ratio x:y is [x, y] (eg. [16, 9])
46 */
47
48 internals.Sumo = class Sumo {
49
50 constructor(config) {
51
52 this.fps = 60;
53 this.aspectRatio = [2.76, 1];
54 this.verticalResolution = 224;
55
56 Object.assign(this, config);
57
58 if (!this.element) {
59 throw new Error(internals.kNoElementError);
60 }
61
62 this._engine = new Serpentity();
63
64 this._previousTime = 0;
65 this._looping = false;
66
67 // Initialization functions
68 this._initializeCanvas();
69 this._initializeMatter();
70 this._initializePixi();
71 this._initializeSystems();
72 this._initializeEntities();
73 }
74
75 /**
76 * Starts the main loop. Resets the FPS (if you change it it won't go
77 * live until after you stop and start the loop)
78 *
79 * @function startLoop
80 * @instance
81 * @memberof Sumo
82 */
83 startLoop() {
84
85 this._looping = true;
86 this._frameDuration = 1000 / this.fps;
87 window.requestAnimationFrame(this._loop.bind(this));
88 }
89
90 /**
91 * Pauses the loop
92 *
93 * @function pauseLoop
94 * @instance
95 * @memberof Sumo
96 */
97 pauseLoop() {
98
99 this._looping = false;
100 }
101
102 // The main loop used above. Runs the serpentity update process and
103 // attempts to maintain FPS. The rest is handled by the engine.
104
105 _loop(currentTime) {
106
107 if (!this._looping) {
108 return;
109 }
110
111 window.requestAnimationFrame(this._loop.bind(this));
112
113 const currentFrameDuration = currentTime - this._previousTime;
114
115 if (currentFrameDuration > this._frameDuration) {
116
117 // We're sending the currentTime since it gives better results for
118 // this type of renderer, though usually we expect the delta
119 this._engine.update(currentTime);
120 this._previousTime = currentTime;
121 }
122 }
123
124 // Creates a canvas for rendering
125
126 _initializeCanvas() {
127
128 this._canvas = document.createElement('canvas');
129 this.element.appendChild(this._canvas);
130 this._resizeCanvas();
131 window.addEventListener('resize', this._resizeCanvas.bind(this));
132 }
133
134 // Initialize MatterJs
135
136 _initializeMatter() {
137
138 this._matterJs = Engine.create();
139
140 this._matterJs.world.gravity.y = 0;
141 }
142
143 // Initialize Pixi
144
145 _initializePixi() {
146
147 this._pixi = new Application({
148 backgroundColor: internals.kBackgroundColor,
149 view: this._canvas,
150 width: this._canvas.width,
151 height: this._canvas.height
152 });
153 }
154
155 // Resizes the canvas to a square the size of the smallest magnitude
156 // of the window.
157
158 _resizeCanvas() {
159
160 let width = window.innerWidth;
161 let height = Math.round(width * this.aspectRatio[1] / this.aspectRatio[0]);
162
163 if (window.innerHeight < height) {
164 height = window.innerHeight;
165 width = Math.round(height * this.aspectRatio[0] / this.aspectRatio[1]);
166 }
167
168 this._canvas.style.width = `${width}px`;
169 this._canvas.style.height = `${height}px`;
170
171 this._canvas.width = Math.round(this.verticalResolution * this.aspectRatio[0] / this.aspectRatio[1]);
172 this._canvas.height = this.verticalResolution;
173 }
174
175 // Initializes the serpentity systems
176
177 _initializeSystems() {
178
179 this._engine.addSystem(new PhysicsWorldControlSystem({
180 engine: this._matterJs
181 }));
182
183 this._engine.addSystem(new PhysicsToAttributesSystem());
184
185 this._engine.addSystem(new AttributesToRenderableSystem());
186
187 this._engine.addSystem(new CreateCouplingLineSystem());
188
189 this._engine.addSystem(new RenderSystem({
190 application: this._pixi
191 }));
192 }
193
194 // Initializes the serpentity entities
195
196 _initializeEntities() {
197
198 const entityA = SumoFactory.createSumo(null, {
199 position: {
200 x: 50,
201 y: 50
202 }
203 });
204
205 const entityB = SumoFactory.createSumo(null, {
206 position: {
207 x: 309,
208 y: 112
209 }
210 });
211
212 const entityC = SumoFactory.createSumo(null, {
213 position: {
214 x: 500,
215 y: 78
216 }
217 });
218
219 SumoFactory.createRubberBand(this._engine, {
220 entityA,
221 entityB
222 });
223
224 SumoFactory.createRubberBand(this._engine, {
225 entityA: entityC,
226 entityB
227 });
228
229 // To keep the coupling behind, we'll manually add the sumos later
230
231 this._engine.addEntity(entityA);
232 this._engine.addEntity(entityB);
233 this._engine.addEntity(entityC);
234 }
235 };
236
237 export default internals.exports = {};
238
239 // autorun.bat
240 window.addEventListener('load', internals.onLoad);