littlejsengine 1.1.26 → 1.2.9
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 +7 -9
- package/build.bat +1 -1
- package/engine/engine.all.js +369 -377
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.release.js +338 -147
- package/engine/engine.js +7 -7
- package/engine/engineAudio.js +2 -2
- package/engine/engineBuild.bat +9 -1
- package/engine/engineDebug.js +32 -231
- package/engine/engineDraw.js +130 -20
- package/engine/engineFont.png +0 -0
- package/engine/engineInput.js +32 -9
- package/engine/engineMedals.js +7 -4
- package/engine/engineObject.js +30 -11
- package/engine/engineParticles.js +5 -3
- package/engine/engineSettings.js +5 -0
- package/engine/engineTileLayer.js +17 -14
- package/engine/engineUtilities.js +50 -18
- package/engine/engineWebGL.js +53 -59
- package/engine/index.d.ts +1644 -0
- package/examples/breakout/game.js +9 -7
- package/examples/breakout/gameObjects.js +3 -3
- package/examples/breakout/index.html +3 -3
- package/examples/particles/index.html +366 -0
- package/examples/particles/tiles.png +0 -0
- package/examples/platformer/game.js +5 -5
- package/examples/platformer/gameEffects.js +10 -10
- package/examples/platformer/gameLevel.js +3 -3
- package/examples/platformer/gameObjects.js +3 -3
- package/examples/platformer/gamePlayer.js +11 -6
- package/examples/platformer/index.html +6 -6
- package/examples/platformer/tiles.png +0 -0
- package/examples/puzzle/game.js +3 -3
- package/examples/puzzle/index.html +2 -2
- package/examples/screenshot.jpg +0 -0
- package/examples/stress/index.html +4 -4
- package/game.js +4 -7
- package/index.html +13 -13
- package/package.json +8 -1
- package/engine/engineBuildSetup.bat +0 -6
package/engine/engineInput.js
CHANGED
|
@@ -159,7 +159,7 @@ const mouseToScreen = (mousePos)=>
|
|
|
159
159
|
|
|
160
160
|
const rect = mainCanvas.getBoundingClientRect();
|
|
161
161
|
return mainCanvasSize.multiply(
|
|
162
|
-
vec2(percent(mousePos.x, rect.
|
|
162
|
+
vec2(percent(mousePos.x, rect.left, rect.right), percent(mousePos.y, rect.top, rect.bottom)));
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -200,8 +200,8 @@ function gamepadsUpdate()
|
|
|
200
200
|
// read clamp dead zone of analog sticks
|
|
201
201
|
const deadZone = .3, deadZoneMax = .8;
|
|
202
202
|
const applyDeadZone = (v)=>
|
|
203
|
-
v > deadZone ? percent( v,
|
|
204
|
-
v < -deadZone ? -percent(-v,
|
|
203
|
+
v > deadZone ? percent( v, deadZone, deadZoneMax) :
|
|
204
|
+
v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
|
|
205
205
|
|
|
206
206
|
// read analog sticks
|
|
207
207
|
for (let j = 0; j < gamepad.axes.length-1; j+=2)
|
|
@@ -327,7 +327,14 @@ function touchGamepadCreate()
|
|
|
327
327
|
if (touchPos.distance(stickCenter) < touchGamepadSize)
|
|
328
328
|
{
|
|
329
329
|
// virtual analog stick
|
|
330
|
-
|
|
330
|
+
if (touchGamepadAnalog)
|
|
331
|
+
touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
|
|
332
|
+
else
|
|
333
|
+
{
|
|
334
|
+
// 8 way dpad
|
|
335
|
+
const angle = touchPos.subtract(stickCenter).angle();
|
|
336
|
+
touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
|
|
337
|
+
}
|
|
331
338
|
}
|
|
332
339
|
else if (touchPos.distance(buttonCenter) < touchGamepadSize)
|
|
333
340
|
{
|
|
@@ -351,7 +358,7 @@ function touchGamepadRender()
|
|
|
351
358
|
return;
|
|
352
359
|
|
|
353
360
|
// fade off when not touching or paused
|
|
354
|
-
const alpha = percent(touchGamepadTimer.get(),
|
|
361
|
+
const alpha = percent(touchGamepadTimer.get(), 4, 3);
|
|
355
362
|
if (!alpha || paused)
|
|
356
363
|
return;
|
|
357
364
|
|
|
@@ -362,12 +369,28 @@ function touchGamepadRender()
|
|
|
362
369
|
overlayContext.lineWidth = 3;
|
|
363
370
|
|
|
364
371
|
// draw left analog stick
|
|
365
|
-
overlayContext.fillStyle =
|
|
372
|
+
overlayContext.fillStyle = touchGamepadStick.lengthSquared() > 0 ? '#fff' : '#000';
|
|
366
373
|
overlayContext.beginPath();
|
|
367
|
-
overlayContext.arc(touchGamepadSize, mainCanvasSize.y-touchGamepadSize, touchGamepadSize/2, 0,9);
|
|
368
|
-
overlayContext.fill();
|
|
369
|
-
overlayContext.stroke();
|
|
370
374
|
|
|
375
|
+
const leftCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
376
|
+
if (touchGamepadAnalog)
|
|
377
|
+
{
|
|
378
|
+
overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize/2, 0, 9);
|
|
379
|
+
overlayContext.fill();
|
|
380
|
+
overlayContext.stroke();
|
|
381
|
+
}
|
|
382
|
+
else // draw cross shaped gamepad
|
|
383
|
+
{
|
|
384
|
+
for(let i=10; i--;)
|
|
385
|
+
{
|
|
386
|
+
const angle = i*PI/4;
|
|
387
|
+
overlayContext.arc(leftCenter.x, leftCenter.y,touchGamepadSize*.6, angle + PI/8, angle + PI/8);
|
|
388
|
+
i%2 && overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize*.33, angle, angle);
|
|
389
|
+
i==1 && overlayContext.fill();
|
|
390
|
+
}
|
|
391
|
+
overlayContext.stroke();
|
|
392
|
+
}
|
|
393
|
+
|
|
371
394
|
// draw right face buttons
|
|
372
395
|
const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
373
396
|
for (let i=4; i--;)
|
package/engine/engineMedals.js
CHANGED
|
@@ -35,7 +35,7 @@ function medalsInit(saveName)
|
|
|
35
35
|
{
|
|
36
36
|
// check if medals are unlocked
|
|
37
37
|
medalsSaveName = saveName;
|
|
38
|
-
debugMedals || medals.forEach(medal=>
|
|
38
|
+
debugMedals || medals.forEach(medal=> localStorage[medal.storageKey()]);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -224,6 +224,9 @@ class Newgrounds
|
|
|
224
224
|
const scoreboardResult = this.call('ScoreBoard.getBoards');
|
|
225
225
|
this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
|
|
226
226
|
debugMedals && console.log(this.scoreboards);
|
|
227
|
+
|
|
228
|
+
const keepAliveMS = 5 * 60 * 1e3;
|
|
229
|
+
setInterval(()=>this.call('Gateway.ping', 0, 1), keepAliveMS);
|
|
227
230
|
}
|
|
228
231
|
|
|
229
232
|
/** Send message to unlock a medal by id
|
|
@@ -235,9 +238,6 @@ class Newgrounds
|
|
|
235
238
|
* @param {Number} value - The score value */
|
|
236
239
|
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, 1); }
|
|
237
240
|
|
|
238
|
-
/** Send message to log a view */
|
|
239
|
-
logView() { return this.call('App.logView', {'host':this.host}, 1); }
|
|
240
|
-
|
|
241
241
|
/** Get scores from a scoreboard
|
|
242
242
|
* @param {Number} id - The scoreboard id
|
|
243
243
|
* @param {String} [user=0] - A user's id or name
|
|
@@ -249,6 +249,9 @@ class Newgrounds
|
|
|
249
249
|
getScores(id, user=0, social=0, skip=0, limit=10)
|
|
250
250
|
{ return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
|
|
251
251
|
|
|
252
|
+
/** Send message to log a view */
|
|
253
|
+
logView() { return this.call('App.logView', {'host':this.host}, 1); }
|
|
254
|
+
|
|
252
255
|
/** Send a message to call a component of the Newgrounds API
|
|
253
256
|
* @param {String} component - Name of the component
|
|
254
257
|
* @param {Object} [parameters=0] - Parameters to use for call
|
package/engine/engineObject.js
CHANGED
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
class EngineObject
|
|
33
33
|
{
|
|
34
34
|
/** Create an engine object and adds it to the list of objects
|
|
35
|
-
* @param {Vector2} [position=new Vector2(
|
|
35
|
+
* @param {Vector2} [position=new Vector2()] - World space position of the object
|
|
36
36
|
* @param {Vector2} [size=objectDefaultSize] - World space size of the object
|
|
37
37
|
* @param {Number} [tileIndex=-1] - Tile to use to render object, untextured if -1
|
|
38
38
|
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
|
|
39
|
-
* @param {Number} [angle=0] - Angle
|
|
39
|
+
* @param {Number} [angle=0] - Angle the object is rotated by
|
|
40
40
|
* @param {Color} [color] - Color to apply to tile when rendered
|
|
41
41
|
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
42
42
|
*/
|
|
@@ -77,12 +77,15 @@ class EngineObject
|
|
|
77
77
|
this.gravityScale = 1;
|
|
78
78
|
/** @property {Number} [renderOrder=0] - Objects are sorted by render order */
|
|
79
79
|
this.renderOrder = renderOrder;
|
|
80
|
+
/** @property {Vector2} [velocity=new Vector2()] - Velocity of the object */
|
|
81
|
+
this.velocity = new Vector2();
|
|
82
|
+
/** @property {Number} [angleVelocity=0] - Angular velocity of the object */
|
|
83
|
+
this.angleVelocity = 0;
|
|
80
84
|
|
|
81
85
|
// init other internal object stuff
|
|
82
86
|
this.spawnTime = time;
|
|
83
|
-
this.velocity = vec2(this.collideSolidObjects = this.angleVelocity = 0);
|
|
84
|
-
this.collideTiles = 1;
|
|
85
87
|
this.children = [];
|
|
88
|
+
this.collideTiles = 1;
|
|
86
89
|
|
|
87
90
|
// add to list of objects
|
|
88
91
|
engineObjects.push(this);
|
|
@@ -101,8 +104,8 @@ class EngineObject
|
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
// limit max speed to prevent missing collisions
|
|
104
|
-
this.velocity.x = clamp(this.velocity.x, objectMaxSpeed,
|
|
105
|
-
this.velocity.y = clamp(this.velocity.y, objectMaxSpeed,
|
|
107
|
+
this.velocity.x = clamp(this.velocity.x, -objectMaxSpeed, objectMaxSpeed);
|
|
108
|
+
this.velocity.y = clamp(this.velocity.y, -objectMaxSpeed, objectMaxSpeed);
|
|
106
109
|
|
|
107
110
|
// apply physics
|
|
108
111
|
const oldPos = this.pos.copy();
|
|
@@ -192,8 +195,8 @@ class EngineObject
|
|
|
192
195
|
|
|
193
196
|
// lerp betwen elastic or inelastic based on elasticity
|
|
194
197
|
const elasticity = max(this.elasticity, o.elasticity);
|
|
195
|
-
this.velocity.y = lerp(elasticity,
|
|
196
|
-
o.velocity.y = lerp(elasticity,
|
|
198
|
+
this.velocity.y = lerp(elasticity, inelastic, elastic0);
|
|
199
|
+
o.velocity.y = lerp(elasticity, inelastic, elastic1);
|
|
197
200
|
}
|
|
198
201
|
}
|
|
199
202
|
if (!smallStepUp && (isBlockedX || !isBlockedY)) // resolve x collision
|
|
@@ -213,8 +216,8 @@ class EngineObject
|
|
|
213
216
|
|
|
214
217
|
// lerp betwen elastic or inelastic based on elasticity
|
|
215
218
|
const elasticity = max(this.elasticity, o.elasticity);
|
|
216
|
-
this.velocity.x = lerp(elasticity,
|
|
217
|
-
o.velocity.x = lerp(elasticity,
|
|
219
|
+
this.velocity.x = lerp(elasticity, inelastic, elastic0);
|
|
220
|
+
o.velocity.x = lerp(elasticity, inelastic, elastic1);
|
|
218
221
|
}
|
|
219
222
|
else // bounce if other object is fixed
|
|
220
223
|
this.velocity.x *= -this.elasticity;
|
|
@@ -244,7 +247,7 @@ class EngineObject
|
|
|
244
247
|
|
|
245
248
|
// adjust next velocity to settle on ground
|
|
246
249
|
const o = (oldPos.y - this.size.y/2|0) - (oldPos.y - this.size.y/2);
|
|
247
|
-
if (o < 0 && o >
|
|
250
|
+
if (o < 0 && o > this.damping * this.velocity.y + gravity * this.gravityScale)
|
|
248
251
|
this.velocity.y = this.damping ? (o - gravity * this.gravityScale) / this.damping : 0;
|
|
249
252
|
|
|
250
253
|
// move to previous position
|
|
@@ -349,4 +352,20 @@ class EngineObject
|
|
|
349
352
|
this.isSolid = isSolid;
|
|
350
353
|
this.collideTiles = collideTiles;
|
|
351
354
|
}
|
|
355
|
+
|
|
356
|
+
toString()
|
|
357
|
+
{
|
|
358
|
+
let text = 'type = ' + this.constructor.name;
|
|
359
|
+
if (this.pos.x || this.pos.y)
|
|
360
|
+
text += '\npos = ' + this.pos;
|
|
361
|
+
if (this.velocity.x || this.velocity.y)
|
|
362
|
+
text += '\nvelocity = ' + this.velocity;
|
|
363
|
+
if (this.size.x || this.size.y)
|
|
364
|
+
text += '\nsize = ' + this.size;
|
|
365
|
+
if (this.angle)
|
|
366
|
+
text += '\nangle = ' + this.angle.toFixed(3);
|
|
367
|
+
if (this.color)
|
|
368
|
+
text += '\ncolor = ' + this.color;
|
|
369
|
+
return text;
|
|
370
|
+
}
|
|
352
371
|
}
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
* let pos = vec2(2,3);
|
|
16
16
|
* let particleEmiter = new ParticleEmitter
|
|
17
17
|
* (
|
|
18
|
-
* pos, 1, 0, 500, PI, // pos, emitSize, emitTime, emitRate, emiteCone
|
|
18
|
+
* pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
|
|
19
19
|
* 0, vec2(16), // tileIndex, tileSize
|
|
20
|
-
* new Color,
|
|
20
|
+
* new Color(1,1,1), new Color(0,0,0), // colorStartA, colorStartB
|
|
21
21
|
* new Color(1,1,1,0), new Color(0,0,0,0), // colorEndA, colorEndB
|
|
22
22
|
* 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
|
|
23
23
|
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
|
|
@@ -28,6 +28,7 @@ class ParticleEmitter extends EngineObject
|
|
|
28
28
|
{
|
|
29
29
|
/** Create a particle system with the given settings
|
|
30
30
|
* @param {Vector2} position - World space position of the emitter
|
|
31
|
+
* @param {Number} [angle=0] - Angle to emit the particles
|
|
31
32
|
* @param {Number} [emitSize=0] - World space size of the emitter (float for circle diameter, vec2 for rect)
|
|
32
33
|
* @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
|
|
33
34
|
* @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
|
|
@@ -57,6 +58,7 @@ class ParticleEmitter extends EngineObject
|
|
|
57
58
|
constructor
|
|
58
59
|
(
|
|
59
60
|
pos,
|
|
61
|
+
angle,
|
|
60
62
|
emitSize = 0,
|
|
61
63
|
emitTime = 0,
|
|
62
64
|
emitRate = 100,
|
|
@@ -84,7 +86,7 @@ class ParticleEmitter extends EngineObject
|
|
|
84
86
|
renderOrder = additive ? 1e9 : 0
|
|
85
87
|
)
|
|
86
88
|
{
|
|
87
|
-
super(pos, new Vector2, tileIndex, tileSize,
|
|
89
|
+
super(pos, new Vector2, tileIndex, tileSize, angle, undefined, renderOrder);
|
|
88
90
|
|
|
89
91
|
// emitter settings
|
|
90
92
|
/** @property {Number} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
package/engine/engineSettings.js
CHANGED
|
@@ -145,6 +145,11 @@ let inputWASDEmulateDirection = 1;
|
|
|
145
145
|
* @memberof Settings */
|
|
146
146
|
let touchGamepadEnable = 0;
|
|
147
147
|
|
|
148
|
+
/** True if touch gamepad should be analog stick or false to use if 8 way dpad
|
|
149
|
+
* @default
|
|
150
|
+
* @memberof Settings */
|
|
151
|
+
let touchGamepadAnalog = 1;
|
|
152
|
+
|
|
148
153
|
/** Size of virutal gamepad for touch devices in pixels
|
|
149
154
|
* @default
|
|
150
155
|
* @memberof Settings */
|
|
@@ -153,14 +153,14 @@ class TileLayerData
|
|
|
153
153
|
*/
|
|
154
154
|
class TileLayer extends EngineObject
|
|
155
155
|
{
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
156
|
+
/** Create a tile layer object
|
|
157
|
+
* @param {Vector2} [position=new Vector2()] - World space position
|
|
158
|
+
* @param {Vector2} [size=tileCollisionSize] - World space size
|
|
159
|
+
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
|
|
160
|
+
* @param {Vector2} [scale=new Vector2(1,1)] - How much to scale this layer when rendered
|
|
161
|
+
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
162
|
+
*/
|
|
163
|
+
constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
|
|
164
164
|
{
|
|
165
165
|
super(pos, size, -1, tileSize, 0, undefined, renderOrder);
|
|
166
166
|
|
|
@@ -218,17 +218,19 @@ class TileLayer extends EngineObject
|
|
|
218
218
|
);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
/** Draw all the tile data to an offscreen canvas
|
|
221
|
+
/** Draw all the tile data to an offscreen canvas
|
|
222
|
+
* - This may be slow in some browsers
|
|
223
|
+
*/
|
|
222
224
|
redraw()
|
|
223
225
|
{
|
|
224
|
-
this.redrawStart();
|
|
226
|
+
this.redrawStart(1);
|
|
225
227
|
this.drawAllTileData();
|
|
226
228
|
this.redrawEnd();
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
/** Call to start the redraw process
|
|
230
|
-
* @param {Boolean} [clear=
|
|
231
|
-
redrawStart(clear =
|
|
232
|
+
* @param {Boolean} [clear=0] - Should it clear the canvas before drawing */
|
|
233
|
+
redrawStart(clear = 0)
|
|
232
234
|
{
|
|
233
235
|
if (clear)
|
|
234
236
|
{
|
|
@@ -318,7 +320,7 @@ class TileLayer extends EngineObject
|
|
|
318
320
|
if (tileIndex < 0)
|
|
319
321
|
{
|
|
320
322
|
// untextured
|
|
321
|
-
context.fillStyle = color
|
|
323
|
+
context.fillStyle = color;
|
|
322
324
|
context.fillRect(-.5, -.5, 1, 1);
|
|
323
325
|
}
|
|
324
326
|
else
|
|
@@ -337,5 +339,6 @@ class TileLayer extends EngineObject
|
|
|
337
339
|
* @param {Vector2} [size=new Vector2(1,1)]
|
|
338
340
|
* @param {Color} [color=new Color(1,1,1)]
|
|
339
341
|
* @param {Number} [angle=0] */
|
|
340
|
-
drawRect(pos, size, color, angle)
|
|
342
|
+
drawRect(pos, size, color, angle)
|
|
343
|
+
{ this.drawTile(pos, size, -1, 0, color, angle); }
|
|
341
344
|
}
|
|
@@ -49,27 +49,27 @@ const mod = (a, b=1)=> ((a % b) + b) % b;
|
|
|
49
49
|
|
|
50
50
|
/** Clamps the value beween max and min
|
|
51
51
|
* @param {Number} value
|
|
52
|
-
* @param {Number} [max=1]
|
|
53
52
|
* @param {Number} [min=0]
|
|
53
|
+
* @param {Number} [max=1]
|
|
54
54
|
* @return {Number}
|
|
55
55
|
* @memberof Utilities */
|
|
56
|
-
const clamp = (v,
|
|
56
|
+
const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
|
|
57
57
|
|
|
58
58
|
/** Returns what percentage the value is between max and min
|
|
59
59
|
* @param {Number} value
|
|
60
|
-
* @param {Number} [max=1]
|
|
61
60
|
* @param {Number} [min=0]
|
|
61
|
+
* @param {Number} [max=1]
|
|
62
62
|
* @return {Number}
|
|
63
63
|
* @memberof Utilities */
|
|
64
|
-
const percent = (v,
|
|
64
|
+
const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
|
|
65
65
|
|
|
66
66
|
/** Linearly interpolates the percent value between max and min
|
|
67
67
|
* @param {Number} percent
|
|
68
|
-
* @param {Number} [max=1]
|
|
69
68
|
* @param {Number} [min=0]
|
|
69
|
+
* @param {Number} [max=1]
|
|
70
70
|
* @return {Number}
|
|
71
71
|
* @memberof Utilities */
|
|
72
|
-
const lerp = (p,
|
|
72
|
+
const lerp = (p, min=0, max=1)=> min + clamp(p) * (max-min);
|
|
73
73
|
|
|
74
74
|
/** Applies smoothstep function to the percentage value
|
|
75
75
|
* @param {Number} value
|
|
@@ -84,11 +84,11 @@ const smoothStep = (p)=> p * p * (3 - 2 * p);
|
|
|
84
84
|
const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
|
|
85
85
|
|
|
86
86
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
87
|
-
* @param {Vector2} pointA
|
|
88
|
-
* @param {Vector2} sizeA
|
|
89
|
-
* @param {Vector2} pointB
|
|
90
|
-
* @param {Vector2} sizeB
|
|
91
|
-
* @return {Boolean}
|
|
87
|
+
* @param {Vector2} pointA - Center of box A
|
|
88
|
+
* @param {Vector2} sizeA - Size of box A
|
|
89
|
+
* @param {Vector2} pointB - Center of box B
|
|
90
|
+
* @param {Vector2} [sizeB] - Size of box B
|
|
91
|
+
* @return {Boolean} - True if overlapping
|
|
92
92
|
* @memberof Utilities */
|
|
93
93
|
const isOverlapping = (pA, sA, pB, sB)=> abs(pA.x - pB.x)*2 < sA.x + sB.x & abs(pA.y - pB.y)*2 < sA.y + sB.y;
|
|
94
94
|
|
|
@@ -311,7 +311,13 @@ class Vector2
|
|
|
311
311
|
/** Returns true if this vector is within the bounds of an array size passed in
|
|
312
312
|
* @param {Vector2} arraySize
|
|
313
313
|
* @return {Boolean} */
|
|
314
|
-
arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
314
|
+
arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
315
|
+
|
|
316
|
+
/** Returns this vector expressed as a string
|
|
317
|
+
* @param {float} digits - precision to display
|
|
318
|
+
* @return {String} */
|
|
319
|
+
toString(digits=3)
|
|
320
|
+
{ return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`; }
|
|
315
321
|
}
|
|
316
322
|
|
|
317
323
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -382,7 +388,7 @@ class Color
|
|
|
382
388
|
* @return {Color} */
|
|
383
389
|
lerp(c, p) { return this.add(c.subtract(this).scale(clamp(p))); }
|
|
384
390
|
|
|
385
|
-
/** Sets this color given a hue, saturation, lightness
|
|
391
|
+
/** Sets this color given a hue, saturation, lightness, and alpha
|
|
386
392
|
* @param {Number} [hue=0]
|
|
387
393
|
* @param {Number} [saturation=0]
|
|
388
394
|
* @param {Number} [lightness=1]
|
|
@@ -418,21 +424,43 @@ class Color
|
|
|
418
424
|
).clamp();
|
|
419
425
|
}
|
|
420
426
|
|
|
421
|
-
/** Returns this color expressed as an
|
|
427
|
+
/** Returns this color expressed as an CSS color value
|
|
422
428
|
* @return {String} */
|
|
423
|
-
|
|
429
|
+
toString()
|
|
424
430
|
{
|
|
425
431
|
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
|
|
426
432
|
return `rgb(${this.r*255|0},${this.g*255|0},${this.b*255|0},${this.a})`;
|
|
427
433
|
}
|
|
428
434
|
|
|
429
|
-
/** Returns this color expressed as 32 bit integer value
|
|
435
|
+
/** Returns this color expressed as 32 bit integer RGBA value
|
|
430
436
|
* @return {Number} */
|
|
431
437
|
rgbaInt()
|
|
432
438
|
{
|
|
433
439
|
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
|
|
434
440
|
return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
|
|
435
441
|
}
|
|
442
|
+
|
|
443
|
+
/** Returns this color expressed as a hex code
|
|
444
|
+
* @return {String} */
|
|
445
|
+
hex()
|
|
446
|
+
{
|
|
447
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
448
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/** Set this color from a hex code
|
|
452
|
+
* @param {String} hex - html hex code
|
|
453
|
+
* @return {Color} */
|
|
454
|
+
setHex(hex)
|
|
455
|
+
{
|
|
456
|
+
const fromHex = (a)=> parseInt(hex.slice(a,a+2), 16)/255;
|
|
457
|
+
this.r = fromHex(1);
|
|
458
|
+
this.g = fromHex(3),
|
|
459
|
+
this.b = fromHex(5);
|
|
460
|
+
this.a = 1;
|
|
461
|
+
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
|
|
462
|
+
return this;
|
|
463
|
+
}
|
|
436
464
|
}
|
|
437
465
|
|
|
438
466
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -469,7 +497,7 @@ class Timer
|
|
|
469
497
|
|
|
470
498
|
/** Returns true if set and elapsed
|
|
471
499
|
* @return {Boolean} */
|
|
472
|
-
elapsed() { return time >
|
|
500
|
+
elapsed() { return time > this.time; }
|
|
473
501
|
|
|
474
502
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
475
503
|
* @return {Number} */
|
|
@@ -477,5 +505,9 @@ class Timer
|
|
|
477
505
|
|
|
478
506
|
/** Get percentage elapsed based on time it was set to, returns 0 if not set
|
|
479
507
|
* @return {Number} */
|
|
480
|
-
getPercent() { return this.isSet()? percent(this.time - time,
|
|
508
|
+
getPercent() { return this.isSet()? percent(this.time - time, this.setTime, 0) : 0; }
|
|
509
|
+
|
|
510
|
+
/** Returns this timer expressed as a string
|
|
511
|
+
* @return {String} */
|
|
512
|
+
toString() { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); }
|
|
481
513
|
}
|
package/engine/engineWebGL.js
CHANGED
|
@@ -38,8 +38,8 @@ function glInit()
|
|
|
38
38
|
|
|
39
39
|
// create the canvas and tile texture
|
|
40
40
|
glCanvas = document.createElement('canvas');
|
|
41
|
-
glContext = glCanvas.getContext('webgl');
|
|
42
|
-
glCanvas.style =
|
|
41
|
+
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
42
|
+
glCanvas.style = styleCanvas;
|
|
43
43
|
glTileTexture = glCreateTexture(tileImage);
|
|
44
44
|
|
|
45
45
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
|
@@ -47,25 +47,24 @@ function glInit()
|
|
|
47
47
|
|
|
48
48
|
// setup vertex and fragment shaders
|
|
49
49
|
glShader = glCreateProgram(
|
|
50
|
-
'precision highp float;'+
|
|
51
|
-
'uniform mat4 m;'+
|
|
52
|
-
'attribute
|
|
53
|
-
'attribute
|
|
54
|
-
'
|
|
55
|
-
'varying
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'
|
|
59
|
-
'
|
|
60
|
-
'}' // end of shader
|
|
50
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
51
|
+
'uniform mat4 m;'+ // transform matrix
|
|
52
|
+
'attribute vec2 p,t;'+ // position, uv
|
|
53
|
+
'attribute vec4 c,a;'+ // color, additiveColor
|
|
54
|
+
'varying vec2 v;'+ // return uv
|
|
55
|
+
'varying vec4 d,e;'+ // return color, additiveColor
|
|
56
|
+
'void main(){'+ // shader entry point
|
|
57
|
+
'gl_Position=m*vec4(p,1,1);'+ // transform position
|
|
58
|
+
'v=t;d=c;e=a;'+ // pass stuff to fragment shader
|
|
59
|
+
'}' // end of shader
|
|
61
60
|
,
|
|
62
|
-
'precision highp float;'+
|
|
63
|
-
'varying vec2 v;'+
|
|
64
|
-
'varying vec4 d,e;'+
|
|
65
|
-
'uniform sampler2D
|
|
66
|
-
'void main(){'+
|
|
67
|
-
'gl_FragColor=texture2D(
|
|
68
|
-
'}'
|
|
61
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
62
|
+
'varying vec2 v;'+ // uv
|
|
63
|
+
'varying vec4 d,e;'+ // color, additiveColor
|
|
64
|
+
'uniform sampler2D s;'+ // texture
|
|
65
|
+
'void main(){'+ // shader entry point
|
|
66
|
+
'gl_FragColor=texture2D(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
67
|
+
'}' // end of shader
|
|
69
68
|
);
|
|
70
69
|
|
|
71
70
|
// init buffers
|
|
@@ -83,12 +82,10 @@ function glInit()
|
|
|
83
82
|
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
84
83
|
offset += size*typeSize;
|
|
85
84
|
}
|
|
86
|
-
initVertexAttribArray('a', gl_FLOAT, 4, 1); // angle
|
|
87
85
|
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
88
|
-
initVertexAttribArray('s', gl_FLOAT, 4, 2); // size
|
|
89
86
|
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
90
87
|
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
91
|
-
initVertexAttribArray('
|
|
88
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
92
89
|
}
|
|
93
90
|
|
|
94
91
|
/** Set the WebGl blend mode, normally you should call setBlendMode instead
|
|
@@ -203,6 +200,7 @@ function glPreRender(width, height, cameraX, cameraY, cameraScale)
|
|
|
203
200
|
|
|
204
201
|
// clear and set to same size as main canvas
|
|
205
202
|
glContext.viewport(0, 0, glCanvas.width = width, glCanvas.height = height);
|
|
203
|
+
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
206
204
|
|
|
207
205
|
// set up the shader
|
|
208
206
|
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
@@ -249,12 +247,10 @@ function glCopyToContext(context, forceDraw)
|
|
|
249
247
|
if (!glEnable || !glBatchCount) return;
|
|
250
248
|
|
|
251
249
|
glFlush();
|
|
250
|
+
|
|
251
|
+
// do not draw in overlay mode because the canvas is visible
|
|
252
252
|
if (!glOverlay || forceDraw)
|
|
253
|
-
{
|
|
254
|
-
// do not draw/clear in overlay mode because the canvas is visible
|
|
255
253
|
context.drawImage(glCanvas, 0, 0);
|
|
256
|
-
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
257
|
-
}
|
|
258
254
|
}
|
|
259
255
|
|
|
260
256
|
/** Add a sprite to the gl draw list, used by all gl draw functions
|
|
@@ -277,51 +273,49 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
|
|
|
277
273
|
// flush if there is no room for more verts or if different blend mode
|
|
278
274
|
if (glBatchCount == gl_MAX_BATCH || glBatchAdditive != glAdditive)
|
|
279
275
|
glFlush();
|
|
276
|
+
|
|
277
|
+
// prepare to create the verts from size and angle
|
|
278
|
+
const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
|
|
279
|
+
const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
|
|
280
280
|
|
|
281
281
|
// setup 2 triangles to form a quad
|
|
282
282
|
let offset = glBatchCount++ * gl_VERTICES_PER_QUAD * gl_INDICIES_PER_VERT;
|
|
283
283
|
|
|
284
284
|
// vertex 0
|
|
285
|
-
glPositionData[offset++] =
|
|
286
|
-
glPositionData[offset++] =
|
|
287
|
-
glPositionData[offset++] =
|
|
288
|
-
|
|
289
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
285
|
+
glPositionData[offset++] = x - cx - sy;
|
|
286
|
+
glPositionData[offset++] = y - cy + sx;
|
|
287
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
288
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
290
289
|
|
|
291
290
|
// vertex 1
|
|
292
|
-
glPositionData[offset++] =
|
|
293
|
-
glPositionData[offset++] =
|
|
294
|
-
glPositionData[offset++] =
|
|
295
|
-
|
|
296
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
291
|
+
glPositionData[offset++] = x + cx + sy;
|
|
292
|
+
glPositionData[offset++] = y + cy - sx;
|
|
293
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
294
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
297
295
|
|
|
298
296
|
// vertex 2
|
|
299
|
-
glPositionData[offset++] =
|
|
300
|
-
glPositionData[offset++] =
|
|
301
|
-
glPositionData[offset++] =
|
|
302
|
-
|
|
303
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
297
|
+
glPositionData[offset++] = x - cx + sy;
|
|
298
|
+
glPositionData[offset++] = y + cy + sx;
|
|
299
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv0Y;
|
|
300
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
304
301
|
|
|
305
302
|
// vertex 0
|
|
306
|
-
glPositionData[offset++] =
|
|
307
|
-
glPositionData[offset++] =
|
|
308
|
-
glPositionData[offset++] =
|
|
309
|
-
|
|
310
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
303
|
+
glPositionData[offset++] = x - cx - sy;
|
|
304
|
+
glPositionData[offset++] = y - cy + sx;
|
|
305
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
306
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
311
307
|
|
|
312
308
|
// vertex 3
|
|
313
|
-
glPositionData[offset++] =
|
|
314
|
-
glPositionData[offset++] =
|
|
315
|
-
glPositionData[offset++] =
|
|
316
|
-
|
|
317
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
309
|
+
glPositionData[offset++] = x + cx - sy;
|
|
310
|
+
glPositionData[offset++] = y - cy - sx;
|
|
311
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv1Y;
|
|
312
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
318
313
|
|
|
319
314
|
// vertex 1
|
|
320
|
-
glPositionData[offset++] =
|
|
321
|
-
glPositionData[offset++] =
|
|
322
|
-
glPositionData[offset++] =
|
|
323
|
-
|
|
324
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
315
|
+
glPositionData[offset++] = x + cx + sy;
|
|
316
|
+
glPositionData[offset++] = y + cy - sx;
|
|
317
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
318
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
325
319
|
}
|
|
326
320
|
|
|
327
321
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -353,6 +347,6 @@ gl_LINK_STATUS = 35714,
|
|
|
353
347
|
|
|
354
348
|
// constants for batch rendering
|
|
355
349
|
gl_VERTICES_PER_QUAD = 6,
|
|
356
|
-
gl_INDICIES_PER_VERT =
|
|
350
|
+
gl_INDICIES_PER_VERT = 6,
|
|
357
351
|
gl_MAX_BATCH = 1<<16,
|
|
358
|
-
gl_VERTEX_BYTE_STRIDE =
|
|
352
|
+
gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2; // vec2 * 2 + (char * 4) * 2
|