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