littlejsengine 1.11.2 → 1.11.4
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/.vscode/launch.json +14 -0
- package/README.md +14 -20
- package/dist/littlejs.d.ts +41 -30
- package/dist/littlejs.esm.js +145 -141
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +141 -140
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +138 -133
- package/examples/box2d/gameObjects.js +4 -4
- package/examples/box2d/scenes.js +2 -2
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakoutTutorial/game.js +1 -1
- package/examples/index.html +1 -7
- package/examples/logo.png +0 -0
- package/examples/module/game.js +1 -1
- package/examples/platformer/game.js +2 -2
- package/examples/platformer/gameCharacter.js +5 -5
- package/examples/platformer/gameEffects.js +4 -4
- package/examples/platformer/gameObjects.js +2 -2
- package/examples/puzzle/game.js +2 -2
- package/examples/starter/game.js +10 -44
- package/examples/starter/tiles.png +0 -0
- package/jsconfig.json +1 -0
- package/package.json +1 -1
- package/plugins/box2d.js +10 -10
- package/plugins/newgrounds.js +1 -1
- package/src/engine.js +5 -3
- package/src/engineAudio.js +4 -4
- package/src/engineDebug.js +3 -7
- package/src/engineDraw.js +50 -25
- package/src/engineExport.js +4 -1
- package/src/engineInput.js +1 -1
- package/src/engineObject.js +8 -8
- package/src/engineParticles.js +6 -6
- package/src/engineSettings.js +5 -5
- package/src/engineTileLayer.js +2 -2
- package/src/engineUtilities.js +5 -5
- package/src/engineWebGL.js +52 -74
- package/examples/electron/build.js +0 -107
- package/examples/electron/electron.js +0 -43
- package/examples/electron/game.js +0 -131
- package/examples/electron/index.html +0 -13
- package/examples/electron/package.json +0 -22
- package/examples/electron/tiles.png +0 -0
- package/examples/js13k/build.bat +0 -2
- package/examples/js13k/build.js +0 -131
- package/examples/js13k/game.js +0 -118
- package/examples/js13k/index.html +0 -21
- package/examples/js13k/tiles.png +0 -0
- package/examples/typescript/build.bat +0 -5
- package/examples/typescript/build.js +0 -33
- package/examples/typescript/game.js +0 -102
- package/examples/typescript/game.ts +0 -134
- package/examples/typescript/index.html +0 -10
- package/examples/typescript/tiles.png +0 -0
- package/examples/typescript/tsconfig.json +0 -8
package/src/engineExport.js
CHANGED
|
@@ -197,14 +197,16 @@ export {
|
|
|
197
197
|
drawEllipse,
|
|
198
198
|
drawCircle,
|
|
199
199
|
drawCanvas2D,
|
|
200
|
-
setBlendMode,
|
|
201
200
|
drawText,
|
|
202
201
|
drawTextOverlay,
|
|
203
202
|
drawTextScreen,
|
|
203
|
+
setBlendMode,
|
|
204
|
+
combineCanvases,
|
|
204
205
|
engineFontImage,
|
|
205
206
|
FontImage,
|
|
206
207
|
isFullscreen,
|
|
207
208
|
toggleFullscreen,
|
|
209
|
+
setCursor,
|
|
208
210
|
getCameraSize,
|
|
209
211
|
|
|
210
212
|
// WebGL
|
|
@@ -218,6 +220,7 @@ export {
|
|
|
218
220
|
glFlush,
|
|
219
221
|
glSetTexture,
|
|
220
222
|
glSetAntialias,
|
|
223
|
+
glClearCanvas,
|
|
221
224
|
glAntialias,
|
|
222
225
|
glShader,
|
|
223
226
|
glActiveTexture,
|
package/src/engineInput.js
CHANGED
|
@@ -341,7 +341,7 @@ function vibrateStop() { vibrate(0); }
|
|
|
341
341
|
|
|
342
342
|
/** True if a touch device has been detected
|
|
343
343
|
* @memberof Input */
|
|
344
|
-
const isTouchDevice = window.ontouchstart !== undefined;
|
|
344
|
+
const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
345
345
|
|
|
346
346
|
// touch gamepad internal variables
|
|
347
347
|
let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
|
package/src/engineObject.js
CHANGED
|
@@ -155,7 +155,7 @@ class EngineObject
|
|
|
155
155
|
const oldPos = this.pos.copy();
|
|
156
156
|
this.velocity.x *= this.damping;
|
|
157
157
|
this.velocity.y *= this.damping;
|
|
158
|
-
if (this.mass) //
|
|
158
|
+
if (this.mass) // don't apply gravity to static objects
|
|
159
159
|
this.velocity.y += gravity * this.gravityScale;
|
|
160
160
|
this.pos.x += this.velocity.x;
|
|
161
161
|
this.pos.y += this.velocity.y;
|
|
@@ -164,7 +164,7 @@ class EngineObject
|
|
|
164
164
|
// physics sanity checks
|
|
165
165
|
ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
|
|
166
166
|
ASSERT(this.damping >= 0 && this.damping <= 1);
|
|
167
|
-
if (!enablePhysicsSolver || !this.mass) //
|
|
167
|
+
if (!enablePhysicsSolver || !this.mass) // don't do collision for static objects
|
|
168
168
|
return;
|
|
169
169
|
|
|
170
170
|
const wasMovingDown = this.velocity.y < 0;
|
|
@@ -183,7 +183,7 @@ class EngineObject
|
|
|
183
183
|
const epsilon = .001; // necessary to push slightly outside of the collision
|
|
184
184
|
for (const o of engineObjectsCollide)
|
|
185
185
|
{
|
|
186
|
-
// non solid objects don't collide with
|
|
186
|
+
// non solid objects don't collide with each other
|
|
187
187
|
if (!this.isSolid && !o.isSolid || o.destroyed || o.parent || o == this)
|
|
188
188
|
continue;
|
|
189
189
|
|
|
@@ -243,7 +243,7 @@ class EngineObject
|
|
|
243
243
|
const elastic1 = o.velocity.y * (o.mass - this.mass) / (this.mass + o.mass)
|
|
244
244
|
+ this.velocity.y * 2 * this.mass / (this.mass + o.mass);
|
|
245
245
|
|
|
246
|
-
// lerp
|
|
246
|
+
// lerp between elastic or inelastic based on elasticity
|
|
247
247
|
this.velocity.y = lerp(elasticity, inelastic, elastic0);
|
|
248
248
|
o.velocity.y = lerp(elasticity, inelastic, elastic1);
|
|
249
249
|
}
|
|
@@ -263,7 +263,7 @@ class EngineObject
|
|
|
263
263
|
const elastic1 = o.velocity.x * (o.mass - this.mass) / (this.mass + o.mass)
|
|
264
264
|
+ this.velocity.x * 2 * this.mass / (this.mass + o.mass);
|
|
265
265
|
|
|
266
|
-
// lerp
|
|
266
|
+
// lerp between elastic or inelastic based on elasticity
|
|
267
267
|
this.velocity.x = lerp(elasticity, inelastic, elastic0);
|
|
268
268
|
o.velocity.x = lerp(elasticity, inelastic, elastic1);
|
|
269
269
|
}
|
|
@@ -329,7 +329,7 @@ class EngineObject
|
|
|
329
329
|
if (this.destroyed)
|
|
330
330
|
return;
|
|
331
331
|
|
|
332
|
-
// disconnect from parent and destroy
|
|
332
|
+
// disconnect from parent and destroy children
|
|
333
333
|
this.destroyed = 1;
|
|
334
334
|
this.parent && this.parent.removeChild(this);
|
|
335
335
|
for (const child of this.children)
|
|
@@ -354,7 +354,7 @@ class EngineObject
|
|
|
354
354
|
|
|
355
355
|
/** Called to check if a tile collision should be resolved
|
|
356
356
|
* @param {Number} tileData - the value of the tile at the position
|
|
357
|
-
* @param {Vector2} pos - tile where the collision
|
|
357
|
+
* @param {Vector2} pos - tile where the collision occurred
|
|
358
358
|
* @return {Boolean} - true if the collision should be resolved */
|
|
359
359
|
collideWithTile(tileData, pos) { return tileData > 0; }
|
|
360
360
|
|
|
@@ -417,7 +417,7 @@ class EngineObject
|
|
|
417
417
|
this.collideRaycast = collideRaycast;
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
-
/** Returns string
|
|
420
|
+
/** Returns string containing info about this object for debugging
|
|
421
421
|
* @return {String} */
|
|
422
422
|
toString()
|
|
423
423
|
{
|
package/src/engineParticles.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* let pos = vec2(2,3);
|
|
13
13
|
* let particleEmitter = new ParticleEmitter
|
|
14
14
|
* (
|
|
15
|
-
* pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate,
|
|
15
|
+
* pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emitCone
|
|
16
16
|
* tile(0, 16), // tileInfo
|
|
17
17
|
* rgb(1,1,1), rgb(0,0,0), // colorStartA, colorStartB
|
|
18
18
|
* rgb(1,1,1,0), rgb(0,0,0,0), // colorEndA, colorEndB
|
|
@@ -47,7 +47,7 @@ class ParticleEmitter extends EngineObject
|
|
|
47
47
|
* @param {Number} [fadeRate] - How quick to fade particles at start/end in percent of life
|
|
48
48
|
* @param {Number} [randomness] - Apply extra randomness percent
|
|
49
49
|
* @param {Boolean} [collideTiles] - Do particles collide against tiles
|
|
50
|
-
* @param {Boolean} [additive] - Should particles use
|
|
50
|
+
* @param {Boolean} [additive] - Should particles use additive blend
|
|
51
51
|
* @param {Boolean} [randomColorLinear] - Should color be randomized linearly or across each component
|
|
52
52
|
* @param {Number} [renderOrder] - Render order for particles (additive is above other stuff by default)
|
|
53
53
|
* @param {Boolean} [localSpace] - Should it be in local space of emitter (world space is default)
|
|
@@ -132,11 +132,11 @@ class ParticleEmitter extends EngineObject
|
|
|
132
132
|
this.randomness = randomness;
|
|
133
133
|
/** @property {Boolean} - Do particles collide against tiles */
|
|
134
134
|
this.collideTiles = collideTiles;
|
|
135
|
-
/** @property {Boolean} - Should particles use
|
|
135
|
+
/** @property {Boolean} - Should particles use additive blend */
|
|
136
136
|
this.additive = additive;
|
|
137
137
|
/** @property {Boolean} - Should it be in local space of emitter */
|
|
138
138
|
this.localSpace = localSpace;
|
|
139
|
-
/** @property {Number} - If non zero the
|
|
139
|
+
/** @property {Number} - If non zero the particle is drawn as a trail, stretched in the direction of velocity */
|
|
140
140
|
this.trailScale = 0;
|
|
141
141
|
/** @property {Function} - Callback when particle is destroyed */
|
|
142
142
|
this.particleDestroyCallback = undefined;
|
|
@@ -185,7 +185,7 @@ class ParticleEmitter extends EngineObject
|
|
|
185
185
|
angle += this.angle;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
// randomness scales each
|
|
188
|
+
// randomness scales each parameter by a percentage
|
|
189
189
|
const randomness = this.randomness;
|
|
190
190
|
const randomizeScale = (v)=> v + v*rand(randomness, -randomness);
|
|
191
191
|
|
|
@@ -214,7 +214,7 @@ class ParticleEmitter extends EngineObject
|
|
|
214
214
|
particle.renderOrder = this.renderOrder;
|
|
215
215
|
particle.mirror = !!randInt(2);
|
|
216
216
|
|
|
217
|
-
// call particle create
|
|
217
|
+
// call particle create callback
|
|
218
218
|
this.particleCreateCallback && this.particleCreateCallback(particle);
|
|
219
219
|
|
|
220
220
|
// return the newly created particle
|
package/src/engineSettings.js
CHANGED
|
@@ -106,7 +106,7 @@ let tileFixBleedScale = 0;
|
|
|
106
106
|
* @memberof Settings */
|
|
107
107
|
let enablePhysicsSolver = true;
|
|
108
108
|
|
|
109
|
-
/** Default object mass for collision
|
|
109
|
+
/** Default object mass for collision calculations (how heavy objects are)
|
|
110
110
|
* @type {Number}
|
|
111
111
|
* @default
|
|
112
112
|
* @memberof Settings */
|
|
@@ -317,7 +317,7 @@ function setFontDefault(font) { fontDefault = font; }
|
|
|
317
317
|
* @memberof Settings */
|
|
318
318
|
function setShowSplashScreen(show) { showSplashScreen = show; }
|
|
319
319
|
|
|
320
|
-
/** Set to
|
|
320
|
+
/** Set to disable rendering, audio, and input for servers
|
|
321
321
|
* @param {Boolean} headless
|
|
322
322
|
* @memberof Settings */
|
|
323
323
|
function setHeadlessMode(headless) { headlessMode = headless; }
|
|
@@ -347,7 +347,7 @@ function setTileFixBleedScale(scale) { tileFixBleedScale = scale; }
|
|
|
347
347
|
* @memberof Settings */
|
|
348
348
|
function setEnablePhysicsSolver(enable) { enablePhysicsSolver = enable; }
|
|
349
349
|
|
|
350
|
-
/** Set default object mass for
|
|
350
|
+
/** Set default object mass for collision calculations
|
|
351
351
|
* @param {Number} mass
|
|
352
352
|
* @memberof Settings */
|
|
353
353
|
function setObjectDefaultMass(mass) { objectDefaultMass = mass; }
|
|
@@ -417,7 +417,7 @@ function setTouchGamepadEnable(enable) { touchGamepadEnable = enable; }
|
|
|
417
417
|
* @memberof Settings */
|
|
418
418
|
function setTouchGamepadAnalog(analog) { touchGamepadAnalog = analog; }
|
|
419
419
|
|
|
420
|
-
/** Set size of
|
|
420
|
+
/** Set size of virtual gamepad for touch devices in pixels
|
|
421
421
|
* @param {Number} size
|
|
422
422
|
* @memberof Settings */
|
|
423
423
|
function setTouchGamepadSize(size) { touchGamepadSize = size; }
|
|
@@ -444,7 +444,7 @@ function setSoundVolume(volume)
|
|
|
444
444
|
{
|
|
445
445
|
soundVolume = volume;
|
|
446
446
|
if (soundEnable && !headlessMode && audioGainNode)
|
|
447
|
-
audioGainNode.gain.value = volume; // update gain
|
|
447
|
+
audioGainNode.gain.value = volume; // update gain immediately
|
|
448
448
|
}
|
|
449
449
|
|
|
450
450
|
/** Set default range where sound no longer plays
|
package/src/engineTileLayer.js
CHANGED
|
@@ -301,7 +301,7 @@ class TileLayer extends EngineObject
|
|
|
301
301
|
|
|
302
302
|
/** Draw the tile at a given position in the tile grid
|
|
303
303
|
* This can be used to clear out tiles when they are destroyed
|
|
304
|
-
* Tiles can also be redrawn if
|
|
304
|
+
* Tiles can also be redrawn if inside a redrawStart/End block
|
|
305
305
|
* @param {Vector2} layerPos
|
|
306
306
|
* @param {Boolean} [clear] - should the old tile be cleared out
|
|
307
307
|
*/
|
|
@@ -326,7 +326,7 @@ class TileLayer extends EngineObject
|
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
/** Draw directly to the 2D canvas in world space (
|
|
329
|
+
/** Draw directly to the 2D canvas in world space (bypass webgl)
|
|
330
330
|
* @param {Vector2} pos
|
|
331
331
|
* @param {Vector2} size
|
|
332
332
|
* @param {Number} angle
|
package/src/engineUtilities.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* @memberof Utilities */
|
|
17
17
|
const PI = Math.PI;
|
|
18
18
|
|
|
19
|
-
/** Returns
|
|
19
|
+
/** Returns absolute value of value passed in
|
|
20
20
|
* @param {Number} value
|
|
21
21
|
* @return {Number}
|
|
22
22
|
* @memberof Utilities */
|
|
@@ -49,7 +49,7 @@ function sign(value) { return Math.sign(value); }
|
|
|
49
49
|
* @memberof Utilities */
|
|
50
50
|
function mod(dividend, divisor=1) { return ((dividend % divisor) + divisor) % divisor; }
|
|
51
51
|
|
|
52
|
-
/** Clamps the value
|
|
52
|
+
/** Clamps the value between max and min
|
|
53
53
|
* @param {Number} value
|
|
54
54
|
* @param {Number} [min]
|
|
55
55
|
* @param {Number} [max]
|
|
@@ -485,7 +485,7 @@ class Vector2
|
|
|
485
485
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
-
/** Set the integer direction of this vector,
|
|
488
|
+
/** Set the integer direction of this vector, corresponding to multiples of 90 degree rotation (0-3)
|
|
489
489
|
* @param {Number} [direction]
|
|
490
490
|
* @param {Number} [length] */
|
|
491
491
|
setDirection(direction, length=1)
|
|
@@ -496,7 +496,7 @@ class Vector2
|
|
|
496
496
|
direction%2 ? 0 : direction ? -length : length);
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
-
/** Returns the integer direction of this vector,
|
|
499
|
+
/** Returns the integer direction of this vector, corresponding to multiples of 90 degree rotation (0-3)
|
|
500
500
|
* @return {Number} */
|
|
501
501
|
direction()
|
|
502
502
|
{ return abs(this.x) > abs(this.y) ? this.x < 0 ? 3 : 1 : this.y < 0 ? 2 : 0; }
|
|
@@ -881,7 +881,7 @@ const MAGENTA = rgb(1,0,1);
|
|
|
881
881
|
* a.set(3); // sets the timer to 3 seconds
|
|
882
882
|
*
|
|
883
883
|
* let b = new Timer(1); // creates a timer with 1 second left
|
|
884
|
-
* b.unset(); //
|
|
884
|
+
* b.unset(); // unset the timer
|
|
885
885
|
*/
|
|
886
886
|
class Timer
|
|
887
887
|
{
|
package/src/engineWebGL.js
CHANGED
|
@@ -22,7 +22,7 @@ let glCanvas;
|
|
|
22
22
|
* @memberof WebGL */
|
|
23
23
|
let glContext;
|
|
24
24
|
|
|
25
|
-
/**
|
|
25
|
+
/** Should webgl be setup with anti-aliasing? must be set before calling engineInit
|
|
26
26
|
* @type {Boolean}
|
|
27
27
|
* @memberof WebGL */
|
|
28
28
|
let glAntialias = true;
|
|
@@ -30,9 +30,15 @@ let glAntialias = true;
|
|
|
30
30
|
// WebGL internal variables not exposed to documentation
|
|
31
31
|
let glShader, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glInstanceCount, glAdditive, glBatchAdditive;
|
|
32
32
|
|
|
33
|
+
// WebGL internal constants
|
|
34
|
+
const gl_MAX_INSTANCES = 1e4;
|
|
35
|
+
const gl_INDICES_PER_INSTANCE = 11;
|
|
36
|
+
const gl_INSTANCE_BYTE_STRIDE = gl_INDICES_PER_INSTANCE * 4;
|
|
37
|
+
const gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
|
|
38
|
+
|
|
33
39
|
///////////////////////////////////////////////////////////////////////////////
|
|
34
40
|
|
|
35
|
-
//
|
|
41
|
+
// Initialize WebGL, called automatically by the engine
|
|
36
42
|
function glInit()
|
|
37
43
|
{
|
|
38
44
|
if (!glEnable || headlessMode) return;
|
|
@@ -82,8 +88,8 @@ function glInit()
|
|
|
82
88
|
|
|
83
89
|
// create the geometry buffer, triangle strip square
|
|
84
90
|
const geometry = new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);
|
|
85
|
-
glContext.bindBuffer(
|
|
86
|
-
glContext.bufferData(
|
|
91
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
92
|
+
glContext.bufferData(glContext.ARRAY_BUFFER, geometry, glContext.STATIC_DRAW);
|
|
87
93
|
}
|
|
88
94
|
|
|
89
95
|
// Setup render each frame, called automatically by engine
|
|
@@ -91,15 +97,12 @@ function glPreRender()
|
|
|
91
97
|
{
|
|
92
98
|
if (!glEnable || headlessMode) return;
|
|
93
99
|
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
97
|
-
|
|
98
|
-
// set up the shader
|
|
100
|
+
// set up the shader and canvas
|
|
101
|
+
glClearCanvas();
|
|
99
102
|
glContext.useProgram(glShader);
|
|
100
|
-
glContext.activeTexture(
|
|
103
|
+
glContext.activeTexture(glContext.TEXTURE0);
|
|
101
104
|
if (textureInfos[0])
|
|
102
|
-
glContext.bindTexture(
|
|
105
|
+
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture = textureInfos[0].glTexture);
|
|
103
106
|
|
|
104
107
|
// set vertex attributes
|
|
105
108
|
let offset = glAdditive = glBatchAdditive = 0;
|
|
@@ -114,15 +117,15 @@ function glPreRender()
|
|
|
114
117
|
glContext.vertexAttribDivisor(location, divisor);
|
|
115
118
|
offset += size*typeSize;
|
|
116
119
|
}
|
|
117
|
-
glContext.bindBuffer(
|
|
118
|
-
initVertexAttribArray('g',
|
|
119
|
-
glContext.bindBuffer(
|
|
120
|
-
glContext.bufferData(
|
|
121
|
-
initVertexAttribArray('p',
|
|
122
|
-
initVertexAttribArray('u',
|
|
123
|
-
initVertexAttribArray('c',
|
|
124
|
-
initVertexAttribArray('a',
|
|
125
|
-
initVertexAttribArray('r',
|
|
120
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
121
|
+
initVertexAttribArray('g', glContext.FLOAT, 0, 2); // geometry
|
|
122
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
|
|
123
|
+
glContext.bufferData(glContext.ARRAY_BUFFER, gl_INSTANCE_BUFFER_SIZE, glContext.DYNAMIC_DRAW);
|
|
124
|
+
initVertexAttribArray('p', glContext.FLOAT, 4, 4); // position & size
|
|
125
|
+
initVertexAttribArray('u', glContext.FLOAT, 4, 4); // texture coords
|
|
126
|
+
initVertexAttribArray('c', glContext.UNSIGNED_BYTE, 1, 4); // color
|
|
127
|
+
initVertexAttribArray('a', glContext.UNSIGNED_BYTE, 1, 4); // additiveColor
|
|
128
|
+
initVertexAttribArray('r', glContext.FLOAT, 4, 1); // rotation
|
|
126
129
|
|
|
127
130
|
// build the transform matrix
|
|
128
131
|
const s = vec2(2*cameraScale).divide(mainCanvasSize);
|
|
@@ -137,6 +140,15 @@ function glPreRender()
|
|
|
137
140
|
);
|
|
138
141
|
}
|
|
139
142
|
|
|
143
|
+
/** Clear the canvas and setup the viewport
|
|
144
|
+
* @memberof WebGL */
|
|
145
|
+
function glClearCanvas()
|
|
146
|
+
{
|
|
147
|
+
// clear and set to same size as main canvas
|
|
148
|
+
glContext.viewport(0, 0, glCanvas.width=mainCanvas.width, glCanvas.height=mainCanvas.height);
|
|
149
|
+
glContext.clear(glContext.COLOR_BUFFER_BIT);
|
|
150
|
+
}
|
|
151
|
+
|
|
140
152
|
/** Set the WebGl texture, called automatically if using multiple textures
|
|
141
153
|
* - This may also flush the gl buffer resulting in more draw calls and worse performance
|
|
142
154
|
* @param {WebGLTexture} texture
|
|
@@ -148,7 +160,7 @@ function glSetTexture(texture)
|
|
|
148
160
|
return;
|
|
149
161
|
|
|
150
162
|
glFlush();
|
|
151
|
-
glContext.bindTexture(
|
|
163
|
+
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture = texture);
|
|
152
164
|
}
|
|
153
165
|
|
|
154
166
|
/** Compile WebGL shader of the given type, will throw errors if in debug mode
|
|
@@ -164,7 +176,7 @@ function glCompileShader(source, type)
|
|
|
164
176
|
glContext.compileShader(shader);
|
|
165
177
|
|
|
166
178
|
// check for errors
|
|
167
|
-
if (debug && !glContext.getShaderParameter(shader,
|
|
179
|
+
if (debug && !glContext.getShaderParameter(shader, glContext.COMPILE_STATUS))
|
|
168
180
|
throw glContext.getShaderInfoLog(shader);
|
|
169
181
|
return shader;
|
|
170
182
|
}
|
|
@@ -178,12 +190,12 @@ function glCreateProgram(vsSource, fsSource)
|
|
|
178
190
|
{
|
|
179
191
|
// build the program
|
|
180
192
|
const program = glContext.createProgram();
|
|
181
|
-
glContext.attachShader(program, glCompileShader(vsSource,
|
|
182
|
-
glContext.attachShader(program, glCompileShader(fsSource,
|
|
193
|
+
glContext.attachShader(program, glCompileShader(vsSource, glContext.VERTEX_SHADER));
|
|
194
|
+
glContext.attachShader(program, glCompileShader(fsSource, glContext.FRAGMENT_SHADER));
|
|
183
195
|
glContext.linkProgram(program);
|
|
184
196
|
|
|
185
197
|
// check for errors
|
|
186
|
-
if (debug && !glContext.getProgramParameter(program,
|
|
198
|
+
if (debug && !glContext.getProgramParameter(program, glContext.LINK_STATUS))
|
|
187
199
|
throw glContext.getProgramInfoLog(program);
|
|
188
200
|
return program;
|
|
189
201
|
}
|
|
@@ -196,20 +208,20 @@ function glCreateTexture(image)
|
|
|
196
208
|
{
|
|
197
209
|
// build the texture
|
|
198
210
|
const texture = glContext.createTexture();
|
|
199
|
-
glContext.bindTexture(
|
|
211
|
+
glContext.bindTexture(glContext.TEXTURE_2D, texture);
|
|
200
212
|
if (image && image.width)
|
|
201
|
-
glContext.texImage2D(
|
|
213
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, image);
|
|
202
214
|
else
|
|
203
215
|
{
|
|
204
216
|
// create a white texture
|
|
205
217
|
const whitePixel = new Uint8Array([255, 255, 255, 255]);
|
|
206
|
-
glContext.texImage2D(
|
|
218
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, 1, 1, 0, glContext.RGBA, glContext.UNSIGNED_BYTE, whitePixel);
|
|
207
219
|
}
|
|
208
220
|
|
|
209
221
|
// use point filtering for pixelated rendering
|
|
210
|
-
const filter = tilesPixelated ?
|
|
211
|
-
glContext.texParameteri(
|
|
212
|
-
glContext.texParameteri(
|
|
222
|
+
const filter = tilesPixelated ? glContext.NEAREST : glContext.LINEAR;
|
|
223
|
+
glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MIN_FILTER, filter);
|
|
224
|
+
glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MAG_FILTER, filter);
|
|
213
225
|
return texture;
|
|
214
226
|
}
|
|
215
227
|
|
|
@@ -219,20 +231,20 @@ function glFlush()
|
|
|
219
231
|
{
|
|
220
232
|
if (!glInstanceCount) return;
|
|
221
233
|
|
|
222
|
-
const destBlend = glBatchAdditive ?
|
|
223
|
-
glContext.blendFuncSeparate(
|
|
224
|
-
glContext.enable(
|
|
234
|
+
const destBlend = glBatchAdditive ? glContext.ONE : glContext.ONE_MINUS_SRC_ALPHA;
|
|
235
|
+
glContext.blendFuncSeparate(glContext.SRC_ALPHA, destBlend, glContext.ONE, destBlend);
|
|
236
|
+
glContext.enable(glContext.BLEND);
|
|
225
237
|
|
|
226
238
|
// draw all the sprites in the batch and reset the buffer
|
|
227
|
-
glContext.bufferSubData(
|
|
228
|
-
glContext.drawArraysInstanced(
|
|
239
|
+
glContext.bufferSubData(glContext.ARRAY_BUFFER, 0, glPositionData);
|
|
240
|
+
glContext.drawArraysInstanced(glContext.TRIANGLE_STRIP, 0, 4, glInstanceCount);
|
|
229
241
|
if (showWatermark)
|
|
230
242
|
drawCount += glInstanceCount;
|
|
231
243
|
glInstanceCount = 0;
|
|
232
244
|
glBatchAdditive = glAdditive;
|
|
233
245
|
}
|
|
234
246
|
|
|
235
|
-
/** Draw any sprites still in the buffer
|
|
247
|
+
/** Draw any sprites still in the buffer and copy to main canvas
|
|
236
248
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
237
249
|
* @param {Boolean} [forceDraw]
|
|
238
250
|
* @memberof WebGL */
|
|
@@ -247,7 +259,7 @@ function glCopyToContext(context, forceDraw=false)
|
|
|
247
259
|
context.drawImage(glCanvas, 0, 0);
|
|
248
260
|
}
|
|
249
261
|
|
|
250
|
-
/** Set
|
|
262
|
+
/** Set anti-aliasing for webgl canvas
|
|
251
263
|
* @param {Boolean} [antialias]
|
|
252
264
|
* @memberof WebGL */
|
|
253
265
|
function glSetAntialias(antialias=true)
|
|
@@ -277,7 +289,7 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
|
|
|
277
289
|
if (glInstanceCount >= gl_MAX_INSTANCES || glBatchAdditive != glAdditive)
|
|
278
290
|
glFlush();
|
|
279
291
|
|
|
280
|
-
let offset = glInstanceCount *
|
|
292
|
+
let offset = glInstanceCount++ * gl_INDICES_PER_INSTANCE;
|
|
281
293
|
glPositionData[offset++] = x;
|
|
282
294
|
glPositionData[offset++] = y;
|
|
283
295
|
glPositionData[offset++] = sizeX;
|
|
@@ -289,38 +301,4 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
|
|
|
289
301
|
glColorData[offset++] = rgba;
|
|
290
302
|
glColorData[offset++] = rgbaAdditive;
|
|
291
303
|
glPositionData[offset++] = angle;
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
296
|
-
// store gl constants as integers so their name doesn't use space in minifed
|
|
297
|
-
const
|
|
298
|
-
gl_ONE = 1,
|
|
299
|
-
gl_TRIANGLE_STRIP = 5,
|
|
300
|
-
gl_SRC_ALPHA = 770,
|
|
301
|
-
gl_ONE_MINUS_SRC_ALPHA = 771,
|
|
302
|
-
gl_BLEND = 3042,
|
|
303
|
-
gl_TEXTURE_2D = 3553,
|
|
304
|
-
gl_UNSIGNED_BYTE = 5121,
|
|
305
|
-
gl_FLOAT = 5126,
|
|
306
|
-
gl_RGBA = 6408,
|
|
307
|
-
gl_NEAREST = 9728,
|
|
308
|
-
gl_LINEAR = 9729,
|
|
309
|
-
gl_TEXTURE_MAG_FILTER = 10240,
|
|
310
|
-
gl_TEXTURE_MIN_FILTER = 10241,
|
|
311
|
-
gl_COLOR_BUFFER_BIT = 16384,
|
|
312
|
-
gl_TEXTURE0 = 33984,
|
|
313
|
-
gl_ARRAY_BUFFER = 34962,
|
|
314
|
-
gl_STATIC_DRAW = 35044,
|
|
315
|
-
gl_DYNAMIC_DRAW = 35048,
|
|
316
|
-
gl_FRAGMENT_SHADER = 35632,
|
|
317
|
-
gl_VERTEX_SHADER = 35633,
|
|
318
|
-
gl_COMPILE_STATUS = 35713,
|
|
319
|
-
gl_LINK_STATUS = 35714,
|
|
320
|
-
gl_UNPACK_FLIP_Y_WEBGL = 37440,
|
|
321
|
-
|
|
322
|
-
// constants for batch rendering
|
|
323
|
-
gl_INDICIES_PER_INSTANCE = 11,
|
|
324
|
-
gl_MAX_INSTANCES = 1e4,
|
|
325
|
-
gl_INSTANCE_BYTE_STRIDE = gl_INDICIES_PER_INSTANCE * 4, // 11 * 4
|
|
326
|
-
gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
|
|
304
|
+
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* LittleJS Build System
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
'use strict';
|
|
8
|
-
|
|
9
|
-
const PROGRAM_NAME = 'game';
|
|
10
|
-
const BUILD_FOLDER = 'build';
|
|
11
|
-
const sourceFiles =
|
|
12
|
-
[
|
|
13
|
-
'../../dist/littlejs.release.js',
|
|
14
|
-
'game.js',
|
|
15
|
-
// add your game's files here
|
|
16
|
-
];
|
|
17
|
-
const dataFiles =
|
|
18
|
-
[
|
|
19
|
-
'tiles.png',
|
|
20
|
-
// add your game's data files here
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
console.log(`Building ${PROGRAM_NAME} with electron...`);
|
|
24
|
-
const startTime = Date.now();
|
|
25
|
-
const fs = require('node:fs');
|
|
26
|
-
const child_process = require('node:child_process');
|
|
27
|
-
|
|
28
|
-
// remove old files and setup build folder
|
|
29
|
-
fs.rmSync(BUILD_FOLDER, { recursive: true, force: true });
|
|
30
|
-
fs.rmSync(`${PROGRAM_NAME}.zip`, { force: true });
|
|
31
|
-
fs.mkdirSync(BUILD_FOLDER);
|
|
32
|
-
|
|
33
|
-
// copy data files
|
|
34
|
-
for(const file of dataFiles)
|
|
35
|
-
fs.copyFileSync(file, `${BUILD_FOLDER}/${file}`);
|
|
36
|
-
|
|
37
|
-
Build
|
|
38
|
-
(
|
|
39
|
-
`${BUILD_FOLDER}/index.js`,
|
|
40
|
-
sourceFiles,
|
|
41
|
-
[closureCompilerStep, uglifyBuildStep, htmlBuildStep, electronBuildStep]
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
console.log(`Build Completed in ${((Date.now() - startTime)/1e3).toFixed(2)} seconds!`);
|
|
45
|
-
|
|
46
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
47
|
-
|
|
48
|
-
// A single build with its own source files, build steps, and output file
|
|
49
|
-
// - each build step is a callback that accepts a single filename
|
|
50
|
-
function Build(outputFile, files=[], buildSteps=[])
|
|
51
|
-
{
|
|
52
|
-
// copy files into a buffer
|
|
53
|
-
let buffer = '';
|
|
54
|
-
for (const file of files)
|
|
55
|
-
buffer += fs.readFileSync(file) + '\n';
|
|
56
|
-
|
|
57
|
-
// output file
|
|
58
|
-
fs.writeFileSync(outputFile, buffer, {flag: 'w+'});
|
|
59
|
-
|
|
60
|
-
// execute build steps in order
|
|
61
|
-
for (const buildStep of buildSteps)
|
|
62
|
-
buildStep(outputFile);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function closureCompilerStep(filename)
|
|
66
|
-
{
|
|
67
|
-
console.log(`Running closure compiler...`);
|
|
68
|
-
|
|
69
|
-
const filenameTemp = filename + '.tmp';
|
|
70
|
-
fs.copyFileSync(filename, filenameTemp);
|
|
71
|
-
child_process.execSync(`npx google-closure-compiler --js=${filenameTemp} --js_output_file=${filename} --compilation_level=ADVANCED --warning_level=VERBOSE --jscomp_off=* --assume_function_wrapper`, {stdio: 'inherit'});
|
|
72
|
-
fs.rmSync(filenameTemp);
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
function uglifyBuildStep(filename)
|
|
76
|
-
{
|
|
77
|
-
console.log(`Running uglify...`);
|
|
78
|
-
child_process.execSync(`npx uglifyjs ${filename} -c -m -o ${filename}`, {stdio: 'inherit'});
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
function htmlBuildStep(filename)
|
|
82
|
-
{
|
|
83
|
-
console.log(`Building html...`);
|
|
84
|
-
|
|
85
|
-
// create html file
|
|
86
|
-
let buffer = '<!DOCTYPE html>';
|
|
87
|
-
buffer += '<script>';
|
|
88
|
-
buffer += fs.readFileSync(filename) + '\n';
|
|
89
|
-
buffer += '</script>';
|
|
90
|
-
|
|
91
|
-
// output html file
|
|
92
|
-
fs.writeFileSync(`${BUILD_FOLDER}/index.html`, buffer, {flag: 'w+'});
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
function electronBuildStep(filename)
|
|
96
|
-
{
|
|
97
|
-
console.log(`Building executable with electron...`);
|
|
98
|
-
|
|
99
|
-
// delete intermediate files
|
|
100
|
-
fs.rmSync(filename);
|
|
101
|
-
|
|
102
|
-
// copy elecron files to build folder
|
|
103
|
-
fs.copyFileSync('electron.js', `${BUILD_FOLDER}/electron.js`);
|
|
104
|
-
fs.copyFileSync('package.json', `${BUILD_FOLDER}/package.json`);
|
|
105
|
-
|
|
106
|
-
child_process.execSync(`npx electron-packager ./${BUILD_FOLDER} --overwrite`, {stdio: 'inherit'});
|
|
107
|
-
};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// Modules to control application life and create native browser window
|
|
2
|
-
const { app, BrowserWindow } = require('electron')
|
|
3
|
-
|
|
4
|
-
const createWindow = () => {
|
|
5
|
-
// Create the browser window.
|
|
6
|
-
const mainWindow = new BrowserWindow({
|
|
7
|
-
width: 800,
|
|
8
|
-
height: 600
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
// hide menu
|
|
12
|
-
mainWindow.setMenu(null);
|
|
13
|
-
//mainWindow.setFullScreen(true);
|
|
14
|
-
|
|
15
|
-
// and load the index.html of the app.
|
|
16
|
-
mainWindow.loadFile('index.html')
|
|
17
|
-
|
|
18
|
-
// Open the DevTools.
|
|
19
|
-
// mainWindow.webContents.openDevTools()
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// This method will be called when Electron has finished
|
|
23
|
-
// initialization and is ready to create browser windows.
|
|
24
|
-
// Some APIs can only be used after this event occurs.
|
|
25
|
-
app.whenReady().then(() => {
|
|
26
|
-
createWindow()
|
|
27
|
-
|
|
28
|
-
app.on('activate', () => {
|
|
29
|
-
// On macOS it's common to re-create a window in the app when the
|
|
30
|
-
// dock icon is clicked and there are no other windows open.
|
|
31
|
-
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
// Quit when all windows are closed, except on macOS. There, it's common
|
|
36
|
-
// for applications and their menu bar to stay active until the user quits
|
|
37
|
-
// explicitly with Cmd + Q.
|
|
38
|
-
app.on('window-all-closed', () => {
|
|
39
|
-
if (process.platform !== 'darwin') app.quit()
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
// In this file you can include the rest of your app's specific main process
|
|
43
|
-
// code. You can also put them in separate files and require them here.
|