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