littlejsengine 1.9.9 → 1.10.2
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 +6 -0
- package/dist/littlejs.d.ts +107 -14
- package/dist/littlejs.esm.js +250 -102
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +226 -98
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +208 -95
- package/examples/box2d/game.js +29 -10
- package/examples/box2d/gameObjects.js +485 -0
- package/examples/box2d/index.html +7 -5
- package/examples/box2d/scenes.js +28 -247
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/game.js +1 -1
- package/examples/breakout/index.html +5 -4
- package/examples/breakoutTutorial/index.html +4 -3
- package/examples/electron/game.js +1 -1
- package/examples/electron/index.html +4 -3
- package/examples/empty/game.js +1 -1
- package/examples/empty/index.html +2 -1
- package/examples/htmlMenu/game.js +67 -0
- package/examples/htmlMenu/index.html +56 -0
- package/examples/htmlMenu/tiles.png +0 -0
- package/examples/index.html +33 -0
- package/examples/js13k/game.js +1 -1
- package/examples/js13k/index.html +17 -14
- package/examples/module/game.js +1 -1
- package/examples/module/index.html +3 -2
- package/examples/particles/index.html +4 -3
- package/examples/platformer/game.js +1 -1
- package/examples/platformer/gameCharacter.js +1 -0
- package/examples/platformer/gameEffects.js +0 -2
- package/examples/platformer/gameLevel.js +2 -2
- package/examples/platformer/gameObjects.js +1 -2
- package/examples/platformer/index.html +10 -9
- package/examples/puzzle/game.js +1 -1
- package/examples/puzzle/index.html +4 -3
- package/examples/starter/game.js +5 -5
- package/examples/starter/index.html +15 -14
- package/examples/stress/index.html +3 -2
- package/examples/typescript/game.js +1 -1
- package/examples/typescript/index.html +3 -2
- package/examples/uiSystem/game.js +134 -0
- package/examples/uiSystem/index.html +25 -0
- package/examples/uiSystem/tiles.png +0 -0
- package/jsconfig.json +11 -0
- package/package.json +1 -1
- package/plugins/box2d.js +116 -42
- package/plugins/postProcess.js +6 -7
- package/plugins/uiSystem.js +243 -0
- package/src/engine.js +48 -20
- package/src/engineAudio.js +22 -25
- package/src/engineDebug.js +18 -3
- package/src/engineDraw.js +101 -27
- package/src/engineExport.js +23 -4
- package/src/engineInput.js +9 -5
- package/src/engineObject.js +15 -12
- package/src/engineSettings.js +1 -1
- package/src/engineUtilities.js +1 -1
- package/src/engineWebGL.js +11 -4
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();
|
|
@@ -641,7 +714,7 @@ function box2dCastObject(object)
|
|
|
641
714
|
function box2dWarmup(frames=100)
|
|
642
715
|
{
|
|
643
716
|
// run the sim for a few frames to let objects settle
|
|
644
|
-
for(let i=frames; i--;)
|
|
717
|
+
for (let i=frames; i--;)
|
|
645
718
|
box2dWorld.Step(timeDelta, box2dStepIterations, box2dStepIterations);
|
|
646
719
|
}
|
|
647
720
|
|
|
@@ -802,7 +875,8 @@ function box2dEngineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameR
|
|
|
802
875
|
engineAddPlugin(box2dUpdate, box2dRender);
|
|
803
876
|
function box2dUpdate()
|
|
804
877
|
{
|
|
805
|
-
|
|
878
|
+
if (!paused)
|
|
879
|
+
box2dWorld.Step(timeDelta, box2dStepIterations, box2dStepIterations);
|
|
806
880
|
}
|
|
807
881
|
function box2dRender()
|
|
808
882
|
{
|
package/plugins/postProcess.js
CHANGED
|
@@ -49,11 +49,6 @@ function initPostProcess(shaderCode, includeOverlay=false)
|
|
|
49
49
|
glPostTexture = glCreateTexture(undefined);
|
|
50
50
|
glPostIncludeOverlay = includeOverlay;
|
|
51
51
|
|
|
52
|
-
// hide the original 2d canvas
|
|
53
|
-
mainCanvas.style.visibility = 'hidden';
|
|
54
|
-
if (glPostIncludeOverlay)
|
|
55
|
-
overlayCanvas.style.visibility = 'hidden';
|
|
56
|
-
|
|
57
52
|
// Render the post processing shader, called automatically by the engine
|
|
58
53
|
engineAddPlugin(undefined, postProcessRender);
|
|
59
54
|
function postProcessRender()
|
|
@@ -72,8 +67,12 @@ function initPostProcess(shaderCode, includeOverlay=false)
|
|
|
72
67
|
glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
|
|
73
68
|
}
|
|
74
69
|
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
if (glPostIncludeOverlay)
|
|
71
|
+
{
|
|
72
|
+
// copy overlay canvas so it will be included in post processing
|
|
73
|
+
mainContext.drawImage(overlayCanvas, 0, 0);
|
|
74
|
+
overlayCanvas.width |= 0;
|
|
75
|
+
}
|
|
77
76
|
|
|
78
77
|
// setup shader program to draw one triangle
|
|
79
78
|
glContext.useProgram(glPostShader);
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LittleJS User Interface Plugin
|
|
3
|
+
* - Nested Menus
|
|
4
|
+
* - Text
|
|
5
|
+
* - Buttons
|
|
6
|
+
* - Checkboxes
|
|
7
|
+
* - Images
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
13
|
+
|
|
14
|
+
// ui defaults
|
|
15
|
+
let uiDefaultColor = WHITE;
|
|
16
|
+
let uiDefaultLineColor = BLACK;
|
|
17
|
+
let uiDefaultTextColor = BLACK;
|
|
18
|
+
let uiDefaultButtonColor = hsl(0,0,.5);
|
|
19
|
+
let uiDefaultHoverColor = hsl(0,0,.7);
|
|
20
|
+
let uiDefaultLineWidth = 4;
|
|
21
|
+
let uiDefaultFont = 'arial';
|
|
22
|
+
|
|
23
|
+
// ui system
|
|
24
|
+
let uiObjects = [];
|
|
25
|
+
let uiContext;
|
|
26
|
+
|
|
27
|
+
function initUISystem(context=overlayContext)
|
|
28
|
+
{
|
|
29
|
+
uiContext = context;
|
|
30
|
+
engineAddPlugin(uiUpdate, uiRender);
|
|
31
|
+
|
|
32
|
+
// setup recursive update and render
|
|
33
|
+
function uiUpdate()
|
|
34
|
+
{
|
|
35
|
+
function updateObject(o)
|
|
36
|
+
{
|
|
37
|
+
if (!o.visible)
|
|
38
|
+
return;
|
|
39
|
+
if (o.parent)
|
|
40
|
+
o.pos = o.localPos.add(o.parent.pos);
|
|
41
|
+
o.update();
|
|
42
|
+
for(const c of o.children)
|
|
43
|
+
updateObject(c)
|
|
44
|
+
}
|
|
45
|
+
uiObjects.forEach(o=> o.parent || updateObject(o));
|
|
46
|
+
}
|
|
47
|
+
function uiRender()
|
|
48
|
+
{
|
|
49
|
+
function renderObject(o)
|
|
50
|
+
{
|
|
51
|
+
if (!o.visible)
|
|
52
|
+
return;
|
|
53
|
+
o.render();
|
|
54
|
+
for(const c of o.children)
|
|
55
|
+
renderObject(c)
|
|
56
|
+
}
|
|
57
|
+
uiObjects.forEach(o=> o.parent || renderObject(o));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function drawUIRect(pos, size, color=uiDefaultColor, lineWidth=uiDefaultLineWidth, lineColor=uiDefaultLineColor)
|
|
62
|
+
{
|
|
63
|
+
uiContext.fillStyle = color.toString();
|
|
64
|
+
uiContext.beginPath();
|
|
65
|
+
uiContext.rect(pos.x-size.x/2, pos.y-size.y/2, size.x, size.y);
|
|
66
|
+
uiContext.fill();
|
|
67
|
+
if (lineWidth)
|
|
68
|
+
{
|
|
69
|
+
uiContext.strokeStyle = lineColor.toString();
|
|
70
|
+
uiContext.lineWidth = lineWidth;
|
|
71
|
+
uiContext.stroke();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function drawUILine(posA, posB, thickness=uiDefaultLineWidth, color=uiDefaultLineColor)
|
|
76
|
+
{
|
|
77
|
+
uiContext.strokeStyle = color.toString();
|
|
78
|
+
uiContext.lineWidth = thickness;
|
|
79
|
+
uiContext.beginPath();
|
|
80
|
+
uiContext.lineTo(posA.x, posA.y);
|
|
81
|
+
uiContext.lineTo(posB.x, posB.y);
|
|
82
|
+
uiContext.stroke();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function drawUITile(pos, size, tileInfo, color=uiDefaultColor, angle=0, mirror=false, additiveColor=BLACK)
|
|
86
|
+
{
|
|
87
|
+
drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, false, true, uiContext);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function drawUIText(text, pos, size, color=uiDefaultColor, lineWidth=uiDefaultLineWidth, lineColor=uiDefaultLineColor, align='center', font=uiDefaultFont)
|
|
91
|
+
{
|
|
92
|
+
drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, uiContext, size.x);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
96
|
+
|
|
97
|
+
class UIObject
|
|
98
|
+
{
|
|
99
|
+
constructor(localPos, size=vec2())
|
|
100
|
+
{
|
|
101
|
+
this.localPos = localPos.copy();
|
|
102
|
+
this.pos = localPos.copy();
|
|
103
|
+
this.size = size.copy();
|
|
104
|
+
this.color = uiDefaultColor;
|
|
105
|
+
this.lineColor = uiDefaultLineColor;
|
|
106
|
+
this.textColor = uiDefaultTextColor;
|
|
107
|
+
this.hoverColor = uiDefaultHoverColor;
|
|
108
|
+
this.lineWidth = uiDefaultLineWidth;
|
|
109
|
+
this.font = uiDefaultFont;
|
|
110
|
+
this.visible = true;
|
|
111
|
+
this.children = [];
|
|
112
|
+
this.parent = null;
|
|
113
|
+
uiObjects.push(this);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
addChild(child)
|
|
117
|
+
{
|
|
118
|
+
ASSERT(!child.parent && !this.children.includes(child));
|
|
119
|
+
this.children.push(child);
|
|
120
|
+
child.parent = this;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
removeChild(child)
|
|
124
|
+
{
|
|
125
|
+
ASSERT(child.parent == this && this.children.includes(child));
|
|
126
|
+
this.children.splice(this.children.indexOf(child), 1);
|
|
127
|
+
child.parent = 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
update()
|
|
131
|
+
{
|
|
132
|
+
// track mouse input
|
|
133
|
+
const mouseWasOver = this.mouseIsOver;
|
|
134
|
+
this.mouseIsOver = isOverlapping(this.pos, this.size, mousePosScreen);
|
|
135
|
+
if (this.mouseIsOver && !mouseWasOver)
|
|
136
|
+
this.onEnter();
|
|
137
|
+
if (!this.mouseIsOver && mouseWasOver)
|
|
138
|
+
this.onLeave();
|
|
139
|
+
if (mouseWasPressed(0) && this.mouseIsOver)
|
|
140
|
+
{
|
|
141
|
+
this.mouseIsHeld = true;
|
|
142
|
+
this.onPress();
|
|
143
|
+
}
|
|
144
|
+
else if (this.mouseIsHeld && !mouseIsDown(0))
|
|
145
|
+
{
|
|
146
|
+
this.mouseIsHeld = false;
|
|
147
|
+
if (this.mouseIsOver)
|
|
148
|
+
this.onClick();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
render()
|
|
152
|
+
{
|
|
153
|
+
if (this.size.x && this.size.y)
|
|
154
|
+
drawUIRect(this.pos, this.size, this.color, this.lineWidth, this.lineColor);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// callback functions
|
|
158
|
+
onEnter() {}
|
|
159
|
+
onLeave() {}
|
|
160
|
+
onPress() {}
|
|
161
|
+
onClick() {}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
165
|
+
|
|
166
|
+
class UIText extends UIObject
|
|
167
|
+
{
|
|
168
|
+
constructor(pos, size, text, align='center', font=fontDefault)
|
|
169
|
+
{
|
|
170
|
+
super(pos, size);
|
|
171
|
+
|
|
172
|
+
this.text = text;
|
|
173
|
+
this.align = align;
|
|
174
|
+
this.font = font;
|
|
175
|
+
this.lineWidth = 0;
|
|
176
|
+
}
|
|
177
|
+
render()
|
|
178
|
+
{
|
|
179
|
+
drawUIText(this.text, this.pos, this.size, this.textColor, this.lineWidth, this.lineColor, this.align, this.font);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
184
|
+
|
|
185
|
+
class UITile extends UIObject
|
|
186
|
+
{
|
|
187
|
+
constructor(pos, size, tileInfo, color=WHITE, angle=0, mirror=false, additiveColor=BLACK)
|
|
188
|
+
{
|
|
189
|
+
super(pos, size);
|
|
190
|
+
|
|
191
|
+
this.tileInfo = tileInfo;
|
|
192
|
+
this.color = color;
|
|
193
|
+
this.angle = angle;
|
|
194
|
+
this.mirror = mirror;
|
|
195
|
+
this.additiveColor = additiveColor;
|
|
196
|
+
}
|
|
197
|
+
render()
|
|
198
|
+
{
|
|
199
|
+
drawUITile(this.pos, this.size, this.tileInfo, this.color, this.angle, this.mirror, this.additiveColor);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
204
|
+
|
|
205
|
+
class UIButton extends UIObject
|
|
206
|
+
{
|
|
207
|
+
constructor(pos, size, text)
|
|
208
|
+
{
|
|
209
|
+
super(pos, size);
|
|
210
|
+
this.text = text;
|
|
211
|
+
this.color = uiDefaultButtonColor;
|
|
212
|
+
}
|
|
213
|
+
render()
|
|
214
|
+
{
|
|
215
|
+
const lineColor = this.mouseIsHeld ? this.color : this.lineColor;
|
|
216
|
+
const color = this.mouseIsOver? this.hoverColor : this.color;
|
|
217
|
+
drawUIRect(this.pos, this.size, color, this.lineWidth, lineColor);
|
|
218
|
+
drawTextScreen(this.text, this.pos, this.size.y*.8,
|
|
219
|
+
this.textColor, undefined, undefined, this.align, this.font, uiContext);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
224
|
+
|
|
225
|
+
class UICheckbox extends UIObject
|
|
226
|
+
{
|
|
227
|
+
constructor(pos, size, checked=false)
|
|
228
|
+
{
|
|
229
|
+
super(pos, size);
|
|
230
|
+
this.checked = checked;
|
|
231
|
+
}
|
|
232
|
+
onClick() { this.checked = !this.checked; }
|
|
233
|
+
render()
|
|
234
|
+
{
|
|
235
|
+
drawUIRect(this.pos, this.size, this.color, this.lineWidth, this.lineColor);
|
|
236
|
+
if (this.checked)
|
|
237
|
+
{
|
|
238
|
+
// draw an X if checked
|
|
239
|
+
drawUILine(this.pos.add(this.size.multiply(vec2(-.5,-.5))), this.pos.add(this.size.multiply(vec2(.5,.5))), this.lineWidth, this.lineColor);
|
|
240
|
+
drawUILine(this.pos.add(this.size.multiply(vec2(-.5,.5))), this.pos.add(this.size.multiply(vec2(.5,-.5))), this.lineWidth, this.lineColor);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|