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