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