littlejsengine 1.9.8 → 1.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/littlejs.d.ts +38 -5
- package/dist/littlejs.esm.js +106 -58
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +98 -56
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +81 -54
- package/examples/box2d/game.js +34 -16
- package/examples/box2d/gameObjects.js +485 -0
- package/examples/box2d/index.html +6 -18
- package/examples/box2d/scenes.js +32 -251
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/electron/build.js +1 -1
- package/examples/electron/index.html +2 -2
- package/examples/js13k/build.js +19 -1
- package/examples/js13k/index.html +13 -13
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +1 -1
- package/examples/platformer/gameCharacter.js +1 -0
- package/examples/platformer/gameEffects.js +0 -2
- package/examples/platformer/gameObjects.js +1 -1
- package/examples/platformer/index.html +8 -8
- package/examples/puzzle/index.html +2 -2
- package/examples/starter/build.js +1 -1
- package/examples/starter/game.js +1 -1
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +1 -1
- package/examples/typescript/index.html +1 -1
- package/package.json +1 -1
- package/plugins/box2d.js +126 -47
- package/plugins/postProcess.js +3 -2
- package/src/engine.js +12 -11
- package/src/engineAudio.js +16 -16
- package/src/engineBuild.js +1 -1
- package/src/engineDebug.js +17 -2
- package/src/engineExport.js +7 -2
- package/src/engineInput.js +13 -6
- package/src/engineMedals.js +3 -2
- package/src/engineObject.js +25 -19
- package/src/engineSettings.js +12 -0
package/plugins/box2d.js
CHANGED
|
@@ -43,8 +43,9 @@ class Box2dObject extends EngineObject
|
|
|
43
43
|
|
|
44
44
|
destroy()
|
|
45
45
|
{
|
|
46
|
-
// destroy physics body and
|
|
47
|
-
box2dWorld.DestroyBody(this.body);
|
|
46
|
+
// destroy physics body, fixtures, and joints
|
|
47
|
+
this.body && box2dWorld.DestroyBody(this.body);
|
|
48
|
+
this.body = 0;
|
|
48
49
|
super.destroy();
|
|
49
50
|
}
|
|
50
51
|
|
|
@@ -91,20 +92,20 @@ class Box2dObject extends EngineObject
|
|
|
91
92
|
addShape(shape, density, friction, restitution, isSensor)
|
|
92
93
|
{
|
|
93
94
|
const fd = box2dCreateFixtureDef(shape, density, friction, restitution, isSensor);
|
|
94
|
-
this.addFixture(fd);
|
|
95
|
+
return this.addFixture(fd);
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
addBox(size=vec2(1), offset=vec2(), angle=0, density, friction, restitution, isSensor)
|
|
98
99
|
{
|
|
99
100
|
const shape = new box2d.b2PolygonShape();
|
|
100
101
|
shape.SetAsBox(size.x/2, size.y/2, offset.getBox2d(), angle);
|
|
101
|
-
this.addShape(shape, density, friction, restitution, isSensor);
|
|
102
|
+
return this.addShape(shape, density, friction, restitution, isSensor);
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
addPoly(points, density, friction, restitution, isSensor)
|
|
105
106
|
{
|
|
106
107
|
const shape = box2dCreatePolygonShape(points);
|
|
107
|
-
this.addShape(shape, density, friction, restitution, isSensor);
|
|
108
|
+
return this.addShape(shape, density, friction, restitution, isSensor);
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
addRegularPoly(diameter=1, sides=8, density, friction, restitution, isSensor)
|
|
@@ -113,7 +114,7 @@ class Box2dObject extends EngineObject
|
|
|
113
114
|
const radius = diameter/2;
|
|
114
115
|
for (let i=sides; i--;)
|
|
115
116
|
points.push(vec2(radius,0).rotate((i+.5)/sides*PI*2));
|
|
116
|
-
this.addPoly(points, density, friction, restitution, isSensor);
|
|
117
|
+
return this.addPoly(points, density, friction, restitution, isSensor);
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
addRandomPoly(diameter=1, density, friction, restitution, isSensor)
|
|
@@ -123,7 +124,7 @@ class Box2dObject extends EngineObject
|
|
|
123
124
|
const radius = diameter/2;
|
|
124
125
|
for (let i=sides; i--;)
|
|
125
126
|
points.push(vec2(rand(radius/2,radius*1.5),0).rotate(i/sides*PI*2));
|
|
126
|
-
this.addPoly(points, density, friction, restitution, isSensor);
|
|
127
|
+
return this.addPoly(points, density, friction, restitution, isSensor);
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
addCircle(diameter=1, offset=vec2(), density, friction, restitution, isSensor)
|
|
@@ -131,18 +132,19 @@ class Box2dObject extends EngineObject
|
|
|
131
132
|
const shape = new box2d.b2CircleShape();
|
|
132
133
|
shape.set_m_p(offset.getBox2d());
|
|
133
134
|
shape.set_m_radius(diameter/2);
|
|
134
|
-
this.addShape(shape, density, friction, restitution, isSensor);
|
|
135
|
+
return this.addShape(shape, density, friction, restitution, isSensor);
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
addEdge(point1, point2, density, friction, restitution, isSensor)
|
|
138
139
|
{
|
|
139
140
|
const shape = new box2d.b2EdgeShape();
|
|
140
141
|
shape.Set(point1.getBox2d(), point2.getBox2d());
|
|
141
|
-
this.addShape(shape, density, friction, restitution, isSensor);
|
|
142
|
+
return this.addShape(shape, density, friction, restitution, isSensor);
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
addEdgeLoop(points, density, friction, restitution, isSensor)
|
|
145
146
|
{
|
|
147
|
+
const fixtures = [];
|
|
146
148
|
const getPoint = i=> points[mod(i,points.length)];
|
|
147
149
|
for (let i=0; i<points.length; ++i)
|
|
148
150
|
{
|
|
@@ -151,12 +153,15 @@ class Box2dObject extends EngineObject
|
|
|
151
153
|
shape.set_m_vertex1(getPoint(i+0).getBox2d());
|
|
152
154
|
shape.set_m_vertex2(getPoint(i+1).getBox2d());
|
|
153
155
|
shape.set_m_vertex3(getPoint(i+2).getBox2d());
|
|
154
|
-
this.addShape(shape, density, friction, restitution, isSensor);
|
|
156
|
+
const f = this.addShape(shape, density, friction, restitution, isSensor);
|
|
157
|
+
fixtures.push(f);
|
|
155
158
|
}
|
|
159
|
+
return fixtures;
|
|
156
160
|
}
|
|
157
161
|
|
|
158
162
|
addEdgeList(points, density, friction, restitution, isSensor)
|
|
159
163
|
{
|
|
164
|
+
const fixtures = [];
|
|
160
165
|
for (let i=0; i<points.length-1; ++i)
|
|
161
166
|
{
|
|
162
167
|
const shape = new box2d.b2EdgeShape();
|
|
@@ -164,19 +169,37 @@ class Box2dObject extends EngineObject
|
|
|
164
169
|
points[i+0] && shape.set_m_vertex1(points[i+0].getBox2d());
|
|
165
170
|
points[i+1] && shape.set_m_vertex2(points[i+1].getBox2d());
|
|
166
171
|
points[i+2] && shape.set_m_vertex3(points[i+2].getBox2d());
|
|
167
|
-
this.addShape(shape, density, friction, restitution, isSensor);
|
|
172
|
+
const f = this.addShape(shape, density, friction, restitution, isSensor);
|
|
173
|
+
fixtures.push(f);
|
|
168
174
|
}
|
|
175
|
+
return fixtures;
|
|
169
176
|
}
|
|
170
177
|
|
|
178
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
179
|
+
// lists of fixtures and joints
|
|
180
|
+
|
|
181
|
+
hasFixtures() { return !box2dIsNull(this.body.GetFixtureList()); }
|
|
171
182
|
getFixtureList()
|
|
172
183
|
{
|
|
173
|
-
const
|
|
184
|
+
const fixtures = [];
|
|
174
185
|
for (let fixture=this.body.GetFixtureList(); !box2dIsNull(fixture); )
|
|
175
186
|
{
|
|
176
|
-
|
|
187
|
+
fixtures.push(fixture);
|
|
177
188
|
fixture = fixture.GetNext();
|
|
178
189
|
}
|
|
179
|
-
return
|
|
190
|
+
return fixtures;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
hasJoints() { return !box2dIsNull(this.body.GetJointList()); }
|
|
194
|
+
getJointList()
|
|
195
|
+
{
|
|
196
|
+
const joints = [];
|
|
197
|
+
for (let joint=this.body.GetJointList(); !box2dIsNull(joint); )
|
|
198
|
+
{
|
|
199
|
+
joints.push(joint);
|
|
200
|
+
joint = joint.get_next();
|
|
201
|
+
}
|
|
202
|
+
return joints;
|
|
180
203
|
}
|
|
181
204
|
|
|
182
205
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -214,6 +237,19 @@ class Box2dObject extends EngineObject
|
|
|
214
237
|
setBodyType(type) { this.body.SetType(type); }
|
|
215
238
|
setSleepingAllowed(isAllowed=true) { this.body.SetSleepingAllowed(isAllowed); }
|
|
216
239
|
setFixedRotation(isFixed=true) { this.body.SetFixedRotation(isFixed); }
|
|
240
|
+
setCenterOfMass(center) { this.setMassData(center) }
|
|
241
|
+
setMass(mass) { this.setMassData(undefined, mass) }
|
|
242
|
+
setMomentOfInertia(I) { this.setMassData(undefined, undefined, I) }
|
|
243
|
+
resetMassData() { this.body.ResetMassData(); }
|
|
244
|
+
setMassData(localCenter, mass, momentOfInertia)
|
|
245
|
+
{
|
|
246
|
+
const data = new box2d.b2MassData();
|
|
247
|
+
this.body.GetMassData(data);
|
|
248
|
+
localCenter && data.set_center(localCenter.getBox2d());
|
|
249
|
+
mass && data.set_mass(mass);
|
|
250
|
+
momentOfInertia && data.set_I(momentOfInertia);
|
|
251
|
+
this.body.SetMassData(data);
|
|
252
|
+
}
|
|
217
253
|
setFilterData(categoryBits=0, ignoreCategoryBits=0, groupIndex=0)
|
|
218
254
|
{
|
|
219
255
|
this.getFixtureList().forEach(fixture=>
|
|
@@ -228,17 +264,17 @@ class Box2dObject extends EngineObject
|
|
|
228
264
|
{ this.getFixtureList().forEach(f=>f.SetSensor(isSensor)); }
|
|
229
265
|
|
|
230
266
|
///////////////////////////////////////////////////////////////////////////////
|
|
231
|
-
// physics
|
|
267
|
+
// physics force and torque functions
|
|
232
268
|
|
|
233
269
|
applyForce(force, pos)
|
|
234
270
|
{
|
|
235
|
-
pos
|
|
271
|
+
pos ||= this.getCenterOfMass();
|
|
236
272
|
this.setAwake();
|
|
237
273
|
this.body.ApplyForce(force.getBox2d(), pos.getBox2d());
|
|
238
274
|
}
|
|
239
275
|
applyAcceleration(acceleration, pos)
|
|
240
276
|
{
|
|
241
|
-
pos
|
|
277
|
+
pos ||= this.getCenterOfMass();
|
|
242
278
|
this.setAwake();
|
|
243
279
|
this.body.ApplyLinearImpulse(acceleration.getBox2d(), pos.getBox2d());
|
|
244
280
|
}
|
|
@@ -270,7 +306,7 @@ class Box2dRaycastResult
|
|
|
270
306
|
}
|
|
271
307
|
}
|
|
272
308
|
|
|
273
|
-
// raycast and return a list of all results
|
|
309
|
+
// raycast and return a list of all the results
|
|
274
310
|
function box2dRaycastAll(start, end)
|
|
275
311
|
{
|
|
276
312
|
const raycastCallback = new box2d.JSRayCastCallback();
|
|
@@ -293,11 +329,12 @@ function box2dRaycastAll(start, end)
|
|
|
293
329
|
function box2dRaycast(start, end)
|
|
294
330
|
{
|
|
295
331
|
const raycastResults = box2dRaycastAll(start, end);
|
|
296
|
-
|
|
297
|
-
|
|
332
|
+
if (!raycastResults.length)
|
|
333
|
+
return undefined;
|
|
334
|
+
return raycastResults.reduce((a,b)=>a.fraction < b.fraction ? a : b);
|
|
298
335
|
}
|
|
299
336
|
|
|
300
|
-
// box aabb cast and return the
|
|
337
|
+
// box aabb cast and return all the objects
|
|
301
338
|
function box2dBoxCastAll(pos, size)
|
|
302
339
|
{
|
|
303
340
|
const queryCallback = new box2d.JSQueryCallback();
|
|
@@ -320,7 +357,7 @@ function box2dBoxCastAll(pos, size)
|
|
|
320
357
|
return queryObjects;
|
|
321
358
|
}
|
|
322
359
|
|
|
323
|
-
// box aabb cast and return the first
|
|
360
|
+
// box aabb cast and return the first object
|
|
324
361
|
function box2dBoxCast(pos, size)
|
|
325
362
|
{
|
|
326
363
|
const queryCallback = new box2d.JSQueryCallback();
|
|
@@ -341,7 +378,7 @@ function box2dBoxCast(pos, size)
|
|
|
341
378
|
return queryObject;
|
|
342
379
|
}
|
|
343
380
|
|
|
344
|
-
// circle cast and return the
|
|
381
|
+
// circle cast and return all the objects
|
|
345
382
|
function box2dCircleCastAll(pos, diameter)
|
|
346
383
|
{
|
|
347
384
|
const radius2 = (diameter/2)**2;
|
|
@@ -349,17 +386,26 @@ function box2dCircleCastAll(pos, diameter)
|
|
|
349
386
|
return results.filter(o=>o.pos.distanceSquared(pos) < radius2);
|
|
350
387
|
}
|
|
351
388
|
|
|
352
|
-
// circle cast and return the first
|
|
389
|
+
// circle cast and return the first object
|
|
353
390
|
function box2dCircleCast(pos, diameter)
|
|
354
391
|
{
|
|
355
392
|
const radius2 = (diameter/2)**2;
|
|
356
393
|
let results = box2dBoxCastAll(pos, vec2(diameter));
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
394
|
+
|
|
395
|
+
let bestResult, bestDistance2;
|
|
396
|
+
for (const result of results)
|
|
397
|
+
{
|
|
398
|
+
const distance2 = result.pos.distanceSquared(pos);
|
|
399
|
+
if (distance2 < radius2 && (!bestResult || distance2 < bestDistance2))
|
|
400
|
+
{
|
|
401
|
+
bestResult = result;
|
|
402
|
+
bestDistance2 = distance2;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return bestResult;
|
|
360
406
|
}
|
|
361
407
|
|
|
362
|
-
// point cast and return the first
|
|
408
|
+
// point cast and return the first object
|
|
363
409
|
function box2dPointCast(pos, dynamicOnly=true)
|
|
364
410
|
{
|
|
365
411
|
const queryCallback = new box2d.JSQueryCallback();
|
|
@@ -384,6 +430,28 @@ function box2dPointCast(pos, dynamicOnly=true)
|
|
|
384
430
|
return queryResult;
|
|
385
431
|
}
|
|
386
432
|
|
|
433
|
+
// box aabb cast and return all the fixtures
|
|
434
|
+
function box2dBoxCastAllFixtures(pos, size)
|
|
435
|
+
{
|
|
436
|
+
const queryCallback = new box2d.JSQueryCallback();
|
|
437
|
+
queryCallback.ReportFixture = function(fixturePointer)
|
|
438
|
+
{
|
|
439
|
+
const fixture = box2d.wrapPointer(fixturePointer, box2d.b2Fixture);
|
|
440
|
+
if (!queryObjects.includes(fixture))
|
|
441
|
+
queryObjects.push(fixture); // add if not already in list
|
|
442
|
+
return true; // continue getting results
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
const aabb = new box2d.b2AABB();
|
|
446
|
+
aabb.set_lowerBound(pos.subtract(size.scale(.5)).getBox2d());
|
|
447
|
+
aabb.set_upperBound(pos.add(size.scale(.5)).getBox2d());
|
|
448
|
+
|
|
449
|
+
let queryFixtures = [];
|
|
450
|
+
box2dWorld.QueryAABB(queryCallback, aabb);
|
|
451
|
+
debugRaycast && debugRect(pos, size, raycstResult ? '#f00' : '#00f', .02);
|
|
452
|
+
return queryFixtures;
|
|
453
|
+
}
|
|
454
|
+
|
|
387
455
|
///////////////////////////////////////////////////////////////////////////////
|
|
388
456
|
// Box2D Joints
|
|
389
457
|
|
|
@@ -394,14 +462,19 @@ function box2dCreateMouseJoint(object, fixedObject, worldPos)
|
|
|
394
462
|
jointDef.set_bodyA(fixedObject.body);
|
|
395
463
|
jointDef.set_bodyB(object.body);
|
|
396
464
|
jointDef.set_target(worldPos.getBox2d());
|
|
397
|
-
jointDef.set_maxForce(
|
|
465
|
+
jointDef.set_maxForce(2e3 * object.getMass());
|
|
398
466
|
return box2dCastObject(box2dWorld.CreateJoint(jointDef));
|
|
399
467
|
}
|
|
400
468
|
|
|
469
|
+
function box2dCreatePinJoint(objectA, objectB, collide=false)
|
|
470
|
+
{
|
|
471
|
+
return box2dCreateDistanceJoint(objectA, objectB, objectB.pos, undefined, collide);
|
|
472
|
+
}
|
|
473
|
+
|
|
401
474
|
function box2dCreateDistanceJoint(objectA, objectB, anchorA, anchorB, collide=false)
|
|
402
475
|
{
|
|
403
|
-
anchorA
|
|
404
|
-
anchorB
|
|
476
|
+
anchorA ||= vec2(objectA.body.GetPosition());
|
|
477
|
+
anchorB ||= vec2(objectB.body.GetPosition());
|
|
405
478
|
const localAnchorA = objectA.worldToLocal(anchorA);
|
|
406
479
|
const localAnchorB = objectB.worldToLocal(anchorB);
|
|
407
480
|
const jointDef = new box2d.b2DistanceJointDef();
|
|
@@ -416,7 +489,7 @@ function box2dCreateDistanceJoint(objectA, objectB, anchorA, anchorB, collide=fa
|
|
|
416
489
|
|
|
417
490
|
function box2dCreateRevoluteJoint(objectA, objectB, anchor, collide=false)
|
|
418
491
|
{
|
|
419
|
-
anchor
|
|
492
|
+
anchor ||= vec2(objectB.body.GetPosition());
|
|
420
493
|
const localAnchorA = objectA.worldToLocal(anchor);
|
|
421
494
|
const localAnchorB = objectB.worldToLocal(anchor);
|
|
422
495
|
const jointDef = new box2d.b2RevoluteJointDef();
|
|
@@ -431,7 +504,7 @@ function box2dCreateRevoluteJoint(objectA, objectB, anchor, collide=false)
|
|
|
431
504
|
|
|
432
505
|
function box2dCreatePrismaticJoint(objectA, objectB, anchor, worldAxis=vec2(0,1), collide=false)
|
|
433
506
|
{
|
|
434
|
-
anchor
|
|
507
|
+
anchor ||= vec2(objectB.body.GetPosition());
|
|
435
508
|
const localAnchorA = objectA.worldToLocal(anchor);
|
|
436
509
|
const localAnchorB = objectB.worldToLocal(anchor);
|
|
437
510
|
const localAxisA = objectB.worldToLocalVector(worldAxis);
|
|
@@ -448,7 +521,7 @@ function box2dCreatePrismaticJoint(objectA, objectB, anchor, worldAxis=vec2(0,1)
|
|
|
448
521
|
|
|
449
522
|
function box2dCreateWheelJoint(objectA, objectB, anchor, worldAxis=vec2(0,1), collide=false)
|
|
450
523
|
{
|
|
451
|
-
anchor
|
|
524
|
+
anchor ||= vec2(objectB.body.GetPosition());
|
|
452
525
|
const localAnchorA = objectA.worldToLocal(anchor);
|
|
453
526
|
const localAnchorB = objectB.worldToLocal(anchor);
|
|
454
527
|
const localAxisA = objectB.worldToLocalVector(worldAxis);
|
|
@@ -464,7 +537,7 @@ function box2dCreateWheelJoint(objectA, objectB, anchor, worldAxis=vec2(0,1), co
|
|
|
464
537
|
|
|
465
538
|
function box2dCreateWeldJoint(objectA, objectB, anchor, collide=false)
|
|
466
539
|
{
|
|
467
|
-
anchor
|
|
540
|
+
anchor ||= vec2(objectB.body.GetPosition());
|
|
468
541
|
const localAnchorA = objectA.worldToLocal(anchor);
|
|
469
542
|
const localAnchorB = objectB.worldToLocal(anchor);
|
|
470
543
|
const jointDef = new box2d.b2WeldJointDef();
|
|
@@ -479,7 +552,7 @@ function box2dCreateWeldJoint(objectA, objectB, anchor, collide=false)
|
|
|
479
552
|
|
|
480
553
|
function box2dCreateFrictionJoint(objectA, objectB, anchor, collide=false)
|
|
481
554
|
{
|
|
482
|
-
anchor
|
|
555
|
+
anchor ||= vec2(objectB.body.GetPosition());
|
|
483
556
|
const localAnchorA = objectA.worldToLocal(anchor);
|
|
484
557
|
const localAnchorB = objectB.worldToLocal(anchor);
|
|
485
558
|
const jointDef = new box2d.b2FrictionJointDef();
|
|
@@ -493,8 +566,8 @@ function box2dCreateFrictionJoint(objectA, objectB, anchor, collide=false)
|
|
|
493
566
|
|
|
494
567
|
function box2dCreateRopeJoint(objectA, objectB, anchorA, anchorB, extraLength=0, collide=false)
|
|
495
568
|
{
|
|
496
|
-
anchorA
|
|
497
|
-
anchorB
|
|
569
|
+
anchorA ||= vec2(objectA.body.GetPosition());
|
|
570
|
+
anchorB ||= vec2(objectB.body.GetPosition());
|
|
498
571
|
const localAnchorA = objectA.worldToLocal(anchorA);
|
|
499
572
|
const localAnchorB = objectB.worldToLocal(anchorB);
|
|
500
573
|
const jointDef = new box2d.b2RopeJointDef();
|
|
@@ -509,8 +582,8 @@ function box2dCreateRopeJoint(objectA, objectB, anchorA, anchorB, extraLength=0,
|
|
|
509
582
|
|
|
510
583
|
function box2dCreatePulleyJoint(objectA, objectB, groundAnchorA, groundAnchorB, anchorA, anchorB, ratio=1, collide=false)
|
|
511
584
|
{
|
|
512
|
-
anchorA
|
|
513
|
-
anchorB
|
|
585
|
+
anchorA ||= vec2(objectA.body.GetPosition());
|
|
586
|
+
anchorB ||= vec2(objectB.body.GetPosition());
|
|
514
587
|
const localAnchorA = objectA.worldToLocal(anchorA);
|
|
515
588
|
const localAnchorB = objectB.worldToLocal(anchorB);
|
|
516
589
|
const jointDef = new box2d.b2PulleyJointDef();
|
|
@@ -638,6 +711,13 @@ function box2dCastObject(object)
|
|
|
638
711
|
ASSERT(false, 'Unknown object type');
|
|
639
712
|
}
|
|
640
713
|
|
|
714
|
+
function box2dWarmup(frames=100)
|
|
715
|
+
{
|
|
716
|
+
// run the sim for a few frames to let objects settle
|
|
717
|
+
for (let i=frames; i--;)
|
|
718
|
+
box2dWorld.Step(timeDelta, box2dStepIterations, box2dStepIterations);
|
|
719
|
+
}
|
|
720
|
+
|
|
641
721
|
///////////////////////////////////////////////////////////////////////////////
|
|
642
722
|
// Box2D Drawing
|
|
643
723
|
|
|
@@ -792,17 +872,16 @@ function box2dEngineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameR
|
|
|
792
872
|
box2dWorld.SetDebugDraw(box2dDebugDraw);
|
|
793
873
|
|
|
794
874
|
// hook up box2d plugin to update and render
|
|
795
|
-
|
|
875
|
+
engineAddPlugin(box2dUpdate, box2dRender);
|
|
876
|
+
function box2dUpdate()
|
|
796
877
|
{
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
});
|
|
801
|
-
addPluginRender(function()
|
|
878
|
+
box2dWorld.Step(timeDelta, box2dStepIterations, box2dStepIterations);
|
|
879
|
+
}
|
|
880
|
+
function box2dRender()
|
|
802
881
|
{
|
|
803
882
|
if (box2dDebug || debugPhysics && debugOverlay)
|
|
804
883
|
box2dWorld.DrawDebugData();
|
|
805
|
-
}
|
|
884
|
+
}
|
|
806
885
|
|
|
807
886
|
// start littlejs
|
|
808
887
|
engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources);
|
package/plugins/postProcess.js
CHANGED
|
@@ -55,7 +55,8 @@ function initPostProcess(shaderCode, includeOverlay=false)
|
|
|
55
55
|
overlayCanvas.style.visibility = 'hidden';
|
|
56
56
|
|
|
57
57
|
// Render the post processing shader, called automatically by the engine
|
|
58
|
-
|
|
58
|
+
engineAddPlugin(undefined, postProcessRender);
|
|
59
|
+
function postProcessRender()
|
|
59
60
|
{
|
|
60
61
|
if (headlessMode) return;
|
|
61
62
|
|
|
@@ -97,5 +98,5 @@ function initPostProcess(shaderCode, includeOverlay=false)
|
|
|
97
98
|
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
98
99
|
glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
|
|
99
100
|
glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 4);
|
|
100
|
-
}
|
|
101
|
+
}
|
|
101
102
|
}
|
package/src/engine.js
CHANGED
|
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
|
|
|
30
30
|
* @type {String}
|
|
31
31
|
* @default
|
|
32
32
|
* @memberof Engine */
|
|
33
|
-
const engineVersion = '1.9.
|
|
33
|
+
const engineVersion = '1.9.11';
|
|
34
34
|
|
|
35
35
|
/** Frames per second to update
|
|
36
36
|
* @type {Number}
|
|
@@ -89,14 +89,14 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
|
89
89
|
const pluginUpdateList = [], pluginRenderList = [];
|
|
90
90
|
|
|
91
91
|
/** Add a new update function for a plugin
|
|
92
|
-
* @param {Function} updateFunction
|
|
92
|
+
* @param {Function} [updateFunction]
|
|
93
|
+
* @param {Function} [renderFunction]
|
|
93
94
|
* @memberof Engine */
|
|
94
|
-
function
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
function addPluginRender(renderFunction) { pluginRenderList.push(renderFunction); }
|
|
95
|
+
function engineAddPlugin(updateFunction, renderFunction)
|
|
96
|
+
{
|
|
97
|
+
updateFunction && pluginUpdateList.push(updateFunction);
|
|
98
|
+
renderFunction && pluginRenderList.push(renderFunction);
|
|
99
|
+
}
|
|
100
100
|
|
|
101
101
|
///////////////////////////////////////////////////////////////////////////////
|
|
102
102
|
// Main engine functions
|
|
@@ -269,10 +269,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
269
269
|
const styleBody =
|
|
270
270
|
'margin:0;overflow:hidden;' + // fill the window
|
|
271
271
|
'background:#000;' + // set background color
|
|
272
|
-
'
|
|
273
|
-
'user-select:none;' + // prevent mobile hold to select
|
|
272
|
+
'user-select:none;' + // prevent hold to select
|
|
274
273
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
275
|
-
'
|
|
274
|
+
(!touchInputEnable ? '' : // no touch css setttings
|
|
275
|
+
'touch-action:none;' + // prevent mobile pinch to resize
|
|
276
|
+
'-webkit-touch-callout:none');// compatibility for ios
|
|
276
277
|
document.body.style.cssText = styleBody;
|
|
277
278
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
278
279
|
mainContext = mainCanvas.getContext('2d');
|
package/src/engineAudio.js
CHANGED
|
@@ -73,6 +73,7 @@ class Sound
|
|
|
73
73
|
// generate zzfx sound now for fast playback
|
|
74
74
|
const defaultRandomness = .05;
|
|
75
75
|
this.randomness = zzfxSound[1] || defaultRandomness;
|
|
76
|
+
zzfxSound[1] = 0; // generate without randomness
|
|
76
77
|
this.sampleChannels = [zzfxG(...zzfxSound)];
|
|
77
78
|
this.sampleRate = zzfxR;
|
|
78
79
|
}
|
|
@@ -305,10 +306,6 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
305
306
|
|
|
306
307
|
///////////////////////////////////////////////////////////////////////////////
|
|
307
308
|
|
|
308
|
-
// internal tracking if audio was suspended when last sound was played
|
|
309
|
-
// allows first suspended sound to play when audio is resumed
|
|
310
|
-
let audioSuspended = false;
|
|
311
|
-
|
|
312
309
|
/** Play cached audio samples with given settings
|
|
313
310
|
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
314
311
|
* @param {Number} [volume] - How much to scale volume by
|
|
@@ -324,20 +321,21 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
324
321
|
if (!soundEnable || headlessMode) return;
|
|
325
322
|
|
|
326
323
|
// prevent sounds from building up if they can't be played
|
|
327
|
-
|
|
328
|
-
if (audioSuspended = audioContext.state != 'running')
|
|
324
|
+
if (audioContext.state != 'running')
|
|
329
325
|
{
|
|
330
326
|
// fix stalled audio
|
|
331
|
-
audioContext.resume()
|
|
327
|
+
audioContext.resume().then(()=>
|
|
328
|
+
playSamples(sampleChannels, volume, rate, pan, loop, sampleRate, gainNode));
|
|
332
329
|
|
|
333
330
|
// prevent suspended sounds from building up
|
|
334
|
-
|
|
335
|
-
return;
|
|
331
|
+
return;
|
|
336
332
|
}
|
|
337
333
|
|
|
338
334
|
// create buffer and source
|
|
339
|
-
const
|
|
340
|
-
|
|
335
|
+
const channelCount = sampleChannels.length;
|
|
336
|
+
const sampleLength = sampleChannels[0].length;
|
|
337
|
+
const buffer = audioContext.createBuffer(channelCount, sampleLength, sampleRate);
|
|
338
|
+
const source = audioContext.createBufferSource();
|
|
341
339
|
|
|
342
340
|
// copy samples to buffer and setup source
|
|
343
341
|
sampleChannels.forEach((c,i)=> buffer.getChannelData(i).set(c));
|
|
@@ -351,7 +349,8 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
351
349
|
gainNode.connect(audioGainNode);
|
|
352
350
|
|
|
353
351
|
// connect source to stereo panner and gain
|
|
354
|
-
|
|
352
|
+
const pannerNode = new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)});
|
|
353
|
+
source.connect(pannerNode).connect(gainNode);
|
|
355
354
|
|
|
356
355
|
// play and return sound
|
|
357
356
|
source.start();
|
|
@@ -367,7 +366,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
367
366
|
* @param {Array} zzfxSound - Array of ZzFX parameters, ex. [.5,.5]
|
|
368
367
|
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
369
368
|
* @memberof Audio */
|
|
370
|
-
function zzfx(...zzfxSound) { return
|
|
369
|
+
function zzfx(...zzfxSound) { return playSamples([zzfxG(...zzfxSound)]); }
|
|
371
370
|
|
|
372
371
|
/** Sample rate used for all ZzFX sounds
|
|
373
372
|
* @default 44100
|
|
@@ -376,7 +375,7 @@ const zzfxR = 44100;
|
|
|
376
375
|
|
|
377
376
|
/** Generate samples for a ZzFX sound
|
|
378
377
|
* @param {Number} [volume] - Volume scale (percent)
|
|
379
|
-
* @param {Number} [randomness] -
|
|
378
|
+
* @param {Number} [randomness] - How much to randomize frequency (percent Hz)
|
|
380
379
|
* @param {Number} [frequency] - Frequency of sound (Hz)
|
|
381
380
|
* @param {Number} [attack] - Attack time, how fast sound starts (seconds)
|
|
382
381
|
* @param {Number} [sustain] - Sustain time, how long sound holds (seconds)
|
|
@@ -402,7 +401,7 @@ const zzfxR = 44100;
|
|
|
402
401
|
function zzfxG
|
|
403
402
|
(
|
|
404
403
|
// parameters
|
|
405
|
-
volume = 1, randomness =
|
|
404
|
+
volume = 1, randomness = .05, frequency = 220, attack = 0, sustain = 0,
|
|
406
405
|
release = .1, shape = 0, shapeCurve = 1, slide = 0, deltaSlide = 0,
|
|
407
406
|
pitchJump = 0, pitchJumpTime = 0, repeatTime = 0, noise = 0, modulation = 0,
|
|
408
407
|
bitCrush = 0, delay = 0, sustainVolume = 1, decay = 0, tremolo = 0, filter = 0
|
|
@@ -413,7 +412,8 @@ function zzfxG
|
|
|
413
412
|
// init parameters
|
|
414
413
|
let PI2 = PI*2, sampleRate = zzfxR,
|
|
415
414
|
startSlide = slide *= 500 * PI2 / sampleRate / sampleRate,
|
|
416
|
-
startFrequency = frequency *=
|
|
415
|
+
startFrequency = frequency *=
|
|
416
|
+
rand(1 + randomness, 1-randomness) * PI2 / sampleRate,
|
|
417
417
|
b = [], t = 0, tm = 0, i = 0, j = 1, r = 0, c = 0, s = 0, f, length,
|
|
418
418
|
|
|
419
419
|
// biquad LP/HP filter
|
package/src/engineBuild.js
CHANGED
|
@@ -137,7 +137,7 @@ function closureCompilerStep(filename)
|
|
|
137
137
|
fs.copyFileSync(filename, filenameTemp);
|
|
138
138
|
try
|
|
139
139
|
{
|
|
140
|
-
child_process.execSync(`npx google-closure-compiler --js=${filenameTemp} --js_output_file=${filename} --
|
|
140
|
+
child_process.execSync(`npx google-closure-compiler --js=${filenameTemp} --js_output_file=${filename} --warning_level=VERBOSE --jscomp_off=*`);
|
|
141
141
|
fs.rmSync(filenameTemp);
|
|
142
142
|
}
|
|
143
143
|
catch (e) { handleError(e, 'Failed to run Closure Compiler step!'); }
|
package/src/engineDebug.js
CHANGED
|
@@ -190,8 +190,23 @@ function debugSaveDataURL(dataURL, filename)
|
|
|
190
190
|
downloadLink.click();
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
/** Show error as full page of red text
|
|
194
|
+
* @memberof Debug */
|
|
195
|
+
function debugShowErrors()
|
|
196
|
+
{
|
|
197
|
+
onunhandledrejection = (event)=>showError(event.reason);
|
|
198
|
+
onerror = (event, source, lineno, colno)=>
|
|
199
|
+
showError(`${event}\n${source}\nLn ${lineno}, Col ${colno}`);
|
|
200
|
+
|
|
201
|
+
const showError = (message)=>
|
|
202
|
+
{
|
|
203
|
+
document.body.style.backgroundColor = '#111';
|
|
204
|
+
document.body.innerHTML = `<pre style=color:#f00;font-size:50px>` + message;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
193
208
|
///////////////////////////////////////////////////////////////////////////////
|
|
194
|
-
// Engine debug
|
|
209
|
+
// Engine debug functions (called automatically)
|
|
195
210
|
|
|
196
211
|
function debugInit()
|
|
197
212
|
{
|
|
@@ -322,7 +337,7 @@ function debugRender()
|
|
|
322
337
|
const pos = worldToScreen(p.pos);
|
|
323
338
|
overlayContext.translate(pos.x|0, pos.y|0);
|
|
324
339
|
overlayContext.rotate(p.angle);
|
|
325
|
-
overlayContext.scale(1, -1);
|
|
340
|
+
overlayContext.scale(1, p.text ? 1 : -1);
|
|
326
341
|
overlayContext.fillStyle = overlayContext.strokeStyle = p.color;
|
|
327
342
|
|
|
328
343
|
if (p.text != undefined)
|
package/src/engineExport.js
CHANGED
|
@@ -20,6 +20,7 @@ export {
|
|
|
20
20
|
engineObjectsUpdate,
|
|
21
21
|
engineObjectsDestroy,
|
|
22
22
|
engineObjectsCallback,
|
|
23
|
+
engineAddPlugin,
|
|
23
24
|
|
|
24
25
|
// Globals
|
|
25
26
|
debug,
|
|
@@ -101,6 +102,7 @@ export {
|
|
|
101
102
|
setObjectMaxSpeed,
|
|
102
103
|
setGravity,
|
|
103
104
|
setParticleEmitRateScale,
|
|
105
|
+
setTouchInputEnable,
|
|
104
106
|
setGamepadsEnable,
|
|
105
107
|
setGamepadDirectionEmulateStick,
|
|
106
108
|
setInputWASDEmulateDirection,
|
|
@@ -186,10 +188,13 @@ export {
|
|
|
186
188
|
// WebGL
|
|
187
189
|
glCanvas,
|
|
188
190
|
glContext,
|
|
189
|
-
glSetTexture,
|
|
190
191
|
glCompileShader,
|
|
192
|
+
glCopyToContext,
|
|
191
193
|
glCreateProgram,
|
|
192
194
|
glCreateTexture,
|
|
195
|
+
glDraw,
|
|
196
|
+
glFlush,
|
|
197
|
+
glSetTexture,
|
|
193
198
|
|
|
194
199
|
// Input
|
|
195
200
|
keyIsDown,
|
|
@@ -249,4 +254,4 @@ export {
|
|
|
249
254
|
medalsPreventUnlock,
|
|
250
255
|
medalsInit,
|
|
251
256
|
Medal,
|
|
252
|
-
};
|
|
257
|
+
};
|