littlejsengine 1.16.2 → 1.17.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 (57) hide show
  1. package/dist/littlejs.d.ts +291 -246
  2. package/dist/littlejs.esm.js +1520 -1271
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +1178 -920
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +1170 -912
  7. package/examples/box2d/gameObjects.js +4 -4
  8. package/examples/breakout/game.js +5 -6
  9. package/examples/electron/index.html +2 -2
  10. package/examples/electron/tiles.png +0 -0
  11. package/examples/htmlMenu/tiles.png +0 -0
  12. package/examples/index.html +9 -8
  13. package/examples/logo.png +0 -0
  14. package/examples/module/build.bat +7 -0
  15. package/examples/module/build.js +121 -0
  16. package/examples/module/tiles.png +0 -0
  17. package/examples/platformer/game.js +1 -1
  18. package/examples/platformer/gameEffects.js +25 -29
  19. package/examples/platformer/gameLevel.js +27 -28
  20. package/examples/puzzle/game.js +2 -2
  21. package/examples/shorts/base.html +4 -1
  22. package/examples/shorts/box2dPool.js +2 -2
  23. package/examples/shorts/box2dTileLayer.js +48 -0
  24. package/examples/shorts/clock.js +3 -3
  25. package/examples/shorts/fontImage.js +4 -3
  26. package/examples/shorts/music.js +2 -2
  27. package/examples/shorts/musicPlayer.js +2 -2
  28. package/examples/shorts/parallax.js +1 -1
  29. package/examples/shorts/sequencer.js +1 -1
  30. package/examples/shorts/shapes.js +1 -1
  31. package/examples/shorts/texture.js +10 -6
  32. package/examples/shorts/tiles.png +0 -0
  33. package/examples/shorts/tiltedView.js +2 -0
  34. package/examples/starter/index.html +2 -2
  35. package/examples/starter/tiles.png +0 -0
  36. package/examples/typescript/tiles.png +0 -0
  37. package/examples/uiSystem/game.js +1 -1
  38. package/examples/uiSystem/tiles.png +0 -0
  39. package/package.json +5 -5
  40. package/plugins/box2d.js +167 -63
  41. package/plugins/postProcess.js +47 -28
  42. package/plugins/uiSystem.js +18 -18
  43. package/plugins/zzfxm.js +2 -2
  44. package/reference.md +4 -3
  45. package/src/engine.js +182 -181
  46. package/src/engineAudio.js +47 -63
  47. package/src/engineDebug.js +8 -8
  48. package/src/engineDraw.js +184 -124
  49. package/src/engineExport.js +325 -334
  50. package/src/engineFont.png +0 -0
  51. package/src/engineInput.js +18 -24
  52. package/src/engineMath.js +24 -113
  53. package/src/engineObject.js +27 -26
  54. package/src/engineParticles.js +2 -2
  55. package/src/engineTileLayer.js +229 -192
  56. package/src/engineUtilities.js +100 -4
  57. package/src/engineWebGL.js +124 -73
@@ -76,7 +76,7 @@ export function spawnRandomEdges()
76
76
  edgePoints.push(vec2(40,0));
77
77
  for (let i=40, y=0; i--;)
78
78
  edgePoints.push(vec2(i, y=LJS.clamp(y+LJS.rand(-2,2),0,5)));
79
- edgePoints.push(vec2(0,0));
79
+ edgePoints.push(vec2());
80
80
  const o = new LJS.Box2dStaticObject;
81
81
  o.addEdgeList(edgePoints);
82
82
  }
@@ -126,7 +126,7 @@ export class CarObject extends LJS.Box2dObject
126
126
  const maxTorque = 35;
127
127
  const sprite = Game.spriteAtlas.wheel;
128
128
  this.wheels = [];
129
- const makeWheel = (pos, isMotor) =>
129
+ const makeWheel = (pos, isMotor)=>
130
130
  {
131
131
  const wheel = new LJS.Box2dObject(pos, vec2(diameter), sprite);
132
132
  const joint = new LJS.Box2dWheelJoint(this, wheel);
@@ -284,7 +284,7 @@ export class SoftBodyObject extends LJS.Box2dObject
284
284
  for (let x=sizeCount.x; x--;)
285
285
  {
286
286
  const o = this.getNode(x, y);
287
- const tryAddJoint = (xo, yo) =>
287
+ const tryAddJoint = (xo, yo)=>
288
288
  {
289
289
  const o2 = this.getNode(x+xo, y+yo);
290
290
  const joint = o2 ? new LJS.Box2dWeldJoint(o, o2) : 0;
@@ -360,7 +360,7 @@ export class ClothObject extends LJS.Box2dStaticObject
360
360
  const d = y%2 ? 1 : -1;
361
361
  const x2 = d>1 ? x : sizeCount.x-1-x;
362
362
  const o = this.getNode(x2, y);
363
- const tryAddJoint = (xo, yo) =>
363
+ const tryAddJoint = (xo, yo)=>
364
364
  {
365
365
  const o2 = this.getNode(x2+xo, y+yo);
366
366
  const joint = o2 ? new LJS.Box2dRopeJoint(o, o2) : 0;
@@ -103,13 +103,12 @@ function gameRender()
103
103
  function gameRenderPost()
104
104
  {
105
105
  // use built in image font for text
106
- const font = new LJS.FontImage;
107
-
108
- font.drawText('Score: ' + score, LJS.cameraPos.add(vec2(0,9.7)), .15, true);
106
+ const font = LJS.engineFontImage;
107
+ font.drawText('Score: ' + score, LJS.cameraPos.add(vec2(0,9.2)), 1);
109
108
  if (!brickCount)
110
- font.drawText('You Win!', LJS.cameraPos.add(vec2(0,-5)), .2, true);
109
+ font.drawText('You Win!', LJS.cameraPos.add(vec2(0,-5)), 2);
111
110
  else if (!ball)
112
- font.drawText('Click to Play', LJS.cameraPos.add(vec2(0,-5)), .2, true);
111
+ font.drawText('Click to Play', LJS.cameraPos.add(vec2(0,-5)), 2);
113
112
  }
114
113
 
115
114
  ///////////////////////////////////////////////////////////////////////////////
@@ -138,7 +137,7 @@ function setupPostProcess()
138
137
 
139
138
  // scan lines
140
139
  const float scanlineScale = 2.;
141
- const float scanlineAlpha = .3;
140
+ const float scanlineAlpha = .6;
142
141
  c *= 1. - scanlineAlpha*cos(p.y*2.*iResolution.y/scanlineScale);
143
142
 
144
143
  {
@@ -7,7 +7,7 @@
7
7
  </head><body>
8
8
 
9
9
  <!-- LittleJS Engine -->
10
- <script src=../../dist/littlejs.js?1.16.2></script>
10
+ <script src=../../dist/littlejs.js?1.17.5></script>
11
11
 
12
12
  <!-- Add your game scripts here -->
13
- <script src=game.js?1.16.2></script>
13
+ <script src=game.js?1.17.5></script>
Binary file
Binary file
@@ -113,7 +113,6 @@ const exampleList =
113
113
  new ExampleInfo('Piano', 'piano.js', 'Interactive piano keyboard', false, 'music, sound, audio, notes, ui, instrument'),
114
114
  new ExampleInfo('Step Sequencer', 'sequencer.js', 'Simple music loop creation tool', false, 'music, sound, audio, notes, ui, instrument'),
115
115
  new ExampleInfo('Music Player', 'musicPlayer.js', 'Music player with audio seeking and drag and drop', false, 'sound, loading, audio, ui'),
116
- new ExampleInfo('WebGL Shader', 'shader.js', 'Full canvas webgl shader', false, 'webgl, visual, effect'),
117
116
  new ExampleInfo('--- MINI GAMES ---'),
118
117
  new ExampleInfo('Pong Game', 'pongGame.js', 'Classic paddle ball bouncing', false, 'objects, collision'),
119
118
  new ExampleInfo('Platformer Game', 'platformer.js', 'Jump and run side view', false, 'objects, gravity, level, tiles, camera'),
@@ -128,7 +127,9 @@ const exampleList =
128
127
  new ExampleInfo('--- PLUGINS ---'),
129
128
  new ExampleInfo('Box2d Demo', 'box2d.js', 'Box2D physics plugin', false, 'objects, mouse'),
130
129
  new ExampleInfo('Box2d Car', 'box2dCar.js', 'Drivable car with Box2D physics', false, 'objects, vehicle, suspension, wheels'),
131
- new ExampleInfo('Box2d Pool', 'box2dPool.js', 'Billiard table pool game with physics', false, 'objects, game'),
130
+ new ExampleInfo('Box2d Pool', 'box2dPool.js', 'Billiard table pool game with Box2d physics', false, 'objects, game'),
131
+ new ExampleInfo('Box2d Tile Layer', 'box2dTileLayer.js', 'Tile layer with Box2d physics', false, 'objects, level, map, grid'),
132
+ new ExampleInfo('WebGL Shader', 'shader.js', 'Full canvas webgl shader', false, 'webgl, visual, effect'),
132
133
  new ExampleInfo('Post Processing', 'postProcess.js', 'Shader effects and filters', false, 'webgl, visual, effect'),
133
134
  new ExampleInfo('UI System', 'uiSystem.js', 'Buttons, sliders, and checkboxes', false, 'objects, widgets, interactive'),
134
135
  new ExampleInfo('Nine Slice', 'nineSlice.js', 'Scalable UI panels', false, 'three slice, stretch, corners, text, tiles'),
@@ -159,7 +160,7 @@ function initExampleBrowser()
159
160
  // set the selected example from the URL parameters
160
161
  const urlParams = new URLSearchParams(window.location.search);
161
162
  const selectedExample = urlParams.get('example') || '';
162
- let exampleIndex = exampleList.findIndex((example) => example.name === selectedExample);
163
+ let exampleIndex = exampleList.findIndex((example)=> example.name === selectedExample);
163
164
  if (exampleIndex < 0)
164
165
  exampleIndex = 1;
165
166
  selectExample.selectedIndex = exampleIndex;
@@ -398,7 +399,7 @@ function codeInput()
398
399
  // debounce input - get content from code mirror if available, otherwise from textarea
399
400
  clearTimeout(inputTimeout);
400
401
  const code = codeMirror ? codeMirror.getValue() : textareaCode.value;
401
- inputTimeout = setTimeout(() => setCode(code), 500);
402
+ inputTimeout = setTimeout(()=> setCode(code), 500);
402
403
  }
403
404
 
404
405
  function restartCode()
@@ -453,7 +454,7 @@ function setCode(code, filename)
453
454
  const anonymousMatch = stack?.match(/(<anonymous>|injectedScript):(\d+)/);
454
455
  return anonymousMatch ? parseInt(anonymousMatch[2]) : -1;
455
456
  }
456
- iframeContent.onerror = (message, source, lineno, colno, error) =>
457
+ iframeContent.onerror = (message, source, lineno, colno, error)=>
457
458
  {
458
459
  let text = message;
459
460
  if (lineno)
@@ -463,7 +464,7 @@ function setCode(code, filename)
463
464
  setErrorMessage(text);
464
465
  setErrorLine(lineno);
465
466
  }
466
- iframeContent.onunhandledrejection = (event) =>
467
+ iframeContent.onunhandledrejection = (event)=>
467
468
  {
468
469
  let text = event.reason;
469
470
  setErrorMessage(text);
@@ -510,14 +511,14 @@ function setCode(code, filename)
510
511
 
511
512
  {
512
513
  // hook up buttons
513
- buttonScreenshot.onclick = () =>
514
+ buttonScreenshot.onclick = ()=>
514
515
  {
515
516
  if (iframeContent.debugScreenshot)
516
517
  iframeContent.debugScreenshot();
517
518
  }
518
519
 
519
520
  // pause/resume functionality
520
- buttonPause.onclick = () =>
521
+ buttonPause.onclick = ()=>
521
522
  {
522
523
  if (!iframeContent.getPaused || !iframeContent.setPaused)
523
524
  return;
package/examples/logo.png CHANGED
Binary file
@@ -0,0 +1,7 @@
1
+ rem LittleJS Build Script
2
+ call node build.js
3
+ if %errorlevel% neq 0 (
4
+ echo Build failed with error level %errorlevel%
5
+ pause
6
+ exit /b %errorlevel%
7
+ )
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * LittleJS Build System
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const PROGRAM_TITLE = 'Little JS Starter Project';
10
+ const PROGRAM_NAME = 'game';
11
+ const BUILD_FOLDER = 'build';
12
+ const sourceFiles =
13
+ [
14
+ 'game.js',
15
+ // add your game's source files here
16
+ ];
17
+ const engineFile = '../../dist/littlejs.esm.min.js'; // Use the minified ES module
18
+ const dataFiles =
19
+ [
20
+ 'tiles.png',
21
+ // add your game's data files here
22
+ ];
23
+
24
+ console.log(`Building ${PROGRAM_NAME}...`);
25
+ const startTime = Date.now();
26
+ const fs = require('node:fs');
27
+ const child_process = require('node:child_process');
28
+
29
+ // rebuild engine
30
+ //child_process.execSync(`npm run build`, { stdio: 'inherit' });
31
+
32
+ // remove old files and setup build folder
33
+ fs.rmSync(BUILD_FOLDER, { recursive: true, force: true });
34
+ fs.rmSync(`${PROGRAM_NAME}.zip`, { force: true });
35
+ fs.mkdirSync(BUILD_FOLDER);
36
+
37
+ // copy data files
38
+ for (const file of dataFiles)
39
+ fs.copyFileSync(file, `${BUILD_FOLDER}/${file}`);
40
+
41
+ // copy engine module
42
+ fs.copyFileSync(engineFile, `${BUILD_FOLDER}/littlejs.esm.min.js`);
43
+
44
+ Build
45
+ (
46
+ sourceFiles,
47
+ [htmlBuildStep, zipBuildStep]
48
+ );
49
+
50
+ console.log('');
51
+ console.log(`Build Completed in ${((Date.now() - startTime)/1e3).toFixed(2)} seconds!`);
52
+
53
+ ///////////////////////////////////////////////////////////////////////////////
54
+
55
+ // A single build with its own source files, build steps, and output file
56
+ // - each build step is a callback that accepts a single filename
57
+ function Build(files=[], buildSteps=[])
58
+ {
59
+ // process each source file separately (don't concatenate modules!)
60
+ for (const file of files)
61
+ {
62
+ const outputFile = `${BUILD_FOLDER}/${file}`;
63
+ fs.copyFileSync(file, outputFile);
64
+ moduleFixStep(outputFile);
65
+ uglifyBuildStep(outputFile);
66
+ }
67
+
68
+ // execute build steps in order
69
+ for (const buildStep of buildSteps)
70
+ buildStep();
71
+ }
72
+
73
+ function moduleFixStep(filename)
74
+ {
75
+ console.log(`Fixing module imports in ${filename}...`);
76
+
77
+ let code = fs.readFileSync(filename, 'utf8');
78
+
79
+ // update the import path to use the local minified version
80
+ code = code.replace(/import \* as LJS from ['"].*littlejs\.esm(?:\.min)?\.js['"];?/g,
81
+ "import * as LJS from './littlejs.esm.min.js';");
82
+
83
+ // also fix relative imports to other game modules
84
+ code = code.replace(/from ['"]\.\.\//g, "from './");
85
+
86
+ fs.writeFileSync(filename, code);
87
+ }
88
+
89
+ function uglifyBuildStep(filename)
90
+ {
91
+ console.log('Running uglify...');
92
+ child_process.execSync(`npx uglifyjs ${filename} -c -m -o ${filename}`, {stdio: 'inherit'});
93
+ };
94
+
95
+ function htmlBuildStep()
96
+ {
97
+ console.log('Building html...');
98
+
99
+ // create html file with module script tag pointing to main game file
100
+ let buffer = ''
101
+ buffer += '<!DOCTYPE html>';
102
+ buffer += '<head>';
103
+ buffer += `<title>${PROGRAM_TITLE}</title>`;
104
+ buffer += '<meta charset=utf-8>';
105
+ buffer += '</head>';
106
+ buffer += '<body>';
107
+ buffer += '<script src="game.js" type="module"></script>';
108
+ buffer += '</body>';
109
+
110
+ // output html file
111
+ fs.writeFileSync(`${BUILD_FOLDER}/index.html`, buffer, {flag: 'w+'});
112
+ };
113
+
114
+ function zipBuildStep()
115
+ {
116
+ console.log('Zipping...');
117
+ const sources = ['index.html', 'littlejs.esm.min.js', ...sourceFiles, ...dataFiles];
118
+ const sourceList = sources.join(' ');
119
+ child_process.execSync(`npx bestzip ../${PROGRAM_NAME}.zip ${sourceList}`, {cwd:BUILD_FOLDER, stdio: 'inherit'});
120
+ console.log(`Size of ${PROGRAM_NAME}.zip: ${fs.statSync(`${PROGRAM_NAME}.zip`).size} bytes`);
121
+ };
Binary file
@@ -127,7 +127,7 @@ function gameRender()
127
127
  function gameRenderPost()
128
128
  {
129
129
  // draw to main canvas for hud rendering
130
- const drawText = (text, x, y, size=40) =>
130
+ const drawText = (text, x, y, size=40)=>
131
131
  {
132
132
  const context = LJS.mainContext;
133
133
  context.textAlign = 'center';
@@ -15,9 +15,6 @@ import * as LJS from '../../dist/littlejs.esm.js';
15
15
  import * as GameLevel from './gameLevel.js';
16
16
  const {vec2, hsl, rgb} = LJS;
17
17
 
18
- // use low graphics mode on mobile devices
19
- const lowGraphicsMode = LJS.isTouchDevice;
20
-
21
18
  ///////////////////////////////////////////////////////////////////////////////
22
19
  // sound effects
23
20
 
@@ -36,15 +33,11 @@ export const sound_score = new LJS.Sound([,,783,,.03,.02,1,2,,,940,.03,,,
36
33
 
37
34
  export const persistentParticleDestroyCallback = (particle)=>
38
35
  {
39
- if (lowGraphicsMode) return;
40
-
41
36
  // draw particle to tile layer on death
42
37
  LJS.ASSERT(!particle.tileInfo, 'quick draw to tile layer uses canvas 2d so must be untextured');
43
38
  if (particle.groundObject)
44
39
  {
45
40
  GameLevel.foregroundTileLayer.drawTile(particle.pos, particle.size, particle.tileInfo, particle.color, particle.angle, particle.mirror);
46
- // update WebGL texture
47
- GameLevel.foregroundTileLayer.useWebGL();
48
41
  }
49
42
  }
50
43
 
@@ -74,26 +67,27 @@ export function explosion(pos, radius=3)
74
67
 
75
68
  sound_explosion.play(pos);
76
69
 
77
- // destroy level
78
- for (let x = -radius; x < radius; ++x)
79
- {
80
- const h = (radius*radius - x*x)**.5;
81
- for (let y = -h; y <= h; ++y)
82
- destroyTile(pos.add(vec2(x,y)), 0, 0);
83
- }
84
-
85
- // cleanup neighbors
86
- const cleanupRadius = radius + 2;
87
- for (let x = -cleanupRadius; x < cleanupRadius; ++x)
88
70
  {
89
- const h = (cleanupRadius**2 - x**2)**.5;
90
- for (let y = -h; y < h; ++y)
91
- GameLevel.decorateTile(pos.add(vec2(x,y)).floor());
71
+ // destroy level
72
+ const layer = GameLevel.tileLayers[1];
73
+ layer.redrawStart();
74
+ for (let x = -radius; x < radius; ++x)
75
+ {
76
+ const h = (radius*radius - x*x)**.5;
77
+ for (let y = -h; y <= h; ++y)
78
+ destroyTile(pos.add(vec2(x,y)), 0, 0);
79
+ }
80
+ // cleanup neighbors
81
+ const cleanupRadius = radius + 2;
82
+ for (let x = -cleanupRadius; x < cleanupRadius; ++x)
83
+ {
84
+ const h = (cleanupRadius**2 - x**2)**.5;
85
+ for (let y = -h; y < h; ++y)
86
+ GameLevel.decorateTile(pos.add(vec2(x,y)).floor(), layer);
87
+ }
88
+ layer.redrawEnd();
92
89
  }
93
90
 
94
- // update WebGL texture
95
- GameLevel.foregroundTileLayer.useWebGL();
96
-
97
91
  // kill/push objects
98
92
  LJS.engineObjectsCallback(pos, radius*3, (o)=>
99
93
  {
@@ -159,16 +153,18 @@ export function destroyTile(pos, makeSound = 1, cleanup = 1)
159
153
  makeSound && sound_destroyObject.play(centerPos);
160
154
 
161
155
  // set and clear tile
162
- layer.setData(pos, new LJS.TileLayerData, true);
156
+ layer.clearData(pos, true);
163
157
  layer.setCollisionData(pos, GameLevel.tileType_empty);
164
158
 
165
159
  // cleanup neighbors and rebuild WebGL
166
160
  if (cleanup)
167
161
  {
162
+ const layer = GameLevel.tileLayers[1];
163
+ layer.redrawStart();
168
164
  for (let i=-1;i<=1;++i)
169
165
  for (let j=-1;j<=1;++j)
170
- GameLevel.decorateTile(pos.add(vec2(i,j)));
171
- layer.useWebGL();
166
+ GameLevel.decorateTile(pos.add(vec2(i,j)), layer);
167
+ layer.redrawEnd();
172
168
  }
173
169
 
174
170
  return true;
@@ -198,7 +194,7 @@ export class Sky extends LJS.EngineObject
198
194
  // draw stars
199
195
  LJS.setBlendMode(true);
200
196
  const random = new LJS.RandomGenerator(this.seed);
201
- for (let i= lowGraphicsMode ? 500 : 1e3; i--;)
197
+ for (let i = 1e3; i--;)
202
198
  {
203
199
  const size = random.float(.5,2)**2;
204
200
  const speed = random.float() < .9 ? random.float(5) : random.float(9,99);
@@ -260,7 +256,7 @@ export class ParallaxLayer extends LJS.CanvasLayer
260
256
  this.context.clearRect(0,0,1,h);
261
257
 
262
258
  // make WebGL texture
263
- this.useWebGL();
259
+ this.updateWebGL();
264
260
  }
265
261
 
266
262
  render()
@@ -96,8 +96,8 @@ function loadLevelData()
96
96
  new GameObjects.Coin(objectPos);
97
97
 
98
98
  // replace with empty tile and empty collision
99
- tileLayer.setData(pos, new LJS.TileLayerData);
100
- tileLayer.setCollisionData(pos, 0);
99
+ tileLayer.clearData(pos);
100
+ tileLayer.clearCollisionData(pos);
101
101
  continue;
102
102
  }
103
103
 
@@ -126,35 +126,32 @@ function loadLevelData()
126
126
  }
127
127
  }
128
128
  }
129
+
130
+ tileLayer.onRedraw = ()=>
131
+ {
132
+ // apply decoration to level tiles
133
+ for (let x=levelSize.x; x--;)
134
+ for (let y=levelSize.y; y--;)
135
+ decorateTile(vec2(x,y), tileLayer);
136
+ }
129
137
  tileLayer.redraw();
130
138
  }
131
-
132
- // apply decoration to all level tiles
133
- const pos = vec2();
134
- const layerCount = tileMapData.layers.length;
135
- for (let layer=layerCount; layer--;)
136
- {
137
- for (pos.x=levelSize.x; pos.x--;)
138
- for (pos.y=levelSize.y; pos.y--;)
139
- decorateTile(pos, layer);
140
- tileLayers[layer].useWebGL();
141
- }
142
139
  }
143
140
 
144
- export function decorateTile(pos, layer=1)
141
+ export function decorateTile(pos, tileLayer)
145
142
  {
146
143
  LJS.ASSERT((pos.x|0) == pos.x && (pos.y|0)== pos.y);
147
- const tileLayer = tileLayers[layer];
148
144
  if (!tileLayer)
149
145
  return;
150
146
 
147
+ const w = tileLayer.tileInfo.size.x;
151
148
  if (tileLayer == foregroundTileLayer)
152
149
  {
153
150
  const tileType = tileLayer.getCollisionData(pos);
154
151
  if (tileType <= 0)
155
152
  {
156
153
  // force it to clear if it is empty
157
- tileType || tileLayer.setData(pos, new LJS.TileLayerData, 1);
154
+ tileType || tileLayer.clearData(pos, true);
158
155
  return;
159
156
  }
160
157
  if (tileType == tileType_breakable)
@@ -167,12 +164,11 @@ export function decorateTile(pos, layer=1)
167
164
 
168
165
  // make pixel perfect outlines
169
166
  const size = i&1 ? vec2(2, 16) : vec2(16, 2);
170
- tileLayer.context.fillStyle = levelOutlineColor.mutate(.1);
171
167
  const drawPos = pos.scale(16)
172
168
  .add(vec2(i==1?14:0,(i==0?14:0)))
173
169
  .subtract((i&1? vec2(0,8-size.y/2) : vec2(8-size.x/2,0)));
174
- tileLayer.context.fillRect(
175
- drawPos.x, tileLayer.canvas.height - drawPos.y, size.x, -size.y);
170
+ const color = levelOutlineColor.mutate(.1);
171
+ tileLayer.drawLayerRect(drawPos, size, color);
176
172
  }
177
173
  }
178
174
  else
@@ -185,15 +181,18 @@ export function decorateTile(pos, layer=1)
185
181
  const neighborTileDataB = tileLayer.getData(pos.add(vec2().setDirection((i+1)%4))).tile;
186
182
  if (neighborTileDataA > 0 || neighborTileDataB > 0)
187
183
  continue;
188
-
189
- const directionVector = vec2().setAngle((i+.5)*LJS.PI/2, 10).floor();
190
- const drawPos = pos.add(vec2(.5)) // center
191
- .scale(16).add(directionVector).floor(); // direction offset
192
-
193
- // clear rect without any scaling to prevent blur from filtering
194
- const s = 2;
195
- tileLayer.context.clearRect(
196
- drawPos.x - s/2, tileLayer.canvas.height - drawPos.y - s/2, s, s);
184
+
185
+ // get direction of corner
186
+ const directionVector = vec2();
187
+ if (i==0) directionVector.set(w-1,w-1);
188
+ if (i==1) directionVector.set(w-1,1);
189
+ if (i==2) directionVector.set(1,1);
190
+ if (i==3) directionVector.set(1,w-1);
191
+
192
+ // clear rect from corner
193
+ const s = vec2(2);
194
+ const drawPos = pos.scale(w).add(directionVector).subtract(s.scale(.5));
195
+ tileLayer.clearLayerRect(drawPos, s);
197
196
  }
198
197
  }
199
198
  }
@@ -44,8 +44,8 @@ const tileColors =
44
44
  ];
45
45
  const tileTypeCount = tileColors.length;
46
46
 
47
- const getTile = (pos) => level[pos.x + pos.y * levelSize.x];
48
- const setTile = (pos, data) => level[pos.x + pos.y * levelSize.x] = data;
47
+ const getTile = (pos)=> level[pos.x + pos.y * levelSize.x];
48
+ const setTile = (pos, data)=> level[pos.x + pos.y * levelSize.x] = data;
49
49
 
50
50
  ///////////////////////////////////////////////////////////////////////////////
51
51
  function gameReset()
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html><meta charset=utf-8><body>
2
- <script src=../../dist/littlejs.js?1.16.2></script>
2
+ <script src=../../dist/littlejs.js?1.17.5></script>
3
3
  <script src=../../dist/box2d.wasm.js></script>
4
4
 
5
5
  <!-- LittleJS Engine Source
@@ -38,6 +38,9 @@ canvasPixelated = false;
38
38
  // disable watermark
39
39
  debugWatermark = false;
40
40
 
41
+ // disable showing the engine version in console
42
+ showEngineVersion = false;
43
+
41
44
  // these functions can be overridden for each example
42
45
  function gameInit() {}
43
46
  function gameUpdate() {}
@@ -3,10 +3,10 @@ const maxHitDistance = 6;
3
3
 
4
4
  class Ball extends Box2dObject
5
5
  {
6
- constructor(position, number=0)
6
+ constructor(pos, number=0)
7
7
  {
8
8
  const color = hsl(number/9, 1, number? .5 : 1);
9
- super(position, vec2(), 0, 0, color);
9
+ super(pos, vec2(), 0, 0, color);
10
10
  this.number = number;
11
11
 
12
12
  // setup pool ball physics
@@ -0,0 +1,48 @@
1
+ let box2DTileLayer;
2
+
3
+ async function gameInit()
4
+ {
5
+ // setup box2d
6
+ await box2dInit();
7
+ cameraPos = vec2(16); // setup camera
8
+ gravity.y = -30; // enable gravity
9
+ canvasClearColor = hsl(0,0,.2); // background color
10
+
11
+ // create tile layer
12
+ const pos = vec2();
13
+ const tileLayer = new TileCollisionLayer(pos, vec2(32));
14
+ for (pos.x = tileLayer.size.x; pos.x--;)
15
+ for (pos.y = tileLayer.size.y; pos.y--;)
16
+ {
17
+ // check if tile should be solid
18
+ if (randBool(.7))
19
+ continue;
20
+
21
+ // set tile data
22
+ const tileIndex = 11;
23
+ const direction = randInt(4)
24
+ const mirror = randBool();
25
+ const color = randColor(WHITE, hsl(0,0,.2));
26
+ const data = new TileLayerData(tileIndex, direction, mirror, color);
27
+ tileLayer.setData(pos, data);
28
+ tileLayer.setCollisionData(pos);
29
+ }
30
+ tileLayer.redraw(); // redraw tile layer with new data
31
+ box2DTileLayer = new Box2dTileLayer(tileLayer);
32
+ }
33
+
34
+ function gameUpdate()
35
+ {
36
+ if (mouseWasPressed(0))
37
+ {
38
+ // clear tile that was clicked
39
+ box2DTileLayer.tileLayer.clearData(mousePos, true);
40
+ box2DTileLayer.tileLayer.clearCollisionData(mousePos);
41
+ box2DTileLayer.buildCollision();
42
+
43
+ // spawn box2d object at mouse position
44
+ const o = new Box2dObject(mousePos, vec2(), 0, 0, randColor());
45
+ const friction = .2, restitution = .5;
46
+ o.addCircle(rand(.5,1), vec2(), 1, friction, restitution);
47
+ }
48
+ }
@@ -15,7 +15,7 @@ function gameRender()
15
15
  const h = (d.slice(0,2)|0) + m/60;
16
16
 
17
17
  // draw clock hands
18
- drawLine(vec2(0,0), vec2(0,4).rotate(h/12*2*PI), 1);
19
- drawLine(vec2(0,0), vec2(0,6).rotate(m/60*2*PI), .4);
20
- drawLine(vec2(0,0), vec2(0,8).rotate(s/60*2*PI), .1);
18
+ drawLine(vec2(), vec2(0,4).rotate(h/12*2*PI), 1);
19
+ drawLine(vec2(), vec2(0,6).rotate(m/60*2*PI), .4);
20
+ drawLine(vec2(), vec2(0,8).rotate(s/60*2*PI), .1);
21
21
  }
@@ -1,8 +1,8 @@
1
1
  function gameRender()
2
2
  {
3
3
  // draw text with built in engine font image
4
- const font = new FontImage;
5
- font.drawText('Engine Font Test', cameraPos.add(vec2(0,3)), .2);
4
+ const font = engineFontImage
5
+ font.drawText('Engine Font', vec2(0,3), 2);
6
6
 
7
7
  // show every character in the font
8
8
  let s = '';
@@ -12,5 +12,6 @@ function gameRender()
12
12
  s += '\n';
13
13
  s += String.fromCharCode(i);
14
14
  }
15
- font.drawText(s, cameraPos, .1);
15
+
16
+ font.drawText(s, vec2());
16
17
  }
@@ -1,4 +1,4 @@
1
- const musicSound = new SoundWave('song.mp3');
1
+ const musicSound = new Sound('song.mp3');
2
2
  let musicVolume = 1, musicInstance;
3
3
 
4
4
  function gameInit()
@@ -23,7 +23,7 @@ function gameInit()
23
23
  const volumeSlider = new UIScrollbar(vec2(0, -20), vec2(400, 30),
24
24
  musicVolume, 'Music Volume');
25
25
  musicPlayer.addChild(volumeSlider);
26
- volumeSlider.onChange = () =>
26
+ volumeSlider.onChange = ()=>
27
27
  {
28
28
  musicVolume = volumeSlider.value;
29
29
  musicInstance?.setVolume(musicVolume);
@@ -29,7 +29,7 @@ function gameInit()
29
29
  const volumeSlider = new UIScrollbar(vec2(0, -20), vec2(400, 30),
30
30
  musicVolume, 'Music Volume');
31
31
  musicPlayer.addChild(volumeSlider);
32
- volumeSlider.onChange = () =>
32
+ volumeSlider.onChange = ()=>
33
33
  {
34
34
  musicVolume = volumeSlider.value;
35
35
  musicInstance?.setVolume(musicVolume);
@@ -89,7 +89,7 @@ function gameInit()
89
89
 
90
90
  // create new sound from dropped file
91
91
  const fileURL = URL.createObjectURL(file);
92
- musicSound = new SoundWave(fileURL, musicVolume);
92
+ musicSound = new Sound(fileURL, musicVolume);
93
93
  dropZoneText.text = file.name;
94
94
 
95
95
  // reset UI