littlejsengine 1.11.2 → 1.11.5

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.
Files changed (73) hide show
  1. package/.vscode/launch.json +14 -0
  2. package/README.md +16 -18
  3. package/dist/littlejs.d.ts +41 -30
  4. package/dist/littlejs.esm.js +165 -143
  5. package/dist/littlejs.esm.min.js +1 -1
  6. package/dist/littlejs.js +161 -142
  7. package/dist/littlejs.min.js +1 -1
  8. package/dist/littlejs.release.js +157 -135
  9. package/examples/box2d/gameObjects.js +4 -4
  10. package/examples/box2d/scenes.js +2 -2
  11. package/examples/breakout/game.js +1 -1
  12. package/examples/breakout/gameObjects.js +28 -29
  13. package/examples/breakout/index.html +1 -1
  14. package/examples/breakoutTutorial/game.js +1 -1
  15. package/examples/index.html +1 -7
  16. package/examples/logo.png +0 -0
  17. package/examples/module/game.js +1 -1
  18. package/examples/platformer/game.js +2 -2
  19. package/examples/platformer/gameCharacter.js +5 -5
  20. package/examples/platformer/gameEffects.js +4 -4
  21. package/examples/platformer/gameObjects.js +2 -2
  22. package/examples/puzzle/game.js +3 -3
  23. package/examples/starter/game.js +11 -45
  24. package/examples/starter/tiles.png +0 -0
  25. package/jsconfig.json +1 -0
  26. package/package.json +1 -1
  27. package/plugins/box2d.js +10 -10
  28. package/plugins/newgrounds.js +1 -1
  29. package/plugins/postProcess.js +8 -8
  30. package/shortExamples/base.html +36 -0
  31. package/shortExamples/code/blending.js +12 -0
  32. package/shortExamples/code/helloWorld.js +7 -0
  33. package/shortExamples/code/particles.js +28 -0
  34. package/shortExamples/code/playSound.js +36 -0
  35. package/shortExamples/code/pong.js +40 -0
  36. package/shortExamples/code/shapes.js +24 -0
  37. package/shortExamples/code/spriteAtlas.js +27 -0
  38. package/shortExamples/code/systemFont.js +14 -0
  39. package/shortExamples/code/texture.js +12 -0
  40. package/shortExamples/code/tileLayer.js +50 -0
  41. package/shortExamples/index.html +242 -0
  42. package/shortExamples/tiles.png +0 -0
  43. package/src/engine.js +17 -3
  44. package/src/engineAudio.js +4 -4
  45. package/src/engineBuild.js +2 -1
  46. package/src/engineDebug.js +4 -7
  47. package/src/engineDraw.js +50 -25
  48. package/src/engineExport.js +4 -1
  49. package/src/engineInput.js +1 -1
  50. package/src/engineObject.js +8 -8
  51. package/src/engineParticles.js +7 -7
  52. package/src/engineSettings.js +5 -5
  53. package/src/engineTileLayer.js +6 -2
  54. package/src/engineUtilities.js +5 -5
  55. package/src/engineWebGL.js +52 -74
  56. package/examples/electron/build.js +0 -107
  57. package/examples/electron/electron.js +0 -43
  58. package/examples/electron/game.js +0 -131
  59. package/examples/electron/index.html +0 -13
  60. package/examples/electron/package.json +0 -22
  61. package/examples/electron/tiles.png +0 -0
  62. package/examples/js13k/build.bat +0 -2
  63. package/examples/js13k/build.js +0 -131
  64. package/examples/js13k/game.js +0 -118
  65. package/examples/js13k/index.html +0 -21
  66. package/examples/js13k/tiles.png +0 -0
  67. package/examples/typescript/build.bat +0 -5
  68. package/examples/typescript/build.js +0 -33
  69. package/examples/typescript/game.js +0 -102
  70. package/examples/typescript/game.ts +0 -134
  71. package/examples/typescript/index.html +0 -10
  72. package/examples/typescript/tiles.png +0 -0
  73. package/examples/typescript/tsconfig.json +0 -8
@@ -0,0 +1,7 @@
1
+ function gameRender()
2
+ {
3
+ // draw to overlay canvas
4
+ drawTextScreen('Hello World',
5
+ mainCanvasSize.scale(.5), 80, // position, size
6
+ hsl(0,0,1), 6, hsl(0,0,0)); // color, outline size and color
7
+ }
@@ -0,0 +1,28 @@
1
+ function gameInit()
2
+ {
3
+ gravity = -.01; // set default gravity
4
+
5
+ // fire
6
+ new ParticleEmitter(
7
+ vec2(-5,-2), 0, // pos, angle
8
+ 2, 0, 200, PI, // emitSize, emitTime, emitRate, emitCone
9
+ tile(0), // tileInfo
10
+ rgb(1,.5,.1), rgb(1,.1,.1), // colorStartA, colorStartB
11
+ rgb(1,.5,.1,0), rgb(1,.1,.1,0), // colorEndA, colorEndB
12
+ .7, 2, 0, .2, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
13
+ .9, 1, -1, PI, .05, // damp, angleDamp, gravity, particleCone, fade
14
+ .5, 0, 1, 0, 1e9 // randomness, collide, additive, colorLinear, renderOrder
15
+ );
16
+
17
+ // smoke
18
+ new ParticleEmitter(
19
+ vec2(5,-2), 0, // pos, angle
20
+ 3, 0, 100, PI, // emitSize, emitTime, emitRate, emitCone
21
+ tile(0), // tileInfo
22
+ hsl(0,0,0,.5), hsl(0,0,1,.5), // colorStartA, colorStartB
23
+ hsl(0,0,0,0), hsl(0,0,1,0), // colorEndA, colorEndB
24
+ 1, 1, 5, .2, .01, // time, sizeStart, sizeEnd, speed, angleSpeed
25
+ .85, 1, -1, PI, .3, // damp, angleDamp, gravity, particleCone, fade
26
+ .5, 0, 0, 1, 1e8 // randomness, collide, additive, colorLinear, renderOrder
27
+ );
28
+ }
@@ -0,0 +1,36 @@
1
+ class SoundButton extends EngineObject
2
+ {
3
+ constructor(pos, icon, sound)
4
+ {
5
+ super(pos, vec2(5,4));
6
+ this.icon = icon;
7
+ this.sound = sound;
8
+ }
9
+
10
+ update()
11
+ {
12
+ if (mouseWasPressed(0) && isOverlapping(this.pos, this.size, mousePos))
13
+ this.sound.play(this.pos);
14
+ }
15
+
16
+ render()
17
+ {
18
+ drawRect(this.pos, this.size, hsl(0,0,.8));
19
+ drawTextOverlay(this.icon, this.pos, 3);
20
+ }
21
+ }
22
+
23
+ function gameInit()
24
+ {
25
+ new SoundButton(vec2(-6, 5),'💰', new Sound([,,1675,,.06,.24,1,1.82,,,837,.06]));
26
+ new SoundButton(vec2( 0, 5),'🥊', new Sound([,,925,.04,.3,.6,1,.3,,6.27,-184,.09,.17]));
27
+ new SoundButton(vec2( 6, 5),'✨', new Sound([,,539,0,.04,.29,1,1.92,,,567,.02,.02,,,,.04]));
28
+
29
+ new SoundButton(vec2(-6, 0),'🐁', new Sound([,.2,1e3,.02,,.01,2,,18,,475,.01,.01]));
30
+ new SoundButton(vec2( 0, 0),'🎹', new Sound([1.5,.5,270,,.1,,1,1.5,,,,,,,,.1,.01]));
31
+ new SoundButton(vec2( 6, 0),'🏌️', new Sound([,,150,.05,,.05,,1.3,,,,,,3]));
32
+
33
+ new SoundButton(vec2(-6,-5),'🌊', new Sound([,.2,40,.5,,1.5,,11,,,,,,199]));
34
+ new SoundButton(vec2( 0,-5),'🛰️', new Sound([,.5,847,.02,.3,.9,1,1.67,,,-294,.04,.13,,,,.1]));
35
+ new SoundButton(vec2( 6,-5),'⚡', new Sound([,,471,,.09,.47,4,1.06,-6.7,,,,,.9,61,.1,,.82,.1]));
36
+ }
@@ -0,0 +1,40 @@
1
+ // globals objects
2
+ let paddle, ball;
3
+
4
+ class PhysicsObject extends EngineObject
5
+ {
6
+ constructor(pos, size)
7
+ {
8
+ super(pos, size); // set object position and size
9
+ this.setCollision(); // make object collide
10
+ this.mass = 0; // make object have static physics
11
+ }
12
+ }
13
+
14
+ function gameInit()
15
+ {
16
+ // setup level
17
+ const levelSize = vec2(38, 21); // size of play area
18
+ cameraPos = levelSize.scale(.5); // center camera in level
19
+ canvasFixedSize = vec2(1280, 720); // use a 720p fixed size canvas
20
+
21
+ // create objects
22
+ paddle = new PhysicsObject(vec2(0,1), vec2(6,1)); // create player's paddle
23
+ new PhysicsObject(vec2(-.5,levelSize.y/2), vec2(1,100)) // top wall
24
+ new PhysicsObject(vec2(levelSize.x+.5,levelSize.y/2), vec2(1,100)) // left wall
25
+ new PhysicsObject(vec2(levelSize.x/2,levelSize.y+.5), vec2(100,1)) // right wall
26
+ }
27
+
28
+ function gameUpdate()
29
+ {
30
+ if (!ball || ball.pos.y < -1) // spawn ball
31
+ {
32
+ ball && ball.destroy(); // destroy old ball
33
+ ball = new PhysicsObject(cameraPos); // create a ball
34
+ ball.velocity = vec2(.2); // give ball some movement
35
+ ball.elasticity = 1; // make ball bounce
36
+ ball.mass = 1; // make ball have dynamic physics
37
+ }
38
+
39
+ paddle.pos.x = mousePos.x; // move paddle to mouse
40
+ }
@@ -0,0 +1,24 @@
1
+ function gameRender()
2
+ {
3
+ // polygon shapes
4
+ for(let j=3; j<7; ++j)
5
+ {
6
+ let points = [];
7
+ for(let i=0; i<j; ++i)
8
+ {
9
+ let angle = i * Math.PI * 2 / j;
10
+ points.push(vec2(j*4-18,0).add(vec2(Math.sin(angle), Math.cos(angle))));
11
+ }
12
+ drawPoly(points, hsl(j/4,1,.5)); // draw a star
13
+ }
14
+
15
+ // circles and ellipses
16
+ drawCircle(vec2(0,5), 1, 0, hsl(.15,1,.5));
17
+ drawEllipse(vec2(-5,5), 2, 1, 0, hsl(0,1,.5));
18
+ drawEllipse(vec2(5,5), 1, .5, 0, hsl(.55,1,.5));
19
+
20
+ // lines and rects
21
+ drawRect(vec2(0,-5), vec2(13,2), hsl(.6,1,.5));
22
+ drawLine(vec2(-5,-7), vec2(5,-3), 1, hsl(0,0,1));
23
+ drawLine(vec2(-5,-3), vec2(5,-7), .2, hsl(1,1,.5));
24
+ }
@@ -0,0 +1,27 @@
1
+ let spriteAtlas;
2
+
3
+ function gameInit()
4
+ {
5
+ // create a table of all sprites
6
+ const gameTile = (i, size=16)=> tile(i, size);
7
+ spriteAtlas =
8
+ {
9
+ circle: gameTile(0),
10
+ crate: gameTile(1),
11
+ icon: gameTile(2),
12
+ largeIcon: gameTile(vec2(1,1),128),
13
+ };
14
+ }
15
+
16
+ function gameRender()
17
+ {
18
+ drawRect(vec2(0,0), vec2(100), GRAY); // draw background
19
+
20
+ // draw a sprite from the atlas
21
+ let pos = vec2(0,0); // world position to draw
22
+ let angle = 0; // world space angle to draw the tile
23
+ let size = vec2(15); // world size of the tile
24
+ let mirror = 0; // should tile be mirrored?
25
+ let color = hsl(0,0,1); // color to multiply the tile by
26
+ drawTile(pos, size, spriteAtlas.largeIcon, color, angle, mirror);
27
+ }
@@ -0,0 +1,14 @@
1
+ function gameRender()
2
+ {
3
+ const font = new FontImage;
4
+ font.drawText('System Font Test ', cameraPos.add(vec2(0,3)), .2, true);
5
+
6
+ for(let j=1; j<4; j++)
7
+ {
8
+ let s = '';
9
+ for(let i=0; i<32; ++i)
10
+ s += String.fromCharCode(32*j + i);
11
+ font.drawText(s, cameraPos.add(vec2(0,1-j)), .1, true);
12
+ }
13
+ }
14
+
@@ -0,0 +1,12 @@
1
+ function gameRender()
2
+ {
3
+ // show the full texture
4
+ let pos = vec2(0,0); // world position to draw
5
+ let size = vec2(15); // world size of the tile
6
+ let color = hsl(0,0,1); // color to multiply the tile by
7
+ let tilePos = vec2(0,0); // top left corner of tile in pixels
8
+ let tileSize = vec2(256); // size of tile in pixels
9
+ let tileInfo = new TileInfo(tilePos, tileSize); // tile info
10
+ drawRect(pos, size, GRAY); // draw background
11
+ drawTile(pos, size, tileInfo, color);
12
+ }
@@ -0,0 +1,50 @@
1
+ function gameInit()
2
+ {
3
+ cameraPos = vec2(16); // setup camera
4
+ gravity = -.01; // enable gravity
5
+
6
+ // create tile collision and visible tile layer
7
+ initTileCollision(vec2(32));
8
+ const pos = vec2();
9
+ const tileLayer = new TileLayer(pos, tileCollisionSize);
10
+
11
+ // init the tile layer
12
+ for (pos.x = tileCollisionSize.x; pos.x--;)
13
+ for (pos.y = tileCollisionSize.y; pos.y--;)
14
+ {
15
+ // check if tile should be solid
16
+ if (rand() < .7)
17
+ continue;
18
+
19
+ // set tile data
20
+ const tileIndex = 1;
21
+ const direction = randInt(4)
22
+ const mirror = !randInt(2);
23
+ const color = randColor(WHITE, hsl(0,0,.2));
24
+ const data = new TileLayerData(tileIndex, direction, mirror, color);
25
+ tileLayer.setData(pos, data);
26
+ setTileCollisionData(pos, 1);
27
+ }
28
+ tileLayer.redraw(); // redraw tile layer with new data
29
+ }
30
+
31
+ function gameUpdate()
32
+ {
33
+ if (mouseWasPressed(0))
34
+ {
35
+ // create particle emitter
36
+ const hue = rand();
37
+ let particleEmitter = new ParticleEmitter(
38
+ mousePos, 0, // emitPos, emitAngle
39
+ 0, 0.1, 500, PI, // emitSize, emitTime, emitRate, emitCone
40
+ tile(0, 16), // tileIndex, tileSize
41
+ hsl(hue,1,.5), hsl(hue,1,1), // colorStartA, colorStartB
42
+ hsl(hue,1,.5,0), hsl(hue,1,1,0), // colorEndA, colorEndB
43
+ 2, .2, .2, .2, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
44
+ .99, 1, 1, PI, // damping, angleDamping, gravityScale, cone
45
+ .05, .8, true, false // fadeRate, randomness, collide, additive
46
+ );
47
+ particleEmitter.elasticity = .5; // bounce when it collides
48
+ particleEmitter.trailScale = 2; // stretch in direction of motion
49
+ }
50
+ }
@@ -0,0 +1,242 @@
1
+ <!DOCTYPE html><head>
2
+ <title>LittleJS Short Examples</title>
3
+ <link rel=icon href=../examples/favicon.png>
4
+ <meta charset=utf-8>
5
+ <style>
6
+ html, body
7
+ {
8
+ height: 100%;
9
+ display: flex;
10
+ flex-direction: column;
11
+ }
12
+ #container1
13
+ {
14
+ height: 100%;
15
+ display: flex;
16
+ }
17
+ #container2
18
+ {
19
+ display: flex;
20
+ flex-direction: column;
21
+ }
22
+ #iframeContainer
23
+ {
24
+ border: 2px solid;
25
+ background: #000;
26
+ }
27
+ iframe
28
+ {
29
+ width: 100%;
30
+ height: 100%;
31
+ border: none;
32
+ }
33
+ #selectExample
34
+ {
35
+ flex-grow: 1;
36
+ }
37
+ #textareaCode
38
+ {
39
+ flex-grow: 1;
40
+ padding: 5px;
41
+ resize: none;
42
+ color: #fff;
43
+ background: #000;
44
+ }
45
+ #textareaError
46
+ {
47
+ flex-grow: 1;
48
+ padding: 5px;
49
+ resize: none;
50
+ display: none;
51
+ color:#f22;
52
+ background: #111;
53
+ }
54
+ #container3
55
+ {
56
+ width: 100%;
57
+ height: 100%;
58
+ display: flex;
59
+ flex-direction: column;
60
+ }
61
+ #titleInfo
62
+ {
63
+ margin: 5px;
64
+ text-align: center;
65
+ font-size: 50px;
66
+ }
67
+ #exampleName
68
+ {
69
+ margin: 0px;
70
+ text-align: center;
71
+ font-size: 30px;
72
+ }
73
+ select
74
+ {
75
+ color:#fff;
76
+ background-color: #111;
77
+ width:100%;
78
+ }
79
+ </style>
80
+ </head><body>
81
+ <div id=container1>
82
+ <div id=container3>
83
+ <p id=titleInfo>LittleJS Short Examples</p>
84
+ <p id=exampleName></p>
85
+ <textarea id=textareaCode oninput=codeInput() spellcheck=false></textarea>
86
+ <textarea id=textareaError readonly spellcheck=false></textarea>
87
+ <div><input type=checkbox id=checkboxLiveEdit checked>Live Edit</div>
88
+ </div>
89
+ <div id=container2>
90
+ <div id=iframeContainer></div>
91
+ <select id=selectExample autofocus onchange=setExample() size=2></select>
92
+ </div>
93
+ </div>
94
+ <script>
95
+ 'use strict';
96
+
97
+ class ExampleInfo
98
+ {
99
+ constructor(name, filename, description)
100
+ {
101
+ this.name = name;
102
+ this.filename = filename;
103
+ this.description = description;
104
+ }
105
+ }
106
+
107
+ const exampleList =
108
+ [
109
+ new ExampleInfo('Hello World', 'helloWorld.js', 'Simplest example'),
110
+ new ExampleInfo('Shapes', 'shapes.js', 'How to draw geometric shapes'),
111
+ new ExampleInfo('Blending', 'blending.js', 'Shows different blending modes'),
112
+ new ExampleInfo('Texture', 'texture.js', 'How to display a full texture'),
113
+ new ExampleInfo('Particles', 'particles.js', 'How to use the particle system'),
114
+ new ExampleInfo('Play Sound', 'playSound.js', 'How to play sounds with zzfx'),
115
+ new ExampleInfo('System Font', 'systemFont.js', 'Demo of the included system font'),
116
+ new ExampleInfo('Sprite Atlas', 'spriteAtlas.js', 'How to set up a simple sprite atlas'),
117
+ new ExampleInfo('Tile Layer', 'tileLayer.js', 'How to use a tile layer for collision and rendering'),
118
+ new ExampleInfo('Pong Game', 'pong.js', 'A simple pong game using LittleJS physics'),
119
+
120
+ // add more examples here!
121
+ ];
122
+
123
+ ///////////////////////////////////////////////////////////////////////////////
124
+
125
+ let iframeExample, inputTimeout;
126
+
127
+ // load the examples into the list
128
+ for (const example of exampleList)
129
+ {
130
+ const text = example.name + (example.description?' - ' + example.description : '');
131
+ selectExample.add(new Option(text));
132
+ }
133
+
134
+ // select first option
135
+ selectExample.selectedIndex = 0;
136
+ setExample();
137
+
138
+ onresize = () =>
139
+ {
140
+ // resize iframe to fit half the window
141
+ const aspect = 1920 / 1080;
142
+ let w = innerWidth / 2 | 0;
143
+ let h = w / aspect | 0;
144
+ iframeContainer.style.width = w + 'px';
145
+ iframeContainer.style.height = h + 'px';
146
+ }
147
+ onresize();
148
+
149
+ ///////////////////////////////////////////////////////////////////////////////
150
+
151
+ function setExample()
152
+ {
153
+ const example = exampleList[selectExample.selectedIndex];
154
+ exampleName.innerText = example.name + ' - ' + example.filename +
155
+ (example.description ? '\n' + example.description : '');
156
+ loadFile(example.filename);
157
+ }
158
+
159
+ function codeInput()
160
+ {
161
+ if (!checkboxLiveEdit.checked)
162
+ return;
163
+
164
+ // debounce input
165
+ clearTimeout(inputTimeout);
166
+ inputTimeout = setTimeout(() => setCode(textareaCode.value), 500);
167
+ }
168
+
169
+ function loadFile(filename)
170
+ {
171
+ fetch('code/' + filename)
172
+ .then(response => {
173
+ if (!response.ok)
174
+ throw new Error('Could not load file: '+filename);
175
+ return response.text();
176
+ })
177
+ .then(text => setCode(textareaCode.value = text))
178
+ .catch(error => setErrorMessage(error.message));
179
+ }
180
+
181
+ function setCode(code)
182
+ {
183
+ clearTimeout(inputTimeout);
184
+ if (iframeExample)
185
+ iframeContainer.removeChild(iframeExample);
186
+
187
+ setErrorMessage('');
188
+ iframeExample = document.createElement('iframe');
189
+ iframeContainer.appendChild(iframeExample);
190
+ iframeExample.onload = () =>
191
+ {
192
+ const iframeContent = iframeExample.contentWindow;
193
+ const iframeDocument = iframeContent.document;
194
+
195
+ // create error event listeners
196
+ iframeContent.onerror = (message, source, lineno, colno, error) =>
197
+ {
198
+ let text = message;
199
+ if (lineno)
200
+ text += ` (Line:${lineno}, Column:${colno})`
201
+ if (error && error.stack)
202
+ text += `\n\n` + error.stack;
203
+ setErrorMessage(text);
204
+ }
205
+ iframeContent.onunhandledrejection = (event) =>
206
+ {
207
+ let text = event.reason;
208
+ if (event.reason.stack)
209
+ text += `\n\n` + event.reason.stack;
210
+ setErrorMessage(text);
211
+ };
212
+ const originalAssert = iframeContent.console.assert;
213
+ iframeContent.console.assert = function (condition, output)
214
+ {
215
+ if (!condition)
216
+ setErrorMessage('Assertion failed: ' + (output || ''));
217
+ originalAssert.apply(this, arguments);
218
+ };
219
+
220
+ // create a script element that overrides the default functions
221
+ const overrideScript = iframeDocument.createElement('script');
222
+ iframeDocument.body.appendChild(overrideScript);
223
+ overrideScript.text = code;
224
+
225
+ if (textareaError.style.display == 'block')
226
+ return; // stop if script error
227
+
228
+ // start LittleJS engine
229
+ iframeContent.engineInit(iframeContent.gameInit, iframeContent.gameUpdate, iframeContent.gameUpdatePost, iframeContent.gameRender, iframeContent.gameRenderPost, ['tiles.png']);
230
+ }
231
+ iframeExample.src = 'base.html';
232
+ }
233
+
234
+ function setErrorMessage(message)
235
+ {
236
+ textareaError.value = message
237
+ textareaError.style.display = message ? 'block' : 'none';
238
+ }
239
+
240
+ </script>
241
+ </body>
242
+ </html>
Binary file
package/src/engine.js CHANGED
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
30
30
  * @type {String}
31
31
  * @default
32
32
  * @memberof Engine */
33
- const engineVersion = '1.11.2';
33
+ const engineVersion = '1.11.5';
34
34
 
35
35
  /** Frames per second to update
36
36
  * @type {Number}
@@ -113,8 +113,21 @@ function engineAddPlugin(updateFunction, renderFunction)
113
113
  * @memberof Engine */
114
114
  function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
115
115
  {
116
+ ASSERT(!mainContext, 'engine already initialized');
116
117
  ASSERT(Array.isArray(imageSources), 'pass in images as array');
117
118
 
119
+ // allow passing in empty functions
120
+ if (!gameInit)
121
+ gameInit = ()=>{};
122
+ if (!gameUpdate)
123
+ gameUpdate = ()=>{};
124
+ if (!gameUpdatePost)
125
+ gameUpdatePost = ()=>{};
126
+ if (!gameRender)
127
+ gameRender = ()=>{};
128
+ if (!gameRenderPost)
129
+ gameRenderPost = ()=>{};
130
+
118
131
  // Called automatically by engine to setup render system
119
132
  function enginePreRender()
120
133
  {
@@ -279,7 +292,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
279
292
  (canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
280
293
  'user-select:none;' + // prevent hold to select
281
294
  '-webkit-user-select:none;' + // compatibility for ios
282
- (!touchInputEnable ? '' : // no touch css setttings
295
+ (!touchInputEnable ? '' : // no touch css settings
283
296
  'touch-action:none;' + // prevent mobile pinch to resize
284
297
  '-webkit-touch-callout:none');// compatibility for ios
285
298
  rootElement.style.cssText = styleRoot;
@@ -308,6 +321,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
308
321
  new Promise(resolve =>
309
322
  {
310
323
  const image = new Image;
324
+ image.crossOrigin = 'anonymous';
311
325
  image.onerror = image.onload = ()=>
312
326
  {
313
327
  textureInfos[textureIndex] = new TextureInfo(image);
@@ -352,7 +366,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
352
366
  * @memberof Engine */
353
367
  function engineObjectsUpdate()
354
368
  {
355
- // get list of solid objects for physics optimzation
369
+ // get list of solid objects for physics optimization
356
370
  engineObjectsCollide = engineObjects.filter(o=>o.collideSolidObjects);
357
371
 
358
372
  // recursive object update
@@ -25,7 +25,7 @@ function audioInit()
25
25
  {
26
26
  if (!soundEnable || headlessMode) return;
27
27
 
28
- // (createGain is more widely spported then GainNode construtor)
28
+ // (createGain is more widely supported then GainNode constructor)
29
29
  audioGainNode = audioContext.createGain();
30
30
  audioGainNode.connect(audioContext.destination);
31
31
  audioGainNode.gain.value = soundVolume; // set starting value
@@ -375,7 +375,7 @@ const zzfxR = 44100;
375
375
  * @param {Number} [sustain] - Sustain time, how long sound holds (seconds)
376
376
  * @param {Number} [release] - Release time, how fast sound fades out (seconds)
377
377
  * @param {Number} [shape] - Shape of the sound wave
378
- * @param {Number} [shapeCurve] - Squarenes of wave (0=square, 1=normal, 2=pointy)
378
+ * @param {Number} [shapeCurve] - Squareness of wave (0=square, 1=normal, 2=pointy)
379
379
  * @param {Number} [slide] - How much to slide frequency (kHz/s)
380
380
  * @param {Number} [deltaSlide] - How much to change slide (kHz/s/s)
381
381
  * @param {Number} [pitchJump] - Frequency of pitch jump (Hz)
@@ -387,7 +387,7 @@ const zzfxR = 44100;
387
387
  * @param {Number} [delay] - Overlap sound with itself for reverb and flanger effects (seconds)
388
388
  * @param {Number} [sustainVolume] - Volume level for sustain (percent)
389
389
  * @param {Number} [decay] - Decay time, how long to reach sustain after attack (seconds)
390
- * @param {Number} [tremolo] - Trembling effect, rate controlled by repeat time (precent)
390
+ * @param {Number} [tremolo] - Trembling effect, rate controlled by repeat time (percent)
391
391
  * @param {Number} [filter] - Filter cutoff frequency, positive for HPF, negative for LPF (Hz)
392
392
  * @return {Array} - Array of audio samples
393
393
  * @memberof Audio
@@ -491,7 +491,7 @@ function zzfxG
491
491
  // ZzFX Music Renderer v2.0.3 by Keith Clark and Frank Force
492
492
 
493
493
  /** Generate samples for a ZzFM song with given parameters
494
- * @param {Array} instruments - Array of ZzFX sound paramaters
494
+ * @param {Array} instruments - Array of ZzFX sound parameters
495
495
  * @param {Array} patterns - Array of pattern data
496
496
  * @param {Array} sequence - Array of pattern indexes
497
497
  * @param {Number} [BPM] - Playback speed of the song in BPM
@@ -34,7 +34,8 @@ const asciiArt =`
34
34
  .|________|_._|______|_._|__|_|_|}
35
35
  OOO OOO OO OO OO=OO-oo\\
36
36
  `;
37
- const license = '// LittleJS - MIT License - Copyright 2021 Frank Force\n'
37
+ const license = '// LittleJS Engine - MIT License - Copyright 2021 Frank Force\n'+
38
+ '// https://github.com/KilledByAPixel/LittleJS\n';
38
39
 
39
40
  console.log(asciiArt);
40
41
  console.log('Choo Choo... Building LittleJS Engine!\n');
@@ -16,7 +16,7 @@
16
16
  * @memberof Debug */
17
17
  const debug = true;
18
18
 
19
- /** True if asserts are enaled
19
+ /** True if asserts are enabled
20
20
  * @type {Boolean}
21
21
  * @default
22
22
  * @memberof Debug */
@@ -200,6 +200,7 @@ function debugShowErrors()
200
200
  const showError = (message)=>
201
201
  {
202
202
  // replace entire page with error message
203
+ document.body.style.display = '';
203
204
  document.body.style.backgroundColor = '#111';
204
205
  document.body.innerHTML = `<pre style=color:#f00;font-size:50px>` + message;
205
206
  }
@@ -244,12 +245,8 @@ function debugRender()
244
245
 
245
246
  if (debugTakeScreenshot)
246
247
  {
247
- // composite canvas
248
- glCopyToContext(mainContext, true);
249
- mainContext.drawImage(overlayCanvas, 0, 0);
250
- overlayCanvas.width |= 0;
251
-
252
- // remove alpha and save
248
+ // combine canvases, remove alpha and save
249
+ combineCanvases();
253
250
  const w = mainCanvas.width, h = mainCanvas.height;
254
251
  overlayContext.fillRect(0,0,w,h);
255
252
  overlayContext.drawImage(mainCanvas, 0, 0);