littlejsengine 1.14.10 → 1.14.11

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.
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
33
33
  * @type {string}
34
34
  * @default
35
35
  * @memberof Engine */
36
- const engineVersion = '1.14.10';
36
+ const engineVersion = '1.14.11';
37
37
 
38
38
  /** Frames per second to update
39
39
  * @type {number}
@@ -122,12 +122,12 @@ function engineAddPlugin(updateFunction, renderFunction)
122
122
  * @example
123
123
  * // Basic engine startup
124
124
  * engineInit(
125
- * () => { console.log('Game initialized!'); }, // gameInit
126
- * () => { updateGameLogic(); }, // gameUpdate
127
- * () => { updateUI(); }, // gameUpdatePost
128
- * () => { drawBackground(); }, // gameRender
129
- * () => { drawHUD(); }, // gameRenderPost
130
- * ['tiles.png', 'tilesLevel.png'] // images to load
125
+ * () => { LOG('Game initialized!'); }, // gameInit
126
+ * () => { updateGameLogic(); }, // gameUpdate
127
+ * () => { updateUI(); }, // gameUpdatePost
128
+ * () => { drawBackground(); }, // gameRender
129
+ * () => { drawHUD(); }, // gameRenderPost
130
+ * ['tiles.png', 'tilesLevel.png'] // images to load
131
131
  * );
132
132
  * @memberof Engine */
133
133
  async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
@@ -173,16 +173,20 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
173
173
  if (!debugSpeedUp)
174
174
  frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp min framerate
175
175
 
176
+ let wasUpdated = false;
176
177
  if (paused)
177
178
  {
179
+ // update everything except the game and objects
180
+ wasUpdated = true;
178
181
  updateCanvas();
182
+ inputUpdate();
183
+ pluginUpdateList.forEach(f=>f());
179
184
 
180
185
  // update object transforms even when paused
181
186
  for (const o of engineObjects)
182
187
  o.parent || o.updateTransforms();
183
188
 
184
- inputUpdate();
185
- pluginUpdateList.forEach(f=>f());
189
+ // do post update
186
190
  debugUpdate();
187
191
  gameUpdatePost();
188
192
  inputUpdatePost();
@@ -199,12 +203,13 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
199
203
  }
200
204
 
201
205
  // update multiple frames if necessary in case of slow framerate
202
- for (;frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 / frameRate)
206
+ for (; frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 / frameRate)
203
207
  {
204
208
  // increment frame and update time
205
209
  time = frame++ / frameRate;
206
210
 
207
211
  // update game and objects
212
+ wasUpdated = true;
208
213
  updateCanvas();
209
214
  inputUpdate();
210
215
  gameUpdate();
@@ -215,7 +220,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
215
220
  debugUpdate();
216
221
  gameUpdatePost();
217
222
  inputUpdatePost();
218
-
219
223
  if (debugVideoCaptureIsActive())
220
224
  renderFrame();
221
225
  }
@@ -232,6 +236,10 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
232
236
  {
233
237
  if (headlessMode) return;
234
238
 
239
+ // canvas must be updated before rendering
240
+ if (!wasUpdated)
241
+ updateCanvas();
242
+
235
243
  // render sort then render while removing destroyed objects
236
244
  enginePreRender();
237
245
  gameRender();
@@ -391,7 +399,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
391
399
  promises.push(new Promise(resolve =>
392
400
  {
393
401
  let t = 0;
394
- console.log(`${engineName} Engine v${engineVersion}`);
402
+ LOG(`${engineName} Engine v${engineVersion}`);
395
403
  updateSplash();
396
404
  function updateSplash()
397
405
  {
@@ -690,6 +698,7 @@ const debugMedals = 0;
690
698
 
691
699
  // debug commands are automatically removed from the final build
692
700
  function ASSERT (){}
701
+ function LOG (){}
693
702
  function debugInit (){}
694
703
  function debugUpdate (){}
695
704
  function debugRender (){}
@@ -927,7 +936,12 @@ function wave(frequency=1, amplitude=1, t=time, offset=0)
927
936
  * @param {number} t - time in seconds
928
937
  * @return {string}
929
938
  * @memberof Utilities */
930
- function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
939
+ function formatTime(t)
940
+ {
941
+ const sign = t < 0 ? '-' : '';
942
+ t = abs(t)|0;
943
+ return sign + (t/60|0) + ':' + (t%60<10?'0':'') + t%60;
944
+ }
931
945
 
932
946
  /** Fetches a JSON file from a URL and returns the parsed JSON object. Must be used with await!
933
947
  * @param {string} url - URL of JSON file
@@ -1263,11 +1277,7 @@ class Vector2
1263
1277
  direction()
1264
1278
  { return abs(this.x) > abs(this.y) ? this.x < 0 ? 3 : 1 : this.y < 0 ? 2 : 0; }
1265
1279
 
1266
- /** Returns a copy of this vector that has been inverted
1267
- * @return {Vector2} */
1268
- invert() { return new Vector2(this.y, -this.x); }
1269
-
1270
- /** Returns a copy of this vector absolute values
1280
+ /** Returns a copy of this vector with absolute values
1271
1281
  * @return {Vector2} */
1272
1282
  abs() { return new Vector2(abs(this.x), abs(this.y)); }
1273
1283
 
@@ -1309,13 +1319,10 @@ class Vector2
1309
1319
  toString(digits=3)
1310
1320
  {
1311
1321
  ASSERT_NUMBER_VALID(digits);
1312
- if (debug)
1313
- {
1314
- if (this.isValid())
1315
- return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`;
1316
- else
1317
- return `(${this.x}, ${this.y})`;
1318
- }
1322
+ if (this.isValid())
1323
+ return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`;
1324
+ else
1325
+ return `(${this.x}, ${this.y})`;
1319
1326
  }
1320
1327
 
1321
1328
  /** Checks if this is a valid vector
@@ -1704,7 +1711,7 @@ class Timer
1704
1711
 
1705
1712
  /** Returns this timer expressed as a string
1706
1713
  * @return {string} */
1707
- toString() { if (debug) { return this.isSet() ? Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }}
1714
+ toString() { return this.isSet() ? Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }
1708
1715
 
1709
1716
  /** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
1710
1717
  * @return {number} */
@@ -5071,6 +5078,12 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
5071
5078
  const tileLayer = new TileCollisionLayer(vec2(), levelSize, tileInfo, layerRenderOrder);
5072
5079
  tileLayers[layerIndex] = tileLayer;
5073
5080
 
5081
+ // apply layer color
5082
+ const layerColor = dataLayer.color || WHITE;
5083
+ if (dataLayer.tintcolor)
5084
+ layerColor.setHex(dataLayer.tintcolor);
5085
+ ASSERT(isColor(layerColor), 'layer color is not a color');
5086
+
5074
5087
  for (let x=levelSize.x; x--;)
5075
5088
  for (let y=levelSize.y; y--;)
5076
5089
  {
@@ -5078,7 +5091,7 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
5078
5091
  const data = dataLayer.data[x + y*levelSize.x];
5079
5092
  if (data)
5080
5093
  {
5081
- const layerData = new TileLayerData(data-1);
5094
+ const layerData = new TileLayerData(data-1, 0, false, layerColor);
5082
5095
  tileLayer.setData(pos, layerData);
5083
5096
 
5084
5097
  // set collision for top layer
@@ -6972,7 +6985,7 @@ class NewgroundsPlugin
6972
6985
  // get medals
6973
6986
  const medalsResult = this.call('Medal.getList');
6974
6987
  this.medals = medalsResult ? medalsResult.result.data['medals'] : [];
6975
- debugMedals && console.log(this.medals);
6988
+ debugMedals && LOG(this.medals);
6976
6989
  for (const newgroundsMedal of this.medals)
6977
6990
  {
6978
6991
  const medal = medals[newgroundsMedal['id']];
@@ -6995,7 +7008,7 @@ class NewgroundsPlugin
6995
7008
  // get scoreboards
6996
7009
  const scoreboardResult = this.call('ScoreBoard.getBoards');
6997
7010
  this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
6998
- debugMedals && console.log(this.scoreboards);
7011
+ debugMedals && LOG(this.scoreboards);
6999
7012
 
7000
7013
  // keep the session alive with a ping every minute
7001
7014
  const keepAliveMS = 60 * 1e3;
@@ -7064,10 +7077,10 @@ class NewgroundsPlugin
7064
7077
  try { xmlHttp.send(formData); }
7065
7078
  catch(e)
7066
7079
  {
7067
- debugMedals && console.log('newgrounds call failed', e);
7080
+ debugMedals && LOG('newgrounds call failed', e);
7068
7081
  return;
7069
7082
  }
7070
- debugMedals && console.log(xmlHttp.responseText);
7083
+ debugMedals && LOG(xmlHttp.responseText);
7071
7084
  return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
7072
7085
  }
7073
7086
  }
@@ -9664,7 +9677,7 @@ async function box2dInit()
9664
9677
  }
9665
9678
  function box2dRender()
9666
9679
  {
9667
- if (box2dDebug || debugPhysics && debugOverlay)
9680
+ if (box2dDebug || debugPhysics)
9668
9681
  box2d.world.DrawDebugData();
9669
9682
  }
9670
9683
 
@@ -53,7 +53,7 @@ Theme:
53
53
 
54
54
  class ExampleInfo
55
55
  {
56
- constructor(name, filename, description='', largeExample=false)
56
+ constructor(name, filename, description='', largeExample=false, tags='')
57
57
  {
58
58
  if (filename && !largeExample)
59
59
  filename = 'shorts/' + filename;
@@ -61,62 +61,66 @@ class ExampleInfo
61
61
  this.filename = filename;
62
62
  this.description = description;
63
63
  this.largeExample = largeExample;
64
+ this.tags = tags;
64
65
  this.isHeading = !filename;
65
66
  this.text = this.name;
66
67
  if (this.description)
67
68
  this.text += ' - ' + this.description;
69
+ this.selectText = this.text;
70
+ if (this.tags)
71
+ this.selectText += ' (' + this.tags + ')';
68
72
  }
69
73
  }
70
74
 
71
75
  const exampleList =
72
76
  [
73
77
  new ExampleInfo('--- SHORT EXAMPLES ---'),
74
- new ExampleInfo('Hello World', 'helloWorld.js', 'Minimal starter example'),
75
- new ExampleInfo('Texture', 'texture.js', 'Texture display and manipulation'),
76
- new ExampleInfo('Animation', 'animation.js', 'Sprite animation system'),
77
- new ExampleInfo('Shapes', 'shapes.js', 'Draw geometric shapes and primitives'),
78
- new ExampleInfo('Colors', 'colors.js', 'Color manipulation and HSL'),
79
- new ExampleInfo('Starfield', 'starfield.js', 'Animated parallax starfield'),
80
- new ExampleInfo('Sprite Atlas', 'spriteAtlas.js', 'Sprite atlas and tile rendering'),
81
- new ExampleInfo('Blending', 'blending.js', 'Additive blending and transparency'),
82
- new ExampleInfo('Tile Layer', 'tileLayer.js', 'Tile layer rendering system'),
83
- new ExampleInfo('Particles', 'particles.js', 'Particle system'),
84
- new ExampleInfo('Timers', 'timers.js', 'Timer objects and UI'),
85
- new ExampleInfo('Sound Effects', 'sound.js', 'ZzFX sound effect generator'),
86
- new ExampleInfo('Music', 'music.js', 'Music playback and controls'),
87
- new ExampleInfo('Font Image', 'systemFont.js', 'Bitmap font system with built-in system font'),
88
- new ExampleInfo('Parallax', 'parallax.js', 'Parallax scrolling mountains'),
89
- new ExampleInfo('Maze Generator', 'maze.js', 'Procedural maze generation'),
90
- new ExampleInfo('Medals', 'medals.js', 'Achievement system'),
91
- new ExampleInfo('Piano', 'piano.js', 'Interactive piano keyboard'),
92
- new ExampleInfo('Clock', 'clock.js', 'Animated analog clock'),
78
+ new ExampleInfo('Hello World', 'helloWorld.js', 'Minimal starter example', false, 'beginner, gradient, text, tiles'),
79
+ new ExampleInfo('Texture', 'texture.js', 'Texture display and manipulation', false, 'sprites, loading, tiles'),
80
+ new ExampleInfo('Animation', 'animation.js', 'Sprite animation system', false, 'frames, loop, tiles, sprites'),
81
+ new ExampleInfo('Shapes', 'shapes.js', 'Draw geometric shapes and primitives', false, 'circle, ellipses, rectangles, polygon, lines'),
82
+ new ExampleInfo('Colors', 'colors.js', 'Color manipulation and HSL', false, 'hue, saturation, blending, rectangles'),
83
+ new ExampleInfo('Starfield', 'starfield.js', 'Animated parallax starfield', false, 'space, movement, depth, rectangles'),
84
+ new ExampleInfo('Sprite Atlas', 'spriteAtlas.js', 'Sprite atlas and tile rendering', false, 'sheet, frames, tiles'),
85
+ new ExampleInfo('Blending', 'blending.js', 'Additive blending and transparency', false, 'alpha, color, tiles, smooth'),
86
+ new ExampleInfo('Tile Layer', 'tileLayer.js', 'Tile layer rendering system', false, 'level, map, grid, particles'),
87
+ new ExampleInfo('Particles', 'particles.js', 'Particle system', false, 'effects, emitter, physics, fire, smoke'),
88
+ new ExampleInfo('Timers', 'timers.js', 'Timer objects and UI', false, 'delay, interval, slider'),
89
+ new ExampleInfo('Sound Effects', 'sound.js', 'ZzFX sound effect generator', false, 'audio, volume, ui'),
90
+ new ExampleInfo('Music', 'music.js', 'Music playback and controls', false, 'audio, streaming, volume, ui'),
91
+ new ExampleInfo('Font Image', 'systemFont.js', 'Bitmap font system with built-in system font', false, 'text, characters'),
92
+ new ExampleInfo('Parallax', 'parallax.js', 'Parallax scrolling mountains', false, 'generative, canvas, background'),
93
+ new ExampleInfo('Maze Generator', 'maze.js', 'Procedural maze generation', false, 'generative, level, tiles, map, grid'),
94
+ new ExampleInfo('Medals', 'medals.js', 'Achievement system', false, 'unlock, progress, newgrounds'),
95
+ new ExampleInfo('Piano', 'piano.js', 'Interactive piano keyboard', false, 'music, notes, ui, instrument'),
96
+ new ExampleInfo('Clock', 'clock.js', 'Animated analog clock', false, 'time, rotation, lines, rectangles'),
93
97
  new ExampleInfo('--- MINI GAMES ---'),
94
- new ExampleInfo('Pong Game', 'pongGame.js', 'Classic paddle ball bouncing'),
95
- new ExampleInfo('Platformer Game', 'platformer.js', 'Jump and run side view'),
96
- new ExampleInfo('Top Down Game', 'topDown.js', 'Top-down style camera'),
97
- new ExampleInfo('Tilted View Game', 'tiltedView.js', 'Pseudo-3D oblique view'),
98
- new ExampleInfo('Flappy Game', 'flappyGame.js', 'Flappy bird style game'),
99
- new ExampleInfo('Lander Game', 'landerGame.js', 'Lunar lander style game'),
100
- new ExampleInfo('Hill Glide Game', 'hillGlideGame.js', 'Tiny wings style sliding game'),
101
- new ExampleInfo('Sliding Puzzle', 'slidingPuzzle.js', '15 tile sliding puzzle'),
102
- new ExampleInfo('Space Game', 'spaceGame.js', 'Spaceship shooter with parallax'),
103
- new ExampleInfo('Raycasting 3D Game', 'raycasting.js', 'Wolfenstein-style raycasting'),
98
+ new ExampleInfo('Pong Game', 'pongGame.js', 'Classic paddle ball bouncing', false, 'objects, collision'),
99
+ new ExampleInfo('Platformer Game', 'platformer.js', 'Jump and run side view', false, 'objects, gravity, level, tiles, camera'),
100
+ new ExampleInfo('Top Down Game', 'topDown.js', 'Top-down style camera', false, 'objects, movement, exploration'),
101
+ new ExampleInfo('Tilted View Game', 'tiltedView.js', 'Pseudo-3D oblique view', false, 'isometric, depth'),
102
+ new ExampleInfo('Flappy Game', 'flappyGame.js', 'Flappy bird style game', false, 'objects, obstacles'),
103
+ new ExampleInfo('Lander Game', 'landerGame.js', 'Lunar lander style game', false, 'objects, physics'),
104
+ new ExampleInfo('Hill Glide Game', 'hillGlideGame.js', 'Tiny wings style sliding game', false, 'objects, physics, speed'),
105
+ new ExampleInfo('Sliding Puzzle', 'slidingPuzzle.js', '15 tile sliding puzzle', false, 'objects, numbers, ui'),
106
+ new ExampleInfo('Space Game', 'spaceGame.js', 'Spaceship shooter with parallax', false, 'objects, weapons, stars, camera, rotation'),
107
+ new ExampleInfo('Raycasting 3D Game', 'raycasting.js', 'Pseudo 3D raycasting demo', false, '3D, maze, camera'),
104
108
  new ExampleInfo('--- PLUGINS ---'),
105
- new ExampleInfo('Box2d Demo', 'box2d.js', 'Box2D physics plugin'),
106
- new ExampleInfo('Box2d Car', 'box2dCar.js', 'Drivable car with Box2D physics'),
107
- new ExampleInfo('Post Processing', 'postProcess.js', 'Shader effects and filters'),
108
- new ExampleInfo('UI System Plugin', 'uiSystem.js', 'Buttons, sliders, and checkboxes'),
109
- new ExampleInfo('Nine Slice', 'nineSlice.js', 'Scalable UI panels'),
109
+ new ExampleInfo('Box2d Demo', 'box2d.js', 'Box2D physics plugin', false, 'objects, mouse'),
110
+ new ExampleInfo('Box2d Car', 'box2dCar.js', 'Drivable car with Box2D physics', false, 'objects, vehicle, suspension, wheels'),
111
+ new ExampleInfo('Post Processing', 'postProcess.js', 'Shader effects and filters', false, 'webgl, visual, effect'),
112
+ new ExampleInfo('UI System Plugin', 'uiSystem.js', 'Buttons, sliders, and checkboxes', false, 'objects, widgets, interactive'),
113
+ new ExampleInfo('Nine Slice', 'nineSlice.js', 'Scalable UI panels', false, 'three slice, stretch, corners, text, tiles'),
110
114
  new ExampleInfo('--- FULL EXAMPLES ---'),
111
- new ExampleInfo('Starter', 'starter', 'Clean project template', true),
112
- new ExampleInfo('Breakout Tutorial', 'breakoutTutorial', 'Step-by-step breakout tutorial', true),
113
- new ExampleInfo('Breakout Game', 'breakout', 'Complete breakout game', true),
114
- new ExampleInfo('Platforming Game', 'platformer', 'Full platformer with levels', true),
115
- new ExampleInfo('Puzzle Game', 'puzzle', 'Match-3 puzzle game', true),
116
- new ExampleInfo('Stress Test', 'stress', 'Performance and music test', true),
117
- new ExampleInfo('Box2D Plugin', 'box2d', 'Full Box2D physics demo', true),
118
- new ExampleInfo('HTML Menus', 'htmlMenu', 'HTML UI integration', true),
119
- new ExampleInfo('UI System Plugin', 'uiSystem', 'Complete UI system demo', true),
115
+ new ExampleInfo('Starter', 'starter', 'Clean project template', true, 'base, empty, particles'),
116
+ new ExampleInfo('Breakout Tutorial', 'breakoutTutorial', 'Step-by-step breakout game tutorial', true, 'objects, physics, score'),
117
+ new ExampleInfo('Breakout Game', 'breakout', 'Complete breakout game', true, 'objects, physics, score'),
118
+ new ExampleInfo('Platforming Game', 'platformer', 'Platformer with level loading', true, 'jump, world, tiles, pixel art, sprites'),
119
+ new ExampleInfo('Puzzle Game', 'puzzle', 'Match 3 style puzzle game', true, 'match, swap, sprites'),
120
+ new ExampleInfo('Stress Test', 'stress', 'Performance and music test', true, 'optimization, tiles, sprites'),
121
+ new ExampleInfo('Box2D Plugin', 'box2d', 'Full Box2D physics demo', true, 'objects, bodies, joints'),
122
+ new ExampleInfo('HTML Menus', 'htmlMenu', 'HTML UI integration', true, 'web, browser, overlay, button, slider, textbox'),
123
+ new ExampleInfo('UI System Plugin', 'uiSystem', 'Complete UI system demo', true, 'menu, overlay, button, slider, checkbox'),
120
124
  ];
121
125
 
122
126
  ///////////////////////////////////////////////////////////////////////////////
@@ -313,9 +317,10 @@ function filterExamples(reset=0)
313
317
  if (!example.filename)
314
318
  continue;
315
319
 
316
- // Check if search term matches name or description
320
+ // Check if search term matches name, description, or tags
317
321
  if (example.name.toLowerCase().includes(searchTerm) ||
318
- example.description.toLowerCase().includes(searchTerm))
322
+ example.description.toLowerCase().includes(searchTerm) ||
323
+ example.tags.toLowerCase().includes(searchTerm))
319
324
  matchingIndices.push(i);
320
325
  }
321
326
 
@@ -345,7 +350,7 @@ function filterExamples(reset=0)
345
350
  else if (matchingIndices.includes(i))
346
351
  {
347
352
  // add matching example
348
- const o = new Option(example.text);
353
+ const o = new Option(example.selectText);
349
354
  o.originalIndex = i;
350
355
  selectExample.add(o);
351
356
  }
@@ -22,10 +22,9 @@ const {vec2, hsl, Timer} = LJS;
22
22
  export class Character extends GameObjects.GameObject
23
23
  {
24
24
  constructor(pos)
25
- {
25
+ {
26
26
  super(pos, vec2(.6,.95));
27
27
 
28
- this.weapon = new GameObjects.Weapon(pos, this);
29
28
  this.lastPos = pos;
30
29
  this.groundTimer = new Timer;
31
30
  this.jumpTimer = new Timer;
@@ -41,6 +40,11 @@ export class Character extends GameObjects.GameObject
41
40
  this.health = 1;
42
41
  this.climbingLadder = false;
43
42
  this.setCollision(true,false);
43
+
44
+ // attach weapon
45
+ this.weapon = new GameObjects.Weapon(pos);
46
+ this.weapon.renderOrder = this.renderOrder + 1;
47
+ this.addChild(this.weapon, vec2(this.size.x,0));
44
48
  }
45
49
 
46
50
  update()
@@ -50,7 +50,7 @@ function loadLevelData()
50
50
  {
51
51
  // load the level data
52
52
  const tileMapData = Game.gameLevelData;
53
- tileLayers = LJS.tileCollisionLoad(tileMapData, tile(0,16,1));
53
+ tileLayers = LJS.tileCollisionLoad(tileMapData, tile(0,16,1), 0, 1);
54
54
  levelSize = tileLayers[0].size;
55
55
  playerStartPos = vec2(0, levelSize.y); // default player start
56
56
  foregroundTileLayer = tileLayers[0];
@@ -101,10 +101,14 @@ export class Coin extends LJS.EngineObject
101
101
 
102
102
  render()
103
103
  {
104
+ // side of coin
105
+ LJS.drawTile(this.pos, vec2(.1, .6), 0, this.color);
106
+
104
107
  // make it appear to spin
105
108
  const t = LJS.time+this.pos.x/4+this.pos.y/4;
106
- LJS.drawTile(this.pos, vec2(.1, .6), 0, this.color); // edge of coin
107
- LJS.drawTile(this.pos, vec2(.5+.5*Math.sin(t*2*LJS.PI), 1), this.tileInfo, this.color);
109
+ const spinSize = vec2(.5+.5*Math.sin(t*2*LJS.PI), 1);
110
+ if (spinSize.x > .1)
111
+ LJS.drawTile(this.pos, spinSize, this.tileInfo, this.color);
108
112
  }
109
113
 
110
114
  update()
@@ -227,7 +231,7 @@ export class Grenade extends GameObject
227
231
 
228
232
  export class Weapon extends LJS.EngineObject
229
233
  {
230
- constructor(pos, parent)
234
+ constructor(pos)
231
235
  {
232
236
  super(pos, vec2(.6), Game.spriteAtlas.gun);
233
237
 
@@ -238,10 +242,8 @@ export class Weapon extends LJS.EngineObject
238
242
  this.damage = 1;
239
243
 
240
244
  // prepare to fire
241
- this.renderOrder = parent.renderOrder + 1;
242
245
  this.fireTimeBuffer = this.localAngle = 0;
243
246
  this.recoilTimer = new Timer;
244
- parent.addChild(this, vec2(.6,0));
245
247
 
246
248
  // shell effect
247
249
  this.addChild(this.shellEmitter = new LJS.ParticleEmitter(
@@ -4,14 +4,38 @@ class Player extends EngineObject
4
4
  {
5
5
  super(pos, vec2(1,2), tile(8), 0, RED);
6
6
  this.setCollision(); // make object collide
7
+ this.angleDamping = .95;
7
8
  }
8
9
 
9
10
  update()
10
11
  {
11
12
  super.update();
13
+
14
+ // space ship controls
15
+ const moveInput = keyDirection();
16
+ this.applyAngularAcceleration(moveInput.x * .002);
17
+ this.applyAcceleration(vec2().setAngle(this.angle, moveInput.y*.002));
18
+ }
19
+
20
+ // explode when it collides
21
+ collideWithObject()
22
+ {
23
+ // don't explode if player is almost still and not tilted
24
+ if (abs(this.angle) < .2)
25
+ if (this.velocity.length() < .05)
26
+ return true;
12
27
 
13
- // apply movement controls
14
- this.applyAcceleration(keyDirection().scale(.002));
28
+ this.destroy();
29
+ new ParticleEmitter(
30
+ this.pos, 0, // pos, angle
31
+ 1, .1, 300, 3.14, // emitSize, emitTime, emitRate, emitCone
32
+ 0, // tileInfo
33
+ rgb(1,.5,.1), rgb(1,.1,.1), // colorStartA, colorStartB
34
+ rgb(1,.5,.1,0), rgb(1,.1,.1,0), // colorEndA, colorEndB
35
+ .7, .8, .2, .2, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
36
+ .9, 1, -.2, 3.14, .05, // damp, angleDamp, gravity, particleCone, fade
37
+ 1, 0, 1 // randomness, collide, additive
38
+ );
15
39
  }
16
40
  }
17
41
 
@@ -48,7 +48,6 @@ function gameInit()
48
48
 
49
49
  // stop button
50
50
  stopButton = new UIButton(vec2(90, 30), vec2(140, 50), 'Stop');
51
- musicPlayer.addChild(stopButton);
52
51
  stopButton.onClick = ()=>
53
52
  {
54
53
  if (!musicInstance)
@@ -57,10 +56,11 @@ function gameInit()
57
56
  // stop sound
58
57
  musicInstance.stop();
59
58
  };
59
+ musicPlayer.addChild(stopButton);
60
60
 
61
61
  // progress bar and scrollbar for seeking
62
62
  progressBar = new UIScrollbar(vec2(0, 100), vec2(400, 30), 0, 'Progress');
63
- musicPlayer.addChild(progressBar);
63
+ progressBar.disabledColor = RED;
64
64
  progressBar.onChange = ()=>
65
65
  {
66
66
  // control music seek position
@@ -73,6 +73,7 @@ function gameInit()
73
73
  if (!wasPlaying)
74
74
  musicInstance.pause();
75
75
  };
76
+ musicPlayer.addChild(progressBar);
76
77
  }
77
78
 
78
79
  function gameUpdate()
@@ -4,7 +4,7 @@ function gameInit()
4
4
 
5
5
  // fire
6
6
  new ParticleEmitter(
7
- vec2(-5,-2), 0, // pos, angle
7
+ vec2(-5,-2), 0, // pos, angle
8
8
  2, 0, 200, PI, // emitSize, emitTime, emitRate, emitCone
9
9
  tile(0), // tileInfo
10
10
  rgb(1,.5,.1), rgb(1,.1,.1), // colorStartA, colorStartB
@@ -16,7 +16,7 @@ function gameInit()
16
16
 
17
17
  // smoke
18
18
  new ParticleEmitter(
19
- vec2(-5,-2), 0, // pos, angle
19
+ vec2(5,-2), 0, // pos, angle
20
20
  3, 0, 100, PI, // emitSize, emitTime, emitRate, emitCone
21
21
  tile(0), // tileInfo
22
22
  hsl(0,0,0,.5), hsl(0,0,1,.5), // colorStartA, colorStartB
@@ -31,7 +31,10 @@ function gameUpdate()
31
31
  playerAngle += mouseDelta.x * .003;
32
32
 
33
33
  // update player movement, prevent walking through walls
34
- const velocity = keyDirection().scale(.1).rotate(playerAngle);
34
+ let velocity = keyDirection();
35
+ if (velocity.y && mouseIsDown(0))
36
+ velocity.y = 1; // forward movement with mouse
37
+ velocity = velocity.rotate(playerAngle).scale(.1);
35
38
  const newPos = playerPos.add(velocity);
36
39
  if (!levelTest(newPos.x, newPos.y))
37
40
  playerPos = newPos;
@@ -6,27 +6,35 @@ class PuzzlePiece extends EngineObject
6
6
  {
7
7
  constructor(gridPos, size)
8
8
  {
9
- const color = rgb(gridPos.x/(gridSize.x-1), gridPos.y/(gridSize.y-1), 1);
9
+ const x = gridPos.x, y = gridPos.y;
10
+ const color = rgb(x/(gridSize.x-1), y/(gridSize.y-1), 1);
10
11
  super(gridPos.multiply(size), size, 0, 0, color);
11
12
  this.gridPos = gridPos;
12
- this.text = gridPos.x + gridPos.y*gridSize.x;
13
+ this.text = x + y*gridSize.x;
14
+ this.moveTimer = new Timer;
15
+ this.lastPos = this.pos;
13
16
  }
14
17
 
15
18
  update()
16
19
  {
20
+ const deltaX = emptyGridPos.x - this.gridPos.x;
21
+ const deltaY = emptyGridPos.y - this.gridPos.y;
17
22
  if (mouseWasPressed(0) && isOverlapping(this.pos, this.size, mousePos))
18
- if (abs(this.gridPos.x-emptyGridPos.x) + abs(this.gridPos.y-emptyGridPos.y) == 1)
23
+ if (abs(deltaX) + abs(deltaY) === 1)
19
24
  {
20
25
  // swap with empty space when clicked on
26
+ this.lastPos = this.pos;
21
27
  this.pos = emptyGridPos.multiply(this.size);
22
28
  [emptyGridPos, this.gridPos] = [this.gridPos, emptyGridPos];
29
+ this.moveTimer.set(.2);
23
30
  }
24
31
  }
25
32
 
26
33
  render()
27
34
  {
28
- super.render();
29
- drawTextOverlay(this.text, this.pos, 2.5, BLACK);
35
+ const pos = this.lastPos.lerp(this.pos, this.moveTimer.getPercent());
36
+ drawRect(pos, this.size, this.color);
37
+ drawTextOverlay(this.text, pos, 2.5, BLACK);
30
38
  }
31
39
  }
32
40
 
@@ -1,7 +1,7 @@
1
1
  function gameRender()
2
2
  {
3
3
  // precreate variables to avoid overhead
4
- const pos = vec2(), size = vec2(), color = WHITE;
4
+ const pos = vec2(), size = vec2(), color = new Color;
5
5
  for (let i=2e3; i--;)
6
6
  {
7
7
  // use math to generate random star positions
@@ -41,14 +41,14 @@ function createUI()
41
41
  uiMenu.addChild(uiBackground);
42
42
 
43
43
  // example large text
44
- const textTitle = new LJS.UIText(vec2(0,-220), vec2(500, 90), 'Test Title');
44
+ const textTitle = new LJS.UIText(vec2(0,-220), vec2(500, 120), 'Test Title');
45
45
  uiMenu.addChild(textTitle);
46
46
  textTitle.textColor = LJS.RED;
47
47
  textTitle.lineWidth = 4;
48
48
  textTitle.lineColor = LJS.BLUE;
49
49
 
50
50
  // example multiline text
51
- const textTest = new LJS.UIText(vec2(-60,-120), vec2(300, 50), 'Test Text\nSecond text line.')
51
+ const textTest = new LJS.UIText(vec2(-60,-120), vec2(350, 60), 'Test Text\nSecond text line.')
52
52
  uiMenu.addChild(textTest);
53
53
 
54
54
  // example tile image
@@ -63,7 +63,7 @@ function createUI()
63
63
  // example button
64
64
  const button1 = new LJS.UIButton(vec2(0,140), vec2(350, 50), 'Test Button');
65
65
  uiMenu.addChild(button1);
66
- button1.onClick = ()=> console.log('Button 1 clicked');
66
+ button1.onClick = ()=> LOG('Button 1 clicked');
67
67
 
68
68
  // example checkbox
69
69
  const checkbox = new LJS.UICheckbox(vec2(-140,-20), vec2(40));
@@ -71,7 +71,7 @@ function createUI()
71
71
  checkbox.onChange = ()=> button1.disabled = checkbox.checked;
72
72
 
73
73
  // text attached to checkbox
74
- const checkboxText = new LJS.UIText(vec2(170,0), vec2(300, 40), 'Test Checkbox');
74
+ const checkboxText = new LJS.UIText(vec2(170,0), vec2(300, 60), 'Test Checkbox');
75
75
  checkbox.addChild(checkboxText);
76
76
 
77
77
  // exit button
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.14.10",
3
+ "version": "1.14.11",
4
4
  "description": "LittleJS - Tiny and Fast HTML5 Game Engine",
5
5
  "main": "dist/littlejs.esm.js",
6
6
  "types": "dist/littlejs.d.ts",
package/plugins/box2d.js CHANGED
@@ -1715,7 +1715,7 @@ async function box2dInit()
1715
1715
  }
1716
1716
  function box2dRender()
1717
1717
  {
1718
- if (box2dDebug || debugPhysics && debugOverlay)
1718
+ if (box2dDebug || debugPhysics)
1719
1719
  box2d.world.DrawDebugData();
1720
1720
  }
1721
1721