littlejsengine 1.11.4 → 1.11.6

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 (50) hide show
  1. package/README.md +4 -0
  2. package/dist/littlejs.esm.js +31 -10
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +31 -10
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +30 -10
  7. package/examples/box2d/index.html +5 -5
  8. package/examples/breakout/game.js +1 -1
  9. package/examples/breakout/gameObjects.js +27 -28
  10. package/examples/breakout/index.html +4 -4
  11. package/examples/breakoutTutorial/README.md +1 -1
  12. package/examples/breakoutTutorial/game.js +1 -1
  13. package/examples/breakoutTutorial/index.html +2 -2
  14. package/examples/htmlMenu/index.html +2 -2
  15. package/examples/module/index.html +1 -1
  16. package/examples/particles/index.html +1 -1
  17. package/examples/platformer/index.html +8 -8
  18. package/examples/puzzle/game.js +1 -1
  19. package/examples/puzzle/index.html +2 -2
  20. package/examples/starter/game.js +2 -2
  21. package/examples/starter/index.html +13 -13
  22. package/examples/stress/index.html +1 -1
  23. package/examples/uiSystem/index.html +3 -3
  24. package/package.json +1 -1
  25. package/plugins/postProcess.js +8 -8
  26. package/shortExamples/base.html +39 -0
  27. package/shortExamples/code/animation.js +18 -0
  28. package/shortExamples/code/blending.js +16 -0
  29. package/shortExamples/code/clock.js +21 -0
  30. package/shortExamples/code/colors.js +25 -0
  31. package/shortExamples/code/helloWorld.js +8 -0
  32. package/shortExamples/code/particles.js +28 -0
  33. package/shortExamples/code/playSound.js +34 -0
  34. package/shortExamples/code/pong.js +40 -0
  35. package/shortExamples/code/shapes.js +24 -0
  36. package/shortExamples/code/spriteAtlas.js +34 -0
  37. package/shortExamples/code/systemFont.js +16 -0
  38. package/shortExamples/code/texture.js +12 -0
  39. package/shortExamples/code/tileLayer.js +50 -0
  40. package/shortExamples/code/timers.js +36 -0
  41. package/shortExamples/index.html +246 -0
  42. package/shortExamples/tiles.png +0 -0
  43. package/src/engine.js +13 -1
  44. package/src/engineBuild.js +2 -1
  45. package/src/engineDebug.js +1 -0
  46. package/src/engineDraw.js +4 -1
  47. package/src/engineObject.js +4 -4
  48. package/src/engineParticles.js +1 -1
  49. package/src/engineTileLayer.js +4 -0
  50. package/src/engineUtilities.js +2 -2
@@ -0,0 +1,246 @@
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: .3;
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('Colors', 'colors.js', 'How to use the color object'),
112
+ new ExampleInfo('Blending', 'blending.js', 'Shows different blending modes'),
113
+ new ExampleInfo('Timers', 'timers.js', 'How to use the timer object'),
114
+ new ExampleInfo('Texture', 'texture.js', 'How to display a full texture'),
115
+ new ExampleInfo('Particles', 'particles.js', 'How to use the particle system'),
116
+ new ExampleInfo('Play Sound', 'playSound.js', 'How to play sounds with zzfx'),
117
+ new ExampleInfo('System Font', 'systemFont.js', 'Demo of the included system font'),
118
+ new ExampleInfo('Sprite Atlas', 'spriteAtlas.js', 'How to set up a simple sprite atlas'),
119
+ new ExampleInfo('Animation', 'animation.js', 'How to animate sprites'),
120
+ new ExampleInfo('Clock', 'clock.js', 'A simple clock showing the time'),
121
+ new ExampleInfo('Tile Layer', 'tileLayer.js', 'How to use a tile layer for collision and rendering'),
122
+ new ExampleInfo('Pong Game', 'pong.js', 'A simple pong game using LittleJS physics'),
123
+
124
+ // add more examples here!
125
+ ];
126
+
127
+ ///////////////////////////////////////////////////////////////////////////////
128
+
129
+ let iframeExample, inputTimeout;
130
+
131
+ // load the examples into the list
132
+ for (const example of exampleList)
133
+ {
134
+ const text = example.name + (example.description?' - ' + example.description : '');
135
+ selectExample.add(new Option(text));
136
+ }
137
+
138
+ // select first option
139
+ selectExample.selectedIndex = 0;
140
+ setExample();
141
+
142
+ onresize = () =>
143
+ {
144
+ // resize iframe to fit half the window
145
+ const aspect = 1920 / 1080;
146
+ let w = innerWidth / 2 | 0;
147
+ let h = w / aspect | 0;
148
+ iframeContainer.style.width = w + 'px';
149
+ iframeContainer.style.height = h + 'px';
150
+ }
151
+ onresize();
152
+
153
+ ///////////////////////////////////////////////////////////////////////////////
154
+
155
+ function setExample()
156
+ {
157
+ const example = exampleList[selectExample.selectedIndex];
158
+ exampleName.innerText = example.name + ' - ' + example.filename +
159
+ (example.description ? '\n' + example.description : '');
160
+ loadFile(example.filename);
161
+ }
162
+
163
+ function codeInput()
164
+ {
165
+ if (!checkboxLiveEdit.checked)
166
+ return;
167
+
168
+ // debounce input
169
+ clearTimeout(inputTimeout);
170
+ inputTimeout = setTimeout(() => setCode(textareaCode.value), 500);
171
+ }
172
+
173
+ function loadFile(filename)
174
+ {
175
+ fetch('code/' + filename)
176
+ .then(response => {
177
+ if (!response.ok)
178
+ throw new Error('Could not load file: '+filename);
179
+ return response.text();
180
+ })
181
+ .then(text => setCode(textareaCode.value = text))
182
+ .catch(error => setErrorMessage(error.message));
183
+ }
184
+
185
+ function setCode(code)
186
+ {
187
+ clearTimeout(inputTimeout);
188
+ if (iframeExample)
189
+ iframeContainer.removeChild(iframeExample);
190
+
191
+ setErrorMessage('');
192
+ iframeExample = document.createElement('iframe');
193
+ iframeContainer.appendChild(iframeExample);
194
+ iframeExample.onload = () =>
195
+ {
196
+ const iframeContent = iframeExample.contentWindow;
197
+ const iframeDocument = iframeContent.document;
198
+
199
+ // create error event listeners
200
+ iframeContent.onerror = (message, source, lineno, colno, error) =>
201
+ {
202
+ let text = message;
203
+ if (lineno)
204
+ text += ` (Line:${lineno}, Column:${colno})`
205
+ if (error && error.stack)
206
+ text += `\n\n` + error.stack;
207
+ setErrorMessage(text);
208
+ }
209
+ iframeContent.onunhandledrejection = (event) =>
210
+ {
211
+ let text = event.reason;
212
+ if (event.reason.stack)
213
+ text += `\n\n` + event.reason.stack;
214
+ setErrorMessage(text);
215
+ };
216
+ const originalAssert = iframeContent.console.assert;
217
+ iframeContent.console.assert = function (condition, output)
218
+ {
219
+ if (!condition)
220
+ setErrorMessage('Assertion failed: ' + (output || ''));
221
+ originalAssert.apply(this, arguments);
222
+ };
223
+
224
+ // create a script element that overrides the default functions
225
+ const overrideScript = iframeDocument.createElement('script');
226
+ iframeDocument.body.appendChild(overrideScript);
227
+ overrideScript.text = code;
228
+
229
+ if (textareaError.style.display == 'block')
230
+ return; // stop if script error
231
+
232
+ // start LittleJS engine
233
+ iframeContent.engineInit(iframeContent.gameInit, iframeContent.gameUpdate, iframeContent.gameUpdatePost, iframeContent.gameRender, iframeContent.gameRenderPost, ['tiles.png?1']);
234
+ }
235
+ iframeExample.src = 'base.html';
236
+ }
237
+
238
+ function setErrorMessage(message)
239
+ {
240
+ textareaError.value = message
241
+ textareaError.style.display = message ? 'block' : 'none';
242
+ }
243
+
244
+ </script>
245
+ </body>
246
+ </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.4';
33
+ const engineVersion = '1.11.6';
34
34
 
35
35
  /** Frames per second to update
36
36
  * @type {Number}
@@ -116,6 +116,18 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
116
116
  ASSERT(!mainContext, 'engine already initialized');
117
117
  ASSERT(Array.isArray(imageSources), 'pass in images as array');
118
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
+
119
131
  // Called automatically by engine to setup render system
120
132
  function enginePreRender()
121
133
  {
@@ -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');
@@ -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
  }
package/src/engineDraw.js CHANGED
@@ -458,7 +458,10 @@ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineCol
458
458
  context.lineJoin = 'round';
459
459
 
460
460
  pos = pos.copy();
461
- (text+'').split('\n').forEach(line=>
461
+
462
+ const lines = (text+'').split('\n');
463
+ pos.y -= (lines.length-1) * size/2; // center text vertically
464
+ lines.forEach(line=>
462
465
  {
463
466
  lineWidth && context.strokeText(line, pos.x, pos.y, maxWidth);
464
467
  context.fillText(line, pos.x, pos.y, maxWidth);
@@ -338,19 +338,19 @@ class EngineObject
338
338
 
339
339
  /** Convert from local space to world space
340
340
  * @param {Vector2} pos - local space point */
341
- localToWorld(pos) { return this.pos.add(pos.rotate(-this.angle)); }
341
+ localToWorld(pos) { return this.pos.add(pos.rotate(this.angle)); }
342
342
 
343
343
  /** Convert from world space to local space
344
344
  * @param {Vector2} pos - world space point */
345
- worldToLocal(pos) { return pos.subtract(this.pos).rotate(this.angle); }
345
+ worldToLocal(pos) { return pos.subtract(this.pos).rotate(-this.angle); }
346
346
 
347
347
  /** Convert from local space to world space for a vector (rotation only)
348
348
  * @param {Vector2} vec - local space vector */
349
- localToWorldVector(vec) { return vec.rotate(this.angle); }
349
+ localToWorldVector(vec) { return vec.rotate(-this.angle); }
350
350
 
351
351
  /** Convert from world space to local space for a vector (rotation only)
352
352
  * @param {Vector2} vec - world space vector */
353
- worldToLocalVector(vec) { return vec.rotate(-this.angle); }
353
+ worldToLocalVector(vec) { return vec.rotate(this.angle); }
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
@@ -283,7 +283,7 @@ class Particle extends EngineObject
283
283
  render()
284
284
  {
285
285
  // modulate size and color
286
- const p = min((time - this.spawnTime) / this.lifeTime, 1);
286
+ const p = this.lifeTime > 0 ? min((time - this.spawnTime) / this.lifeTime, 1) : 1;
287
287
  const radius = this.sizeStart + p * this.sizeEndDelta;
288
288
  const size = vec2(radius);
289
289
  const fadeRate = this.fadeRate/2;
@@ -239,6 +239,10 @@ class TileLayer extends EngineObject
239
239
 
240
240
  // draw the entire cached level onto the canvas
241
241
  const pos = worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));
242
+
243
+ // fix canvas jitter in some browsers if position is not an integer
244
+ pos.x |= 0; pos.y |= 0;
245
+
242
246
  (this.isOverlay ? overlayContext : mainContext).drawImage
243
247
  (
244
248
  this.canvas, pos.x, pos.y,
@@ -482,7 +482,7 @@ class Vector2
482
482
  rotate(angle)
483
483
  {
484
484
  const c = Math.cos(angle), s = Math.sin(angle);
485
- return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
485
+ return new Vector2(this.x*c + this.y*s, this.x*s + this.y*c);
486
486
  }
487
487
 
488
488
  /** Set the integer direction of this vector, corresponding to multiples of 90 degree rotation (0-3)
@@ -914,7 +914,7 @@ class Timer
914
914
 
915
915
  /** Get percentage elapsed based on time it was set to, returns 0 if not set
916
916
  * @return {Number} */
917
- getPercent() { return this.isSet()? percent(this.time - time, this.setTime, 0) : 0; }
917
+ getPercent() { return this.isSet()? 1-percent(this.time - time, 0, this.setTime) : 0; }
918
918
 
919
919
  /** Returns this timer expressed as a string
920
920
  * @return {String} */