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/engine.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
const engineName = 'LittleJS';
|
|
23
23
|
|
|
24
24
|
/** Version of engine */
|
|
25
|
-
const engineVersion = '1.
|
|
25
|
+
const engineVersion = '1.2.9';
|
|
26
26
|
|
|
27
27
|
/** Frames per second to update objects
|
|
28
28
|
* @default */
|
|
@@ -59,7 +59,7 @@ let averageFPS, drawCount;
|
|
|
59
59
|
// css text used for elements created by engine
|
|
60
60
|
const styleBody = 'margin:0;overflow:hidden;background:#000' +
|
|
61
61
|
';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
|
|
62
|
-
const
|
|
62
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
63
63
|
|
|
64
64
|
///////////////////////////////////////////////////////////////////////////////
|
|
65
65
|
|
|
@@ -84,7 +84,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
84
84
|
document.body.style = styleBody;
|
|
85
85
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
86
86
|
mainContext = mainCanvas.getContext('2d');
|
|
87
|
-
mainCanvas.style =
|
|
87
|
+
mainCanvas.style = styleCanvas;
|
|
88
88
|
|
|
89
89
|
// init stuff and start engine
|
|
90
90
|
debugInit();
|
|
@@ -93,7 +93,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
93
93
|
// create overlay canvas for hud to appear above gl canvas
|
|
94
94
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
95
95
|
overlayContext = overlayCanvas.getContext('2d');
|
|
96
|
-
overlayCanvas.style =
|
|
96
|
+
overlayCanvas.style = styleCanvas;
|
|
97
97
|
|
|
98
98
|
gameInit();
|
|
99
99
|
touchGamepadCreate();
|
|
@@ -103,13 +103,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
103
103
|
// main update loop
|
|
104
104
|
const engineUpdate = (frameTimeMS=0)=>
|
|
105
105
|
{
|
|
106
|
-
requestAnimationFrame(engineUpdate);
|
|
107
|
-
|
|
108
106
|
// update time keeping
|
|
109
107
|
let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
|
|
110
108
|
frameTimeLastMS = frameTimeMS;
|
|
111
109
|
if (debug || showWatermark)
|
|
112
|
-
averageFPS = lerp(.05, 1e3/(frameTimeDeltaMS||1)
|
|
110
|
+
averageFPS = lerp(.05, averageFPS || 0, 1e3/(frameTimeDeltaMS||1));
|
|
113
111
|
if (debug)
|
|
114
112
|
frameTimeDeltaMS *= keyIsDown(107) ? 5 : keyIsDown(109) ? .2 : 1; // +/- to speed/slow time
|
|
115
113
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
@@ -203,6 +201,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
203
201
|
overlayContext.fillText(text, mainCanvas.width-2, 2);
|
|
204
202
|
drawCount = 0;
|
|
205
203
|
}
|
|
204
|
+
|
|
205
|
+
requestAnimationFrame(engineUpdate);
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
// set tile image source to load the image and start the engine
|
package/engine/engineAudio.js
CHANGED
|
@@ -69,7 +69,7 @@ class Sound
|
|
|
69
69
|
return; // out of range
|
|
70
70
|
|
|
71
71
|
// attenuate volume by distance
|
|
72
|
-
volume *= percent(lengthSquared**.5, range*this.taper
|
|
72
|
+
volume *= percent(lengthSquared**.5, range, range*this.taper);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// get pan from screen space coords
|
|
@@ -251,7 +251,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
251
251
|
// connect source to gain
|
|
252
252
|
(
|
|
253
253
|
window.StereoPannerNode ? // create pan node if possible
|
|
254
|
-
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, 1,
|
|
254
|
+
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)}))
|
|
255
255
|
: source
|
|
256
256
|
)
|
|
257
257
|
.connect(gainNode);
|
package/engine/engineBuild.bat
CHANGED
|
@@ -87,7 +87,15 @@ if %ERRORLEVEL% NEQ 0 (
|
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
rem lightly minify with uglify
|
|
90
|
-
call uglifyjs -o
|
|
90
|
+
call uglifyjs -o %OUTPUT_FILENAME% -- %OUTPUT_FILENAME%
|
|
91
|
+
if %ERRORLEVEL% NEQ 0 (
|
|
92
|
+
pause
|
|
93
|
+
exit /b %ERRORLEVEL%
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
rem --- BUILD TYPESCRIPT DEFINITIONS ---
|
|
97
|
+
|
|
98
|
+
call tsc engine.all.js --declaration --allowJs --emitDeclarationOnly --outFile index.d.ts
|
|
91
99
|
if %ERRORLEVEL% NEQ 0 (
|
|
92
100
|
pause
|
|
93
101
|
exit /b %ERRORLEVEL%
|
package/engine/engineDebug.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* LittleJS Debug System
|
|
8
8
|
* <br> - Press ~ to show debug overlay with mouse pick
|
|
9
9
|
* <br> - Number keys toggle debug functions
|
|
10
|
+
* <br> - +/- apply time scale
|
|
10
11
|
* <br> - Debug primitive rendering
|
|
11
12
|
* <br> - Save a 2d canvas as an image
|
|
12
13
|
* @namespace Debug
|
|
@@ -159,27 +160,27 @@ const debugUpdate = ()=>
|
|
|
159
160
|
{
|
|
160
161
|
if (!debug)
|
|
161
162
|
return;
|
|
162
|
-
|
|
163
|
+
|
|
163
164
|
if (keyWasPressed(192)) // ~
|
|
164
165
|
debugOverlay = !debugOverlay;
|
|
165
166
|
if (debugOverlay)
|
|
166
167
|
{
|
|
168
|
+
if (keyWasPressed(48)) // 0
|
|
169
|
+
showWatermark = !showWatermark;
|
|
167
170
|
if (keyWasPressed(49)) // 1
|
|
168
171
|
debugPhysics = !debugPhysics, debugParticles = 0;
|
|
169
172
|
if (keyWasPressed(50)) // 2
|
|
170
173
|
debugParticles = !debugParticles, debugPhysics = 0;
|
|
171
174
|
if (keyWasPressed(51)) // 3
|
|
175
|
+
debugGamepads = !debugGamepads;
|
|
176
|
+
if (keyWasPressed(52)) // 4
|
|
172
177
|
godMode = !godMode;
|
|
173
178
|
if (keyWasPressed(53)) // 5
|
|
174
179
|
debugTakeScreenshot = 1;
|
|
175
180
|
//if (keyWasPressed(54)) // 6
|
|
176
|
-
//
|
|
177
|
-
if (keyWasPressed(55)) // 7
|
|
178
|
-
debugGamepads = !debugGamepads;
|
|
181
|
+
//if (keyWasPressed(55)) // 7
|
|
179
182
|
//if (keyWasPressed(56)) // 8
|
|
180
183
|
//if (keyWasPressed(57)) // 9
|
|
181
|
-
if (keyWasPressed(48)) // 0
|
|
182
|
-
showWatermark = !showWatermark;
|
|
183
184
|
}
|
|
184
185
|
}
|
|
185
186
|
|
|
@@ -200,21 +201,20 @@ const debugRender = ()=>
|
|
|
200
201
|
|
|
201
202
|
if (debugGamepads && gamepadsEnable && navigator.getGamepads)
|
|
202
203
|
{
|
|
203
|
-
//
|
|
204
|
+
// gamepad debug display
|
|
204
205
|
const gamepads = navigator.getGamepads();
|
|
205
206
|
for (let i = gamepads.length; i--;)
|
|
206
207
|
{
|
|
207
208
|
const gamepad = gamepads[i];
|
|
208
209
|
if (gamepad)
|
|
209
210
|
{
|
|
210
|
-
// gamepad debug display
|
|
211
211
|
const stickScale = 1;
|
|
212
212
|
const buttonScale = .2;
|
|
213
213
|
const centerPos = cameraPos;
|
|
214
214
|
const sticks = stickData[i];
|
|
215
215
|
for (let j = sticks.length; j--;)
|
|
216
216
|
{
|
|
217
|
-
const drawPos = centerPos.add(vec2(j*stickScale*2,i*stickScale*3));
|
|
217
|
+
const drawPos = centerPos.add(vec2(j*stickScale*2, i*stickScale*3));
|
|
218
218
|
const stickPos = drawPos.add(sticks[j].scale(stickScale));
|
|
219
219
|
debugCircle(drawPos, stickScale, '#fff7',0,1);
|
|
220
220
|
debugLine(drawPos, stickPos, '#f00');
|
|
@@ -251,16 +251,14 @@ const debugRender = ()=>
|
|
|
251
251
|
bestObject = o
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
size.y = max(size.y, .2);
|
|
256
|
-
|
|
254
|
+
// show object info
|
|
257
255
|
const color = new Color(
|
|
258
256
|
o.collideTiles?1:0,
|
|
259
257
|
o.collideSolidObjects?1:0,
|
|
260
258
|
o.isSolid?1:0,
|
|
261
259
|
o.parent ? .2 : .5);
|
|
262
|
-
|
|
263
|
-
|
|
260
|
+
size.x = max(size.x, .2);
|
|
261
|
+
size.y = max(size.y, .2);
|
|
264
262
|
drawRect(o.pos, size, color);
|
|
265
263
|
drawRect(o.pos, size.scale(.8), o.parent ? new Color(1,1,1,.5) : new Color(0,0,0,.8));
|
|
266
264
|
o.parent && drawLine(o.pos, o.parent.pos, .1, new Color(0,0,1,.5));
|
|
@@ -275,17 +273,11 @@ const debugRender = ()=>
|
|
|
275
273
|
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), new Color(0,0,1,.5));
|
|
276
274
|
drawLine(mousePos, bestObject.pos, .1, !raycastHitPos ? new Color(0,1,0,.5) : new Color(1,0,0,.5));
|
|
277
275
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
+ (bestObject.angle>0?' ':' ') + (bestObject.angle*180/PI).toFixed(1) + '°',
|
|
284
|
-
pos = pos.add(height), ...args);
|
|
285
|
-
drawText('vel = ' + printVec2(bestObject.velocity), pos = pos.add(height), ...args);
|
|
286
|
-
drawText('size = ' + printVec2(bestObject.size), pos = pos.add(height), ...args);
|
|
287
|
-
drawText('type = ' + ( bestObject.constructor.name), pos = mousePos.subtract(height), ...args);
|
|
288
|
-
drawText('collision = ' + getTileCollisionData(mousePos), pos = mousePos.subtract(height.scale(2)), ...args);
|
|
276
|
+
const debugText = 'mouse pos = ' + mousePos +
|
|
277
|
+
'\nmouse collision = ' + getTileCollisionData(mousePos) +
|
|
278
|
+
'\n\n--- object info ---\n' +
|
|
279
|
+
bestObject.toString();
|
|
280
|
+
drawTextScreen(debugText, mousePosScreen, 24, new Color, .05, undefined, undefined, 'monospace');
|
|
289
281
|
mainContext = saveContext;
|
|
290
282
|
}
|
|
291
283
|
|
|
@@ -293,16 +285,16 @@ const debugRender = ()=>
|
|
|
293
285
|
}
|
|
294
286
|
|
|
295
287
|
{
|
|
296
|
-
//
|
|
297
|
-
overlayContext.lineWidth =
|
|
288
|
+
// draw debug primitives
|
|
289
|
+
overlayContext.lineWidth = 2;
|
|
298
290
|
const pointSize = debugPointSize * cameraScale;
|
|
299
291
|
debugPrimitives.forEach(p=>
|
|
300
292
|
{
|
|
293
|
+
overlayContext.save();
|
|
294
|
+
|
|
301
295
|
// create canvas transform from world space to screen space
|
|
302
296
|
const pos = worldToScreen(p.pos);
|
|
303
297
|
|
|
304
|
-
overlayContext.save();
|
|
305
|
-
overlayContext.lineWidth = 2;
|
|
306
298
|
overlayContext.translate(pos.x|0, pos.y|0);
|
|
307
299
|
overlayContext.rotate(p.angle);
|
|
308
300
|
overlayContext.fillStyle = overlayContext.strokeStyle = p.color;
|
|
@@ -335,15 +327,17 @@ const debugRender = ()=>
|
|
|
335
327
|
p.fill && overlayContext.fill();
|
|
336
328
|
overlayContext.stroke();
|
|
337
329
|
}
|
|
338
|
-
|
|
330
|
+
|
|
339
331
|
overlayContext.restore();
|
|
340
332
|
});
|
|
341
333
|
|
|
342
|
-
|
|
334
|
+
// remove expired pritives
|
|
335
|
+
debugPrimitives = debugPrimitives.filter(r=>r.time.get()<0);
|
|
343
336
|
}
|
|
344
337
|
|
|
345
338
|
{
|
|
346
|
-
|
|
339
|
+
// draw debug overlay
|
|
340
|
+
overlayContext.save();
|
|
347
341
|
overlayContext.fillStyle = '#fff';
|
|
348
342
|
overlayContext.textAlign = 'left';
|
|
349
343
|
overlayContext.textBaseline = 'top';
|
|
@@ -351,6 +345,7 @@ const debugRender = ()=>
|
|
|
351
345
|
overlayContext.shadowColor = '#000';
|
|
352
346
|
overlayContext.shadowBlur = 9;
|
|
353
347
|
|
|
348
|
+
let x = 9, y = -20, h = 30;
|
|
354
349
|
if (debugOverlay)
|
|
355
350
|
{
|
|
356
351
|
overlayContext.fillText(engineName, x, y += h);
|
|
@@ -363,14 +358,12 @@ const debugRender = ()=>
|
|
|
363
358
|
overlayContext.fillText('1: Debug Physics', x, y += h);
|
|
364
359
|
overlayContext.fillStyle = debugParticles ? '#f00' : '#fff';
|
|
365
360
|
overlayContext.fillText('2: Debug Particles', x, y += h);
|
|
361
|
+
overlayContext.fillStyle = debugGamepads ? '#f00' : '#fff';
|
|
362
|
+
overlayContext.fillText('3: Debug Gamepads', x, y += h);
|
|
366
363
|
overlayContext.fillStyle = godMode ? '#f00' : '#fff';
|
|
367
|
-
overlayContext.fillText('
|
|
364
|
+
overlayContext.fillText('4: God Mode', x, y += h);
|
|
368
365
|
overlayContext.fillStyle = '#fff';
|
|
369
366
|
overlayContext.fillText('5: Save Screenshot', x, y += h);
|
|
370
|
-
//overlayContext.fillStyle = debugParticleEditor ? '#f00' : '#fff';
|
|
371
|
-
//overlayContext.fillText('6: Particle Editor', x, y += h);
|
|
372
|
-
overlayContext.fillStyle = debugGamepads ? '#f00' : '#fff';
|
|
373
|
-
overlayContext.fillText('7: Debug Gamepads', x, y += h);
|
|
374
367
|
|
|
375
368
|
let keysPressed = '';
|
|
376
369
|
for(const i in inputData[0])
|
|
@@ -388,7 +381,6 @@ const debugRender = ()=>
|
|
|
388
381
|
buttonsPressed += i + ' ' ;
|
|
389
382
|
}
|
|
390
383
|
buttonsPressed && overlayContext.fillText('Gamepad: ' + buttonsPressed, x, y += h);
|
|
391
|
-
|
|
392
384
|
}
|
|
393
385
|
else
|
|
394
386
|
{
|
|
@@ -398,197 +390,6 @@ const debugRender = ()=>
|
|
|
398
390
|
overlayContext.fillText(debugGamepads ? 'Debug Gamepads' : '', x, y += h);
|
|
399
391
|
}
|
|
400
392
|
|
|
401
|
-
overlayContext.
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
debugPrimitives = debugPrimitives.filter(r=>r.time.get()<0);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
408
|
-
// particle system editor (work in progress)
|
|
409
|
-
let debugParticleEditor = 0, debugParticleSystem, debugParticleSystemDiv, particleSystemCode;
|
|
410
|
-
|
|
411
|
-
const debugToggleParticleEditor = ()=>
|
|
412
|
-
{
|
|
413
|
-
debugParticleEditor = !debugParticleEditor;
|
|
414
|
-
|
|
415
|
-
if (debugParticleEditor)
|
|
416
|
-
{
|
|
417
|
-
if (!debugParticleSystem || debugParticleSystem.destroyed)
|
|
418
|
-
debugParticleSystem = new ParticleEmitter(cameraPos);
|
|
419
|
-
}
|
|
420
|
-
else if (debugParticleSystem && !debugParticleSystem.destroyed)
|
|
421
|
-
debugParticleSystem.destroy();
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const colorToHex = (color)=>
|
|
425
|
-
{
|
|
426
|
-
const componentToHex = (c)=>
|
|
427
|
-
{
|
|
428
|
-
const hex = (c*255|0).toString(16);
|
|
429
|
-
return hex.length == 1 ? '0' + hex : hex;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return '#' + componentToHex(color.r) + componentToHex(color.g) + componentToHex(color.b);
|
|
433
|
-
}
|
|
434
|
-
const hexToColor = (hex)=>
|
|
435
|
-
{
|
|
436
|
-
return new Color(
|
|
437
|
-
parseInt(hex.substr(1,2), 16)/255,
|
|
438
|
-
parseInt(hex.substr(3,2), 16)/255,
|
|
439
|
-
parseInt(hex.substr(5,2), 16)/255)
|
|
393
|
+
overlayContext.restore();
|
|
440
394
|
}
|
|
441
|
-
|
|
442
|
-
if (!debugParticleSystemDiv)
|
|
443
|
-
{
|
|
444
|
-
const div = debugParticleSystemDiv = document.createElement('div');
|
|
445
|
-
div.innerHTML = '<big><b>Particle Editor';
|
|
446
|
-
div.style = 'position:absolute;top:10;left:10;color:#fff';
|
|
447
|
-
document.body.appendChild(div);
|
|
448
|
-
|
|
449
|
-
for ( const setting of debugParticleSettings)
|
|
450
|
-
{
|
|
451
|
-
const input = setting[2] = document.createElement('input');
|
|
452
|
-
const name = setting[0];
|
|
453
|
-
const type = setting[1];
|
|
454
|
-
if (type)
|
|
455
|
-
{
|
|
456
|
-
if (type == 'color')
|
|
457
|
-
{
|
|
458
|
-
input.type = type;
|
|
459
|
-
const color = debugParticleSystem[name];
|
|
460
|
-
input.value = colorToHex(color);
|
|
461
|
-
}
|
|
462
|
-
else if (type == 'alpha' && name == 'colorStartAlpha')
|
|
463
|
-
input.value = debugParticleSystem.colorStartA.a;
|
|
464
|
-
else if (type == 'alpha' && name == 'colorEndAlpha')
|
|
465
|
-
input.value = debugParticleSystem.colorEndA.a;
|
|
466
|
-
else if (name == 'tileSizeX')
|
|
467
|
-
input.value = debugParticleSystem.tileSize.x;
|
|
468
|
-
else if (name == 'tileSizeY')
|
|
469
|
-
input.value = debugParticleSystem.tileSize.y;
|
|
470
|
-
}
|
|
471
|
-
else
|
|
472
|
-
input.value = debugParticleSystem[name] || '0';
|
|
473
|
-
|
|
474
|
-
input.oninput = (e)=>
|
|
475
|
-
{
|
|
476
|
-
const inputFloat = parseFloat(input.value) || 0;
|
|
477
|
-
if (type)
|
|
478
|
-
{
|
|
479
|
-
if (type == 'color')
|
|
480
|
-
{
|
|
481
|
-
const color = hexToColor(input.value);
|
|
482
|
-
debugParticleSystem[name].r = color.r;
|
|
483
|
-
debugParticleSystem[name].g = color.g;
|
|
484
|
-
debugParticleSystem[name].b = color.b;
|
|
485
|
-
}
|
|
486
|
-
else if (type == 'alpha' && name == 'colorStartAlpha')
|
|
487
|
-
{
|
|
488
|
-
debugParticleSystem.colorStartA.a = clamp(inputFloat);
|
|
489
|
-
debugParticleSystem.colorStartB.a = clamp(inputFloat);
|
|
490
|
-
}
|
|
491
|
-
else if (type == 'alpha' && name == 'colorEndAlpha')
|
|
492
|
-
{
|
|
493
|
-
debugParticleSystem.colorEndA.a = clamp(inputFloat);
|
|
494
|
-
debugParticleSystem.colorEndB.a = clamp(inputFloat);
|
|
495
|
-
}
|
|
496
|
-
else if (name == 'tileSizeX')
|
|
497
|
-
{
|
|
498
|
-
debugParticleSystem.tileSize = vec2(parseInt(input.value), debugParticleSystem.tileSize.y);
|
|
499
|
-
}
|
|
500
|
-
else if (name == 'tileSizeY')
|
|
501
|
-
{
|
|
502
|
-
debugParticleSystem.tileSize.y = vec2(debugParticleSystem.tileSize.x, parseInt(input.value));
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
else
|
|
506
|
-
debugParticleSystem[name] = inputFloat;
|
|
507
|
-
|
|
508
|
-
updateCode();
|
|
509
|
-
}
|
|
510
|
-
div.appendChild(document.createElement('br'));
|
|
511
|
-
div.appendChild(input);
|
|
512
|
-
div.appendChild(document.createTextNode(' ' + name));
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
div.appendChild(document.createElement('br'));
|
|
516
|
-
div.appendChild(document.createElement('br'));
|
|
517
|
-
div.appendChild(particleSystemCode = document.createElement('input'));
|
|
518
|
-
particleSystemCode.disabled = true;
|
|
519
|
-
div.appendChild(document.createTextNode(' code'));
|
|
520
|
-
|
|
521
|
-
div.appendChild(document.createElement('br'));
|
|
522
|
-
const button = document.createElement('button')
|
|
523
|
-
div.appendChild(button);
|
|
524
|
-
button.innerHTML = 'Copy To Clipboard';
|
|
525
|
-
|
|
526
|
-
button.onclick = (e)=> navigator.clipboard.writeText(particleSystemCode.value);
|
|
527
|
-
|
|
528
|
-
const updateCode = ()=>
|
|
529
|
-
{
|
|
530
|
-
let code = '';
|
|
531
|
-
let count = 0;
|
|
532
|
-
for ( const setting of debugParticleSettings)
|
|
533
|
-
{
|
|
534
|
-
const name = setting[0];
|
|
535
|
-
const type = setting[1];
|
|
536
|
-
let value;
|
|
537
|
-
if (name == 'tileSizeX' || type == 'alpha')
|
|
538
|
-
continue;
|
|
539
|
-
|
|
540
|
-
if (count++)
|
|
541
|
-
code += ', ';
|
|
542
|
-
|
|
543
|
-
if (name == 'tileSizeY')
|
|
544
|
-
{
|
|
545
|
-
value = `vec2(${debugParticleSystem.tileSize.x},${debugParticleSystem.tileSize.y})`;
|
|
546
|
-
}
|
|
547
|
-
else if (type == 'color')
|
|
548
|
-
{
|
|
549
|
-
const c = debugParticleSystem[name];
|
|
550
|
-
value = `new Color(${c.r},${c.g},${c.b},${c.a})`;
|
|
551
|
-
}
|
|
552
|
-
else
|
|
553
|
-
value = debugParticleSystem[name];
|
|
554
|
-
code += value;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
particleSystemCode.value = '...[' + code + ']';
|
|
558
|
-
}
|
|
559
|
-
updateCode();
|
|
560
|
-
}
|
|
561
|
-
debugParticleSystemDiv.style.display = debugParticleEditor ? '' : 'none'
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
const debugParticleSettings =
|
|
565
|
-
[
|
|
566
|
-
['emitSize'],
|
|
567
|
-
['emitTime'],
|
|
568
|
-
['emitRate'],
|
|
569
|
-
['emitConeAngle'],
|
|
570
|
-
['tileIndex'],
|
|
571
|
-
['tileSizeX', 'tileSize'],
|
|
572
|
-
['tileSizeY', 'tileSize'],
|
|
573
|
-
['colorStartA', 'color'],
|
|
574
|
-
['colorStartB', 'color'],
|
|
575
|
-
['colorStartAlpha', 'alpha'],
|
|
576
|
-
['colorEndA', 'color'],
|
|
577
|
-
['colorEndB', 'color'],
|
|
578
|
-
['colorEndAlpha', 'alpha'],
|
|
579
|
-
['particleTime'],
|
|
580
|
-
['sizeStart'],
|
|
581
|
-
['sizeEnd'],
|
|
582
|
-
['speed'],
|
|
583
|
-
['angleSpeed'],
|
|
584
|
-
['damping'],
|
|
585
|
-
['angleDamping'],
|
|
586
|
-
['gravityScale'],
|
|
587
|
-
['particleConeAngle'],
|
|
588
|
-
['fadeRate'],
|
|
589
|
-
['randomness'],
|
|
590
|
-
['collideTiles'],
|
|
591
|
-
['additive'],
|
|
592
|
-
['randomColorComponents'],
|
|
593
|
-
['renderOrder'],
|
|
594
|
-
];
|
|
395
|
+
}
|
package/engine/engineDraw.js
CHANGED
|
@@ -110,7 +110,7 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
|
|
|
110
110
|
if (tileIndex < 0)
|
|
111
111
|
{
|
|
112
112
|
// if negative tile index, force untextured
|
|
113
|
-
context.fillStyle = color
|
|
113
|
+
context.fillStyle = color;
|
|
114
114
|
context.fillRect(-.5, -.5, 1, 1);
|
|
115
115
|
}
|
|
116
116
|
else
|
|
@@ -203,7 +203,20 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
203
203
|
context.restore();
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
/**
|
|
206
|
+
/** Enable normal or additive blend mode
|
|
207
|
+
* @param {Boolean} [additive=0]
|
|
208
|
+
* @param {Boolean} [useWebGL=glEnable]
|
|
209
|
+
* @memberof Draw */
|
|
210
|
+
function setBlendMode(additive, useWebGL=glEnable)
|
|
211
|
+
{
|
|
212
|
+
if (glEnable && useWebGL)
|
|
213
|
+
glSetBlendMode(additive);
|
|
214
|
+
else
|
|
215
|
+
mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Draw text on overlay canvas in screen space
|
|
219
|
+
* Automatically splits new lines into rows
|
|
207
220
|
* @param {String} text
|
|
208
221
|
* @param {Vector2} pos
|
|
209
222
|
* @param {Number} [size=1]
|
|
@@ -212,32 +225,129 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
212
225
|
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
213
226
|
* @param {String} [textAlign='center']
|
|
214
227
|
* @memberof Draw */
|
|
215
|
-
function
|
|
228
|
+
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
|
|
216
229
|
{
|
|
217
|
-
|
|
218
|
-
overlayContext.
|
|
230
|
+
overlayContext.fillStyle = color;
|
|
231
|
+
overlayContext.lineWidth = lineWidth *= cameraScale;
|
|
232
|
+
overlayContext.strokeStyle = lineColor;
|
|
219
233
|
overlayContext.textAlign = textAlign;
|
|
234
|
+
overlayContext.font = size + 'px '+ font;
|
|
220
235
|
overlayContext.textBaseline = 'middle';
|
|
221
|
-
|
|
236
|
+
|
|
237
|
+
pos = pos.copy();
|
|
238
|
+
text.split('\n').forEach(line=>
|
|
222
239
|
{
|
|
223
|
-
overlayContext.
|
|
224
|
-
overlayContext.
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
overlayContext.fillStyle = color.rgba();
|
|
228
|
-
overlayContext.fillText(text, pos.x, pos.y);
|
|
240
|
+
lineWidth && overlayContext.strokeText(line, pos.x, pos.y);
|
|
241
|
+
overlayContext.fillText(line, pos.x, pos.y);
|
|
242
|
+
pos.y += size;
|
|
243
|
+
});
|
|
229
244
|
}
|
|
230
245
|
|
|
231
|
-
/**
|
|
232
|
-
*
|
|
233
|
-
* @param {
|
|
246
|
+
/** Draw text on overlay canvas in world space
|
|
247
|
+
* Automatically splits new lines into rows
|
|
248
|
+
* @param {String} text
|
|
249
|
+
* @param {Vector2} pos
|
|
250
|
+
* @param {Number} [size=1]
|
|
251
|
+
* @param {Color} [color=new Color(1,1,1)]
|
|
252
|
+
* @param {Number} [lineWidth=0]
|
|
253
|
+
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
254
|
+
* @param {String} [textAlign='center']
|
|
234
255
|
* @memberof Draw */
|
|
235
|
-
function
|
|
256
|
+
function drawText(text, pos, size=1, color, lineWidth, lineColor, textAlign, font)
|
|
236
257
|
{
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
258
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth, lineColor, textAlign, font);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Font Image Object - Draw text on a 2D canvas by using characters in an image
|
|
265
|
+
* <br> - 96 characters (from space to tilde) are stored in an image
|
|
266
|
+
* <br> - Uses a default 8x8 font if none is supplied
|
|
267
|
+
* <br> - You can also use fonts from the main tile sheet
|
|
268
|
+
* @example
|
|
269
|
+
* // use built in font
|
|
270
|
+
* const font = new ImageFont;
|
|
271
|
+
*
|
|
272
|
+
* // draw text
|
|
273
|
+
* font.drawTextScreen("LittleJS\nHello World!", vec2(200, 50));
|
|
274
|
+
*/
|
|
275
|
+
|
|
276
|
+
let engineFontImage;
|
|
277
|
+
|
|
278
|
+
class FontImage
|
|
279
|
+
{
|
|
280
|
+
/** Create an image font
|
|
281
|
+
* @param {Image} [image] - The image the font is stored in, if undefined the default font is used
|
|
282
|
+
* @param {Vector2} [tileSize=vec2(8)] - The size of the font source tiles
|
|
283
|
+
* @param {Vector2} [paddingSize=vec2(0,1)] - How much extra space to add between characters
|
|
284
|
+
* @param {Number} [startTileIndex=0] - Tile index in image where font starts
|
|
285
|
+
* @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
|
|
286
|
+
*/
|
|
287
|
+
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
|
|
288
|
+
{
|
|
289
|
+
if (!image && !engineFontImage)
|
|
290
|
+
{
|
|
291
|
+
// load default font image
|
|
292
|
+
engineFontImage = new Image();
|
|
293
|
+
engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
this.image = image || engineFontImage;
|
|
297
|
+
this.tileSize = tileSize;
|
|
298
|
+
this.paddingSize = paddingSize;
|
|
299
|
+
this.startTileIndex = startTileIndex;
|
|
300
|
+
this.context = context;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Draw text in screen space using the image font
|
|
304
|
+
* @param {String} text
|
|
305
|
+
* @param {Vector2} pos
|
|
306
|
+
* @param {Number} [scale=4]
|
|
307
|
+
* @param {Boolean} [center]
|
|
308
|
+
*/
|
|
309
|
+
drawTextScreen(text, pos, scale=4, center)
|
|
310
|
+
{
|
|
311
|
+
const context = this.context;
|
|
312
|
+
context.save();
|
|
313
|
+
context.imageSmoothingEnabled = !cavasPixelated;
|
|
314
|
+
|
|
315
|
+
const size = this.tileSize;
|
|
316
|
+
const drawSize = size.add(this.paddingSize).scale(scale);
|
|
317
|
+
const cols = this.image.width / this.tileSize.x |0;
|
|
318
|
+
text.split('\n').forEach((line, i)=>
|
|
319
|
+
{
|
|
320
|
+
const centerOffset = center ? line.length * size.x * scale / 2 |0 : 0;
|
|
321
|
+
for(let j=line.length; j--;)
|
|
322
|
+
{
|
|
323
|
+
// draw each character
|
|
324
|
+
let charCode = line[j].charCodeAt();
|
|
325
|
+
if (charCode < 32 || charCode > 127)
|
|
326
|
+
charCode = 127; // unknown character
|
|
327
|
+
|
|
328
|
+
// get the character source location and draw it
|
|
329
|
+
const tile = this.startTileIndex + charCode - 32;
|
|
330
|
+
const x = tile % cols;
|
|
331
|
+
const y = tile / cols |0;
|
|
332
|
+
const drawPos = pos.add(vec2(j,i).multiply(drawSize));
|
|
333
|
+
context.drawImage(this.image, x * size.x, y * size.y, size.x, size.y,
|
|
334
|
+
drawPos.x - centerOffset, drawPos.y, size.x * scale, size.y * scale);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
context.restore();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** Draw text in world space using the image font
|
|
342
|
+
* @param {String} text
|
|
343
|
+
* @param {Vector2} pos
|
|
344
|
+
* @param {Number} [scale=.25]
|
|
345
|
+
* @param {Boolean} [center]
|
|
346
|
+
*/
|
|
347
|
+
drawText(text, pos, scale=1, center)
|
|
348
|
+
{
|
|
349
|
+
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center);
|
|
350
|
+
}
|
|
241
351
|
}
|
|
242
352
|
|
|
243
353
|
///////////////////////////////////////////////////////////////////////////////
|
|
Binary file
|