]> git.r.bdr.sh - rbdr/sumo/blob - lib/factories/sumo.js
Fix positioning and lessen friction (#8)
[rbdr/sumo] / lib / factories / sumo.js
1 import { Bodies, Constraint } from 'matter-js';
2 import { Entity } from '@serpentity/serpentity';
3
4 // Components
5
6 import AngleComponent from '../components/angle';
7 import BodyComponent from '../components/body';
8 import ControlMapComponent from '../components/control_map';
9 import CoupledEntitiesComponent from '../components/coupled_entities';
10 import DashComponent from '../components/dash';
11 import ElasticComponent from '../components/elastic';
12 import ForceComponent from '../components/force';
13 import MaxVelocityComponent from '../components/max_velocity';
14 import PositionComponent from '@serpentity/components.position';
15 import PixiContainerComponent from '../components/pixi_container';
16
17 import PixiFactory from '../factories/pixi';
18 import Config from '../config';
19
20 const internals = {
21 kNoEntityError: 'Entity Not Found: This method requires entityA and entityB to be set in the config.',
22 kEntityHasNoBodyError: 'Entity Has No Body: This method requires entities have a BodyComponent.'
23 };
24
25 /**
26 * Factory object that contains many methods to create prefab entities.
27 *
28 * @type object
29 * @name SumoFactory
30 */
31 export default {
32
33 /**
34 * Creates a sumo entity and adds it to the engine. Can override
35 * position in the config object
36 *
37 * @function createSumo
38 * @memberof SumoFactory
39 * @param {external:Serpentity} [engine] the serpentity engine to attach
40 * to. If not sent, it will not be attached.
41 * @param {object} [config] the config to override the entity, accepts
42 * the key `position` as an object with an x and y property.
43 * @return {external:Serpentity.Entity} the created entity
44 */
45 createSumo(engine, config = {}) {
46
47 const entity = new Entity();
48
49 // POSITION
50
51 entity.addComponent(new PositionComponent(config.position));
52 const position = entity.getComponent(PositionComponent);
53
54 entity.addComponent(new AngleComponent(config.angle));
55 const angle = entity.getComponent(AngleComponent);
56
57 entity.addComponent(new ForceComponent(config.force));
58
59 config.maxVelocity = {
60 maxVelocity: 12
61 };
62 entity.addComponent(new MaxVelocityComponent(config.maxVelocity));
63
64 // CONTROLS & ABILITIES
65
66 entity.addComponent(new DashComponent(config.dash));
67
68 // RENDERING
69
70 const radius = 25;
71
72 const container = config.container || {
73 container: PixiFactory.createSumo({ radius })
74 };
75 container.container.position.x = position.x;
76 container.container.position.y = position.y;
77 container.container.rotation = angle.angle;
78 entity.addComponent(new PixiContainerComponent(container));
79
80 // PHYSICS
81
82 const frictionAir = 0.02;
83 const friction = 0.01;
84 const frictionStatic = 0.01;
85 const restitution = 1;
86 const density = 1.5;
87
88 const body = Bodies.circle(position.x / Config.meterSize, position.y / Config.meterSize, radius / Config.meterSize, {
89 label: 'Sumo body',
90 angle: angle.angle,
91 density,
92 frictionAir,
93 frictionStatic,
94 friction,
95 restitution
96 });
97 entity.addComponent(new BodyComponent({ body }));
98
99 if (engine) {
100 engine.addEntity(entity);
101 }
102
103 return entity;
104 },
105
106 /**
107 * Creates a rubber band entity and adds it to the engine.
108 *
109 * @function createRubberBand
110 * @memberof SumoFactory
111 * @param {external:Serpentity} [engine] the serpentity engine to attach
112 * to. If not sent, it will not be attached.
113 * @param {object} [config] the config to override the entity, it
114 * must include entityA and entityB which will be tied by it. If they
115 * are not sent or don't have a physics body, this will throw.
116 * @return {external:Serpentity.Entity} the created entity
117 */
118 createRubberBand(engine, config = {}) {
119
120 const entity = new Entity();
121
122 if (!config.entityA || !config.entityB) {
123 throw new Error(internals.kNoEntityError);
124 }
125
126 if (!config.entityA.hasComponent(BodyComponent) || !config.entityB.hasComponent(BodyComponent)) {
127 throw new Error(internals.kEntityHasNoBodyError);
128 }
129
130 // RENDERING
131
132 const container = config.container || {
133 container: PixiFactory.createEmptyGraphic()
134 };
135 entity.addComponent(new PixiContainerComponent(container));
136
137 // PHYSICS
138
139 const bodyA = config.entityA.getComponent(BodyComponent).body;
140 const bodyB = config.entityB.getComponent(BodyComponent).body;
141 const damping = 0;
142 const length = 100 / Config.meterSize;
143 const stiffness = 0.001;
144
145 const body = Constraint.create({
146 bodyA,
147 bodyB,
148 damping,
149 length,
150 stiffness
151 });
152 entity.addComponent(new BodyComponent({ body }));
153
154 entity.addComponent(new CoupledEntitiesComponent({
155 coupledEntities: [config.entityA, config.entityB]
156 }));
157
158 entity.addComponent(new ElasticComponent());
159
160 if (engine) {
161 engine.addEntity(entity);
162 }
163
164 return entity;
165 },
166
167 /**
168 * Creates a controllable sumo entity and adds it to the engine. Can override
169 * position in the config object
170 *
171 * @function createControllableSumo
172 * @memberof SumoFactory
173 * @param {external:Serpentity} [engine] the serpentity engine to attach
174 * to. If not sent, it will not be attached.
175 * @param {object} [config] the config to override the entity, accepts
176 * the key `position` as an object with an x and y property.
177 * @return {external:Serpentity.Entity} the created entity
178 */
179 createControllableSumo(engine, config = {}) {
180
181 const entity = this.createSumo(null, config);
182
183 entity.addComponent(new ControlMapComponent({
184 map: [
185 {
186 source: {
187 type: 'keyboard',
188 index: 37 // left arrow
189 },
190 target: {
191 component: ForceComponent,
192 property: 'x',
193 value: (value) => -Number(value)
194 }
195 },
196 {
197 source: {
198 type: 'keyboard',
199 index: 39 // right arrow
200 },
201 target: {
202 component: ForceComponent,
203 property: 'x',
204 value: (value) => Number(value)
205 }
206 },
207 {
208 source: {
209 type: 'keyboard',
210 index: 38 // up arrow
211 },
212 target: {
213 component: ForceComponent,
214 property: 'y',
215 value: (value) => -Number(value)
216 }
217 },
218 {
219 source: {
220 type: 'keyboard',
221 index: 40 // down arrow
222 },
223 target: {
224 component: ForceComponent,
225 property: 'y',
226 value: (value) => Number(value)
227 }
228 },
229 {
230 source: {
231 type: 'keyboard',
232 index: 90 // Z
233 },
234 target: {
235 component: DashComponent,
236 property: 'dashing'
237 }
238 }
239 ]
240 }));
241
242 if (engine) {
243 engine.addEntity(entity);
244 }
245
246 return entity;
247 },
248
249 /**
250 * Creates a static harness entity
251 *
252 * @function createHarness
253 * @memberof SumoFactory
254 * @param {external:Serpentity} [engine] the serpentity engine to attach
255 * to. If not sent, it will not be attached.
256 * @param {object} [config] the config to override the entity, accepts
257 * the key `position` as an object with an x and y property.
258 * @return {external:Serpentity.Entity} the created entity
259 */
260 createHarness(engine, config = {}) {
261
262 const entity = new Entity();
263
264 // POSITION
265
266 entity.addComponent(new PositionComponent(config.position));
267 const position = entity.getComponent(PositionComponent);
268
269 // RENDERING
270
271 const radius = 15;
272
273 const container = config.container || {
274 container: PixiFactory.createHarness({ radius })
275 };
276 container.container.position.x = position.x;
277 container.container.position.y = position.y;
278 entity.addComponent(new PixiContainerComponent(container));
279
280 // PHYSICS
281
282 const friction = 0;
283 const frictionStatic = 0;
284 const restitution = 1;
285
286 const body = Bodies.circle(position.x / Config.meterSize, position.y / Config.meterSize, radius / Config.meterSize, {
287 isStatic: true,
288 label: 'Harness body',
289 friction,
290 restitution,
291 frictionStatic
292 });
293 entity.addComponent(new BodyComponent({ body }));
294
295 if (engine) {
296 engine.addEntity(entity);
297 }
298
299 return entity;
300 },
301
302 /**
303 * Creates a static arena entity
304 *
305 * @function createArena
306 * @memberof SumoFactory
307 * @param {external:Serpentity} [engine] the serpentity engine to attach
308 * to. If not sent, it will not be attached.
309 * @param {object} [config] the config to override the entity, accepts
310 * the key `position` as an object with an x and y property.
311 * @return {external:Serpentity.Entity} the created entity
312 */
313 createArena(engine, config = {}) {
314
315 const entity = new Entity();
316
317 // POSITION
318
319 entity.addComponent(new PositionComponent(config.position));
320 const position = entity.getComponent(PositionComponent);
321
322 // RENDERING
323
324 const radius = 300;
325
326 const container = config.container || {
327 container: PixiFactory.createArena({ radius })
328 };
329 container.container.position.x = position.x;
330 container.container.position.y = position.y;
331 entity.addComponent(new PixiContainerComponent(container));
332
333 if (engine) {
334 engine.addEntity(entity);
335 }
336
337 return entity;
338 },
339
340 /**
341 * Creates an invisible block
342 *
343 * @function createInvisibleBlock
344 * @memberof SumoFactory
345 * @param {external:Serpentity} [engine] the serpentity engine to attach
346 * to. If not sent, it will not be attached.
347 * @param {object} [config] the config to override the entity, accepts
348 * the key `position` as an object with an x and y property.
349 * @return {external:Serpentity.Entity} the created entity
350 */
351 createInvisibleBlock(engine, config = {}) {
352
353 const entity = new Entity();
354
355 // POSITION
356
357 entity.addComponent(new PositionComponent(config.position));
358 const position = entity.getComponent(PositionComponent);
359
360 // PHYSICS
361
362 const friction = 0;
363 const frictionStatic = 0;
364 const restitution = 1;
365
366 const body = Bodies.rectangle(position.x / Config.meterSize,
367 position.y / Config.meterSize,
368 config.width / Config.meterSize,
369 config.height / Config.meterSize,
370 {
371 isStatic: true,
372 label: 'Invisible Block',
373 friction,
374 restitution,
375 frictionStatic
376 });
377 entity.addComponent(new BodyComponent({ body }));
378
379 if (engine) {
380 engine.addEntity(entity);
381 }
382
383 return entity;
384 }
385 };