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/examples/box2d/game.js
CHANGED
|
@@ -16,12 +16,13 @@ setShowSplashScreen(true);
|
|
|
16
16
|
//box2dDebug = 1; // enable box2d debug draw
|
|
17
17
|
|
|
18
18
|
// game variables
|
|
19
|
-
const maxScenes =
|
|
19
|
+
const maxScenes = 11;
|
|
20
20
|
let scene = 0;
|
|
21
21
|
let sceneName;
|
|
22
22
|
let groundObject;
|
|
23
23
|
let mouseJoint;
|
|
24
|
-
let
|
|
24
|
+
let car;
|
|
25
|
+
let repeatSpawnTimer = new Timer;
|
|
25
26
|
|
|
26
27
|
///////////////////////////////////////////////////////////////////////////////
|
|
27
28
|
function gameInit()
|
|
@@ -54,13 +55,25 @@ function gameUpdate()
|
|
|
54
55
|
mouseJoint = 0;
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
|
-
if (
|
|
58
|
-
|
|
58
|
+
if (mouseIsDown(1) || mouseIsDown('KeyZ'))
|
|
59
|
+
{
|
|
60
|
+
const isSet = repeatSpawnTimer.isSet();
|
|
61
|
+
if (!isSet || repeatSpawnTimer.elapsed())
|
|
62
|
+
{
|
|
63
|
+
// spawn continuously after a delay
|
|
64
|
+
isSet || repeatSpawnTimer.set(.5);
|
|
65
|
+
spawnRandomObject(mousePos);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else
|
|
69
|
+
repeatSpawnTimer.unset();
|
|
59
70
|
if (mouseWasPressed(2) || keyWasPressed('KeyX'))
|
|
60
71
|
explosion(mousePos);
|
|
61
72
|
if (mouseJoint)
|
|
62
73
|
mouseJoint.SetTarget(mousePos.getBox2d());
|
|
63
74
|
|
|
75
|
+
if (keyWasPressed('KeyR'))
|
|
76
|
+
loadScene(scene); // reset scene
|
|
64
77
|
if (keyWasPressed('ArrowUp') || keyWasPressed('ArrowDown'))
|
|
65
78
|
{
|
|
66
79
|
// change scene
|
|
@@ -69,13 +82,11 @@ function gameUpdate()
|
|
|
69
82
|
loadScene(scene);
|
|
70
83
|
}
|
|
71
84
|
|
|
72
|
-
if (
|
|
85
|
+
if (car)
|
|
73
86
|
{
|
|
74
87
|
// update car control
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
s = control ? clamp(s + control, -40, 40) : s * .8;
|
|
78
|
-
carWheelJoint.SetMotorSpeed(s);
|
|
88
|
+
const input = keyIsDown('ArrowLeft') - keyIsDown('ArrowRight');
|
|
89
|
+
car.applyMotorInput(input);
|
|
79
90
|
}
|
|
80
91
|
}
|
|
81
92
|
|
|
@@ -110,6 +121,14 @@ function gameRender()
|
|
|
110
121
|
///////////////////////////////////////////////////////////////////////////////
|
|
111
122
|
function gameRenderPost()
|
|
112
123
|
{
|
|
124
|
+
if (mouseJoint)
|
|
125
|
+
{
|
|
126
|
+
// draw mouse joint
|
|
127
|
+
const ab = vec2(mouseJoint.GetAnchorB());
|
|
128
|
+
drawTile(ab, vec2(.3), tile(0), BLACK);
|
|
129
|
+
drawLine(mousePos, ab, .1, BLACK);
|
|
130
|
+
}
|
|
131
|
+
|
|
113
132
|
// draw to overlay canvas for hud rendering
|
|
114
133
|
const pos = vec2(mainCanvasSize.x/2, 50);
|
|
115
134
|
drawText('LittleJS Box2D Demo', 80, 80);
|
|
@@ -134,4 +153,4 @@ function gameRenderPost()
|
|
|
134
153
|
///////////////////////////////////////////////////////////////////////////////
|
|
135
154
|
// Startup LittleJS Engine with Box2D
|
|
136
155
|
|
|
137
|
-
box2dEngineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost);
|
|
156
|
+
box2dEngineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Little JS Box2d Demo Objects
|
|
3
|
+
- Object functions that use Box2D features
|
|
4
|
+
- Feel free to copy and paste these into your own project
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
10
|
+
// spawn object functions
|
|
11
|
+
|
|
12
|
+
function spawnBox(pos, size=1, color=WHITE, type=box2dBodyTypeDynamic, applyTexture=true, angle=0)
|
|
13
|
+
{
|
|
14
|
+
size = typeof size == 'number' ? vec2(size) : size; // square
|
|
15
|
+
const o = new Box2dObject(pos, size, applyTexture && tile(3), angle, color, type);
|
|
16
|
+
o.addBox(size);
|
|
17
|
+
return o;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function spawnCircle(pos, diameter=1, color=WHITE, type=box2dBodyTypeDynamic, applyTexture=true, angle=0)
|
|
21
|
+
{
|
|
22
|
+
const size = vec2(diameter);
|
|
23
|
+
const o = new Box2dObject(pos, size, applyTexture && tile(2), angle, color, type);
|
|
24
|
+
o.addCircle(diameter);
|
|
25
|
+
return o;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function spawnRandomPoly(pos, diameter=1, color=WHITE, type=box2dBodyTypeDynamic, angle=0)
|
|
29
|
+
{
|
|
30
|
+
const o = new Box2dObject(pos, vec2(), 0, angle, color, type);
|
|
31
|
+
o.addRandomPoly(diameter);
|
|
32
|
+
return o;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function spawnRandomObject(pos, scale=1, type=box2dBodyTypeDynamic, angle=0)
|
|
36
|
+
{
|
|
37
|
+
if (randInt(2))
|
|
38
|
+
{
|
|
39
|
+
const size = vec2(scale*rand(.5,1), scale*rand(.5,1));
|
|
40
|
+
return spawnBox(pos, size, randColor(), type, true, angle);
|
|
41
|
+
}
|
|
42
|
+
else
|
|
43
|
+
{
|
|
44
|
+
const diameter = scale*rand(.5,1);
|
|
45
|
+
return spawnCircle(pos, diameter, randColor(), type, true, angle);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function spawnPyramid(pos, count)
|
|
50
|
+
{
|
|
51
|
+
for (let i=count+1;i--;)
|
|
52
|
+
for (let j=i;j--;)
|
|
53
|
+
spawnBox(pos.add(vec2(j-i/2+.5,count-i+.5)), 1, randColor());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function spawnDominoes(pos, count, size=vec2(.5,2))
|
|
57
|
+
{
|
|
58
|
+
for (let i=count; i--;)
|
|
59
|
+
spawnBox(pos.add(vec2(i*size.y*.9,size.y/2)), size, randColor());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function spawnRandomEdges()
|
|
63
|
+
{
|
|
64
|
+
// edge list along bottom
|
|
65
|
+
const edgePoints = [];
|
|
66
|
+
edgePoints.push(vec2(40,0));
|
|
67
|
+
for (let i=40, y=0; i--;)
|
|
68
|
+
edgePoints.push(vec2(i, y=clamp(y+rand(-2,2),0,5)));
|
|
69
|
+
edgePoints.push(vec2(0,0));
|
|
70
|
+
const o = new Box2dObject(vec2(), vec2(), 0, 0, BLACK, box2dBodyTypeStatic);
|
|
71
|
+
o.addEdgeList(edgePoints);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function spawnRope(startPos, count, angle=PI, color=WHITE, size=vec2(.3,1))
|
|
75
|
+
{
|
|
76
|
+
let lastObject = groundObject;
|
|
77
|
+
for (let i=0; i<count; ++i)
|
|
78
|
+
{
|
|
79
|
+
const pos = startPos.add(size.multiply(vec2(0,i+.5)).rotate(-angle));
|
|
80
|
+
const o = spawnBox(pos, size, color, box2dBodyTypeDynamic, false, angle);
|
|
81
|
+
o.setFilterData(2, 2);
|
|
82
|
+
const anchorPos = pos.add(vec2(0,-size.y/2).rotate(-angle));
|
|
83
|
+
box2dCreateRevoluteJoint(lastObject, o, anchorPos);
|
|
84
|
+
lastObject = o;
|
|
85
|
+
}
|
|
86
|
+
const endPos = lastObject.localToWorld(vec2(0,-size.y/2));
|
|
87
|
+
box2dCreateRopeJoint(groundObject, lastObject, startPos, endPos);
|
|
88
|
+
return lastObject;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
92
|
+
|
|
93
|
+
// simple car vehicle using wheel joints
|
|
94
|
+
class CarObject extends Box2dObject
|
|
95
|
+
{
|
|
96
|
+
constructor(pos)
|
|
97
|
+
{
|
|
98
|
+
super(pos, vec2(), 0, 0, randColor());
|
|
99
|
+
const carPoints = [
|
|
100
|
+
vec2(-1.5,-.5),
|
|
101
|
+
vec2(1.5, -.5),
|
|
102
|
+
vec2(1.5, 0),
|
|
103
|
+
vec2(0, .9),
|
|
104
|
+
vec2(-1.15,.9),
|
|
105
|
+
vec2(-1.5, .2),
|
|
106
|
+
];
|
|
107
|
+
this.addPoly(carPoints);
|
|
108
|
+
|
|
109
|
+
{
|
|
110
|
+
// wheel settings
|
|
111
|
+
const diameter = .8;
|
|
112
|
+
const density = 1;
|
|
113
|
+
const friction = 2;
|
|
114
|
+
const restitution = 0;
|
|
115
|
+
const damping = .7;
|
|
116
|
+
const frequencyHz = 4;
|
|
117
|
+
const maxTorque = 50;
|
|
118
|
+
const sprite = tile(4);
|
|
119
|
+
|
|
120
|
+
// create wheels
|
|
121
|
+
this.wheels = [];
|
|
122
|
+
const makeWheel = (pos, isMotor) =>
|
|
123
|
+
{
|
|
124
|
+
const wheel = new Box2dObject(pos, vec2(diameter), sprite);
|
|
125
|
+
wheel.addCircle(diameter, vec2(), density, friction, restitution);
|
|
126
|
+
this.wheels.push(wheel);
|
|
127
|
+
const joint = box2dCreateWheelJoint(this, wheel);
|
|
128
|
+
joint.SetSpringDampingRatio(damping);
|
|
129
|
+
joint.SetSpringFrequencyHz(frequencyHz);
|
|
130
|
+
if (isMotor)
|
|
131
|
+
{
|
|
132
|
+
joint.SetMaxMotorTorque(maxTorque);
|
|
133
|
+
joint.EnableMotor(true);
|
|
134
|
+
this.wheelMotorJoint = joint;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
makeWheel(pos.add(vec2( 1, -.6)));
|
|
139
|
+
makeWheel(pos.add(vec2(-1, -.65)), true);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
applyMotorInput(input)
|
|
143
|
+
{
|
|
144
|
+
const maxSpeed = 40;
|
|
145
|
+
const brakeAmount = .8;
|
|
146
|
+
let s = this.wheelMotorJoint.GetMotorSpeed();
|
|
147
|
+
s = input ? clamp(s + input, -maxSpeed, maxSpeed) : s * brakeAmount;
|
|
148
|
+
this.wheelMotorJoint.SetMotorSpeed(s);
|
|
149
|
+
}
|
|
150
|
+
destroy()
|
|
151
|
+
{
|
|
152
|
+
this.wheels.forEach(o=>o && o.destroy());
|
|
153
|
+
super.destroy();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
158
|
+
|
|
159
|
+
// changes objects color when touched
|
|
160
|
+
class ContactTester extends Box2dObject
|
|
161
|
+
{
|
|
162
|
+
constructor(pos, size, color, contactColor, isCircle=true, isSensor=true)
|
|
163
|
+
{
|
|
164
|
+
super(pos, size, 0, 0, color, box2dBodyTypeStatic);
|
|
165
|
+
isCircle ? this.addCircle(size.x) : this.addBox(size);
|
|
166
|
+
this.setSensor(isSensor);
|
|
167
|
+
this.contactColor = contactColor;
|
|
168
|
+
}
|
|
169
|
+
beginContact(other) { other.color = this.contactColor; }
|
|
170
|
+
endContact(other) { other.color = WHITE; }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
174
|
+
|
|
175
|
+
// balanced recursive mobile object with revolve joints
|
|
176
|
+
class MobileObject extends Box2dObject
|
|
177
|
+
{
|
|
178
|
+
constructor(anchor, w, h, depth)
|
|
179
|
+
{
|
|
180
|
+
super(anchor, vec2(w, h), 0, 0, randColor());
|
|
181
|
+
this.addBox(vec2(h/4, h), vec2(0, -h/2));
|
|
182
|
+
this.childMobiles = [];
|
|
183
|
+
if (--depth)
|
|
184
|
+
{
|
|
185
|
+
// long box on bottom
|
|
186
|
+
this.addBox(vec2(w, h/4), vec2(0, -h));
|
|
187
|
+
for (let i=2; i--;)
|
|
188
|
+
{
|
|
189
|
+
// spawn smaller mobiles attached to each side
|
|
190
|
+
const a = anchor.add(vec2( w/(i?2:-2), -h));
|
|
191
|
+
const o = new MobileObject(a, w/2, h, depth);
|
|
192
|
+
box2dCreateRevoluteJoint(this, o, a);
|
|
193
|
+
this.childMobiles.push(o);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
destroy()
|
|
198
|
+
{
|
|
199
|
+
this.childMobiles.forEach(o=>o && o.destroy());
|
|
200
|
+
super.destroy();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
205
|
+
|
|
206
|
+
// pulley object renders a rope to connected point
|
|
207
|
+
class PulleyJointObjects extends Box2dObject
|
|
208
|
+
{
|
|
209
|
+
constructor(pos, size, color, connectionPos)
|
|
210
|
+
{
|
|
211
|
+
super(pos, size, tile(3), 0, color);
|
|
212
|
+
this.addBox(size);
|
|
213
|
+
this.connectionPos = connectionPos;
|
|
214
|
+
}
|
|
215
|
+
render()
|
|
216
|
+
{
|
|
217
|
+
// draw rope
|
|
218
|
+
const topPos = this.localToWorld(vec2(0,this.size.y/2));
|
|
219
|
+
drawLine(topPos, this.connectionPos, .2, BLACK);
|
|
220
|
+
super.render();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
225
|
+
|
|
226
|
+
// motor object renders a rope to connected point
|
|
227
|
+
class MotorJointObject extends Box2dObject
|
|
228
|
+
{
|
|
229
|
+
constructor(pos, size, color, otherObject)
|
|
230
|
+
{
|
|
231
|
+
super(pos, size, tile(4), 0, color);
|
|
232
|
+
this.addCircle(size.x);
|
|
233
|
+
this.connectionPos = pos;
|
|
234
|
+
const joint = box2dCreateMotorJoint(otherObject, this);
|
|
235
|
+
joint.SetMaxForce(500);
|
|
236
|
+
joint.SetMaxTorque(500);
|
|
237
|
+
}
|
|
238
|
+
render()
|
|
239
|
+
{
|
|
240
|
+
// draw rope
|
|
241
|
+
const width = 2/(1+this.pos.distance(this.connectionPos));
|
|
242
|
+
drawLine(this.pos, this.connectionPos, width, BLACK);
|
|
243
|
+
super.render();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
248
|
+
|
|
249
|
+
// soft body sim using grid of weld joints
|
|
250
|
+
class SoftBodyObject extends Box2dObject
|
|
251
|
+
{
|
|
252
|
+
constructor(pos, scale, sizeCount, color)
|
|
253
|
+
{
|
|
254
|
+
super(pos, vec2());
|
|
255
|
+
const nodeSize = sizeCount.subtract(vec2(1));
|
|
256
|
+
const spacing = scale.divide(nodeSize);
|
|
257
|
+
const objectDiameter = min(spacing.x, spacing.y);
|
|
258
|
+
this.sizeCount = sizeCount.copy();
|
|
259
|
+
|
|
260
|
+
// create nodes
|
|
261
|
+
this.nodes = [];
|
|
262
|
+
for (let y=sizeCount.y; y--;)
|
|
263
|
+
for (let x=sizeCount.y; x--;)
|
|
264
|
+
{
|
|
265
|
+
const mass = .1;
|
|
266
|
+
const center = vec2(x-nodeSize.x/2, y-nodeSize.y/2);
|
|
267
|
+
const p = pos.add(center.multiply(spacing));
|
|
268
|
+
const o = new Box2dObject(p, vec2(objectDiameter*1.1), tile(0), 0, color);
|
|
269
|
+
o.addCircle(objectDiameter);
|
|
270
|
+
o.setMass(mass);
|
|
271
|
+
o.setAngularDamping(10);
|
|
272
|
+
o.joints = [];
|
|
273
|
+
this.nodes.push(o);
|
|
274
|
+
}
|
|
275
|
+
// attach joints
|
|
276
|
+
for (let y=sizeCount.y; y--;)
|
|
277
|
+
for (let x=sizeCount.x; x--;)
|
|
278
|
+
{
|
|
279
|
+
const o = this.getNode(x, y);
|
|
280
|
+
const tryAddJoint = (xo, yo) =>
|
|
281
|
+
{
|
|
282
|
+
const o2 = this.getNode(x+xo, y+yo);
|
|
283
|
+
const joint = o2 ? box2dCreateWeldJoint(o, o2) : 0;
|
|
284
|
+
o.joints.push(joint);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// attach horizontal and vertical joints first
|
|
288
|
+
tryAddJoint(1, 0);
|
|
289
|
+
tryAddJoint(0, 1);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
render()
|
|
293
|
+
{
|
|
294
|
+
// fill inside background with a poly around the perimeter
|
|
295
|
+
const poly = [];
|
|
296
|
+
const sx = this.sizeCount.x;
|
|
297
|
+
const sy = this.sizeCount.y;
|
|
298
|
+
for (let x = 0; x < sx; ++x) poly.push(this.getNode(x, 0).pos);
|
|
299
|
+
for (let y = 0; y < sy.y; ++y) poly.push(this.getNode(sx-1, y).pos);
|
|
300
|
+
for (let x = sx; x--;) poly.push(this.getNode(x, sy-1).pos);
|
|
301
|
+
for (let y = sy; y--;) poly.push(this.getNode(0, y).pos);
|
|
302
|
+
box2dDrawPoly(vec2(), 0, poly, BLACK)
|
|
303
|
+
}
|
|
304
|
+
getNode(x, y)
|
|
305
|
+
{
|
|
306
|
+
if (vec2(x,y).arrayCheck(this.sizeCount))
|
|
307
|
+
return this.nodes[y*this.sizeCount.x+x];
|
|
308
|
+
}
|
|
309
|
+
destroy()
|
|
310
|
+
{
|
|
311
|
+
this.nodes.forEach(o=>o && o.destroy());
|
|
312
|
+
super.destroy();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
317
|
+
|
|
318
|
+
// cloth sim using grid of rope joints
|
|
319
|
+
class ClothObject extends Box2dObject
|
|
320
|
+
{
|
|
321
|
+
constructor(pos, scale, sizeCount, color, maxJointStress=10)
|
|
322
|
+
{
|
|
323
|
+
super(pos, vec2(), 0, 0, color, box2dBodyTypeStatic);
|
|
324
|
+
const nodeSize = sizeCount.subtract(vec2(1));
|
|
325
|
+
const spacing = scale.divide(nodeSize);
|
|
326
|
+
const objectDiameter = min(spacing.x, spacing.y);
|
|
327
|
+
this.sizeCount = sizeCount.copy();
|
|
328
|
+
this.maxJointStress = maxJointStress;
|
|
329
|
+
this.lineWidth = .1;
|
|
330
|
+
|
|
331
|
+
// create nodes
|
|
332
|
+
this.nodes = [];
|
|
333
|
+
for (let y=sizeCount.y; y--;)
|
|
334
|
+
for (let x=sizeCount.y; x--;)
|
|
335
|
+
{
|
|
336
|
+
const mass = .5;
|
|
337
|
+
const center = vec2(x-nodeSize.x/2, y-nodeSize.y/2);
|
|
338
|
+
const p = pos.add(center.multiply(spacing));
|
|
339
|
+
const o = new Box2dObject(p, vec2(.4), tile(0), 0, color);
|
|
340
|
+
o.addCircle(objectDiameter);
|
|
341
|
+
o.setFilterData(2, 2);
|
|
342
|
+
o.setLinearDamping(1);
|
|
343
|
+
o.setMass(mass);
|
|
344
|
+
o.joints = [];
|
|
345
|
+
this.nodes.push(o);
|
|
346
|
+
}
|
|
347
|
+
// attach joints
|
|
348
|
+
for (let pass=2; pass--;)
|
|
349
|
+
for (let y=sizeCount.y; y--;)
|
|
350
|
+
for (let x=sizeCount.x; x--;)
|
|
351
|
+
{
|
|
352
|
+
// alternate direction each row for stability
|
|
353
|
+
const d = y%2 ? 1 : -1;
|
|
354
|
+
const x2 = d>1 ? x : sizeCount.x-1-x;
|
|
355
|
+
const o = this.getNode(x2, y);
|
|
356
|
+
const tryAddJoint = (xo, yo) =>
|
|
357
|
+
{
|
|
358
|
+
const o2 = this.getNode(x2+xo, y+yo);
|
|
359
|
+
const joint = o2 ? box2dCreateRopeJoint(o, o2) : 0;
|
|
360
|
+
o.joints.push(joint);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (pass)
|
|
364
|
+
{
|
|
365
|
+
// attach horizontal and vertical joints first
|
|
366
|
+
tryAddJoint(d, 0);
|
|
367
|
+
tryAddJoint(0, 1);
|
|
368
|
+
}
|
|
369
|
+
else
|
|
370
|
+
{
|
|
371
|
+
// attach cross joints
|
|
372
|
+
tryAddJoint(-d, 1);
|
|
373
|
+
tryAddJoint( d, 1);
|
|
374
|
+
|
|
375
|
+
// attach pin joint to top row
|
|
376
|
+
y || box2dCreatePinJoint(this, this.getNode(x, y));
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
getNode(x, y)
|
|
381
|
+
{
|
|
382
|
+
if (vec2(x,y).arrayCheck(this.sizeCount))
|
|
383
|
+
return this.nodes[y*this.sizeCount.x+x];
|
|
384
|
+
}
|
|
385
|
+
update()
|
|
386
|
+
{
|
|
387
|
+
for (let y=this.sizeCount.y; y--;)
|
|
388
|
+
for (let x=this.sizeCount.x; x--;)
|
|
389
|
+
{
|
|
390
|
+
const o = this.getNode(x, y)
|
|
391
|
+
if (!o) continue;
|
|
392
|
+
|
|
393
|
+
// check node joints
|
|
394
|
+
const joints = o.joints;
|
|
395
|
+
for (let i=joints.length; i--;)
|
|
396
|
+
{
|
|
397
|
+
const j = joints[i];
|
|
398
|
+
if (!j) continue;
|
|
399
|
+
|
|
400
|
+
const oA = j.GetBodyA().object;
|
|
401
|
+
const oB = j.GetBodyB().object;
|
|
402
|
+
const d2 = oA.pos.distanceSquared(oB.pos);
|
|
403
|
+
const maxLength2 = j.GetMaxLength()**2;
|
|
404
|
+
const stress = vec2(j.GetReactionForce(1)).length();
|
|
405
|
+
if (stress > this.maxJointStress || d2 > maxLength2*4)
|
|
406
|
+
{
|
|
407
|
+
// remove stessed joint
|
|
408
|
+
box2dDestroyJoint(j);
|
|
409
|
+
joints[i] = 0;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (!o.hasJoints())
|
|
413
|
+
{
|
|
414
|
+
// destroy if not connected to anything
|
|
415
|
+
o.destroy();
|
|
416
|
+
this.nodes[y*this.sizeCount.x+x] = 0;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
super.update();
|
|
420
|
+
}
|
|
421
|
+
render()
|
|
422
|
+
{
|
|
423
|
+
// draw connections
|
|
424
|
+
for (const o of this.nodes)
|
|
425
|
+
{
|
|
426
|
+
if (!o) continue;
|
|
427
|
+
for (const joint of o.joints)
|
|
428
|
+
{
|
|
429
|
+
if (!joint) continue;
|
|
430
|
+
const oA = joint.GetBodyA().object;
|
|
431
|
+
const oB = joint.GetBodyB().object;
|
|
432
|
+
drawLine(oA.pos, oB.pos, this.lineWidth, BLACK);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
destroy()
|
|
437
|
+
{
|
|
438
|
+
this.nodes.forEach(o=>o && o.destroy());
|
|
439
|
+
super.destroy();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
444
|
+
// Effects
|
|
445
|
+
|
|
446
|
+
const sound_click = new Sound([.2,.1,,,,.01,,,,,,,,,,,,,,,-500]); // Loaded Sound 0
|
|
447
|
+
const sound_explosion = new Sound([.5,.2,72,.01,.01,.2,4,,,,,,,1,,.5,.1,.5,.02]);
|
|
448
|
+
|
|
449
|
+
function explosion(pos, radius=3, strength=300)
|
|
450
|
+
{
|
|
451
|
+
sound_explosion.play(pos);
|
|
452
|
+
const objects = box2dCircleCastAll(pos, (radius*2));
|
|
453
|
+
const newColor = randColor();
|
|
454
|
+
for (const o of objects)
|
|
455
|
+
{
|
|
456
|
+
const p = percent(o.pos.distance(pos), 2*radius, radius);
|
|
457
|
+
const force = o.pos.subtract(pos).normalize(p*radius*strength);
|
|
458
|
+
o.applyForce(force);
|
|
459
|
+
o.color = newColor;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// smoke
|
|
463
|
+
new ParticleEmitter(
|
|
464
|
+
pos, 0, // pos, angle
|
|
465
|
+
radius/2, .2, 50*radius, PI,// emitSize, emitTime, emitRate, emiteCone
|
|
466
|
+
tile(1), // tileInfo
|
|
467
|
+
hsl(0,0,0), hsl(0,0,0), // colorStartA, colorStartB
|
|
468
|
+
hsl(0,0,0,0), hsl(0,0,0,0), // colorEndA, colorEndB
|
|
469
|
+
1, 1, 2, .1, .1, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
470
|
+
.9, 1, -.5, PI, .1, // damp, angleDamp, gravity, particleCone, fade
|
|
471
|
+
1, 0, 0, 0, 1e8 // randomness, collide, additive, colorLinear, renderOrder
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
// fire
|
|
475
|
+
new ParticleEmitter(
|
|
476
|
+
pos, 0, // pos, angle
|
|
477
|
+
radius, .1, 100*radius, PI, // emitSize, emitTime, emitRate, emiteCone
|
|
478
|
+
tile(1), // tileInfo
|
|
479
|
+
hsl(0,1,.5), hsl(.15,1,.5), // colorStartA, colorStartB
|
|
480
|
+
hsl(0,1,.5,0), hsl(.1, 1,.5,0), // colorEndA, colorEndB
|
|
481
|
+
.7, 1, .5, .1, .1, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
482
|
+
.9, 1, 0, PI, .05, // damp, angleDamp, gravity, particleCone, fade
|
|
483
|
+
1, 0, 1, 0, 1e9 // randomness, collide, additive, colorLinear, renderOrder
|
|
484
|
+
);
|
|
485
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
<!DOCTYPE html><head>
|
|
2
|
-
<title>
|
|
2
|
+
<title>LittleJS Box2D Example</title>
|
|
3
|
+
<meta charset=utf-8>
|
|
3
4
|
<meta name=apple-mobile-web-app-capable content=yes>
|
|
4
5
|
<meta name=mobile-web-app-capable content=yes>
|
|
5
6
|
<link rel=icon type=image/png href=../favicon.png>
|
|
6
7
|
</head><body>
|
|
7
8
|
|
|
8
9
|
<script src=../../dist/littlejs.js></script>
|
|
9
|
-
<script src=../../plugins/Box2D_v2.3.1_min.wasm.js?
|
|
10
|
-
<script src=../../plugins/box2d.js?
|
|
11
|
-
<script src=scenes.js?
|
|
12
|
-
<script src=
|
|
10
|
+
<script src=../../plugins/Box2D_v2.3.1_min.wasm.js?1100></script>
|
|
11
|
+
<script src=../../plugins/box2d.js?1100></script>
|
|
12
|
+
<script src=scenes.js?1100></script>
|
|
13
|
+
<script src=gameObjects.js?1100></script>
|
|
14
|
+
<script src=game.js?1100></script>
|