littlejsengine 1.9.1 → 1.9.3

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 (68) hide show
  1. package/README.md +11 -9
  2. package/{build → dist}/littlejs.d.ts +48 -43
  3. package/{build → dist}/littlejs.esm.js +256 -147
  4. package/dist/littlejs.esm.min.js +1 -0
  5. package/{build → dist}/littlejs.js +256 -147
  6. package/dist/littlejs.min.js +1 -0
  7. package/{build → dist}/littlejs.release.js +249 -144
  8. package/examples/breakout/game.js +2 -2
  9. package/examples/breakout/gameObjects.js +3 -3
  10. package/examples/breakout/index.html +3 -3
  11. package/examples/breakoutTutorial/index.html +2 -2
  12. package/examples/electron/build.js +1 -1
  13. package/examples/electron/game.js +49 -38
  14. package/examples/electron/index.html +2 -2
  15. package/examples/electron/tiles.png +0 -0
  16. package/examples/empty/index.html +1 -1
  17. package/examples/js13k/build.js +1 -1
  18. package/examples/js13k/game.js +10 -9
  19. package/examples/js13k/index.html +13 -13
  20. package/examples/js13k/tiles.png +0 -0
  21. package/examples/logo.png +0 -0
  22. package/examples/module/game.js +45 -41
  23. package/examples/module/index.html +1 -1
  24. package/examples/module/tiles.png +0 -0
  25. package/examples/particles/index.html +1 -1
  26. package/examples/platformer/data/gameLevelData.tmx +143 -0
  27. package/examples/platformer/data/gameLevelData.tsx +4 -0
  28. package/examples/platformer/game.js +31 -11
  29. package/examples/platformer/gameCharacter.js +9 -7
  30. package/examples/platformer/gameEffects.js +105 -164
  31. package/examples/platformer/gameLevel.js +164 -181
  32. package/examples/platformer/gameLevelData.js +181 -0
  33. package/examples/platformer/gameObjects.js +71 -26
  34. package/examples/platformer/gamePlayer.js +3 -2
  35. package/examples/platformer/index.html +8 -7
  36. package/examples/platformer/tiles.png +0 -0
  37. package/examples/platformer/tilesLevel.png +0 -0
  38. package/examples/puzzle/game.js +12 -12
  39. package/examples/puzzle/index.html +2 -2
  40. package/examples/screenshot.jpg +0 -0
  41. package/examples/starter/build.js +1 -1
  42. package/examples/starter/game.js +6 -6
  43. package/examples/starter/index.html +13 -13
  44. package/examples/starter/tiles.png +0 -0
  45. package/examples/stress/index.html +2 -2
  46. package/examples/typescript/build.bat +4 -1
  47. package/examples/typescript/game.js +34 -31
  48. package/examples/typescript/game.ts +40 -36
  49. package/examples/typescript/index.html +1 -1
  50. package/examples/typescript/tiles.png +0 -0
  51. package/package.json +3 -3
  52. package/reference.md +409 -0
  53. package/src/engine.js +46 -41
  54. package/src/engineAudio.js +22 -19
  55. package/src/engineBuild.js +9 -2
  56. package/src/engineDebug.js +7 -3
  57. package/src/engineDraw.js +8 -7
  58. package/src/engineInput.js +5 -5
  59. package/src/engineMedals.js +3 -3
  60. package/src/engineObject.js +2 -2
  61. package/src/engineParticles.js +16 -15
  62. package/src/engineSettings.js +3 -3
  63. package/src/engineTileLayer.js +3 -3
  64. package/src/engineUtilities.js +139 -38
  65. package/src/engineWebGL.js +2 -8
  66. package/build/littlejs.esm.min.js +0 -1
  67. package/build/littlejs.min.js +0 -1
  68. package/index.d.ts +0 -2094
@@ -8,214 +8,197 @@
8
8
 
9
9
  'use strict';
10
10
 
11
- const tileType_ladder = -1;
12
- const tileType_empty = 0;
13
- const tileType_solid = 1;
11
+ const tileType_ladder = -1;
12
+ const tileType_empty = 0;
13
+ const tileType_solid = 1;
14
+ const tileType_breakable = 2;
14
15
 
15
- let player, playerStartPos, tileLayer, tileBackgroundLayer, gameTimer = new Timer;
16
- let levelSize, levelColor, levelGroundColor, warmup;
16
+ let player, playerStartPos, tileData, tileLayers, foregroundLayerIndex, sky;
17
+ let levelSize, levelColor, levelBackgroundColor, levelOutlineColor, warmup;
17
18
 
18
- let tileBackground;
19
- const setTileBackgroundData = (pos, data=0)=>
20
- pos.arrayCheck(tileCollisionSize) && (tileBackground[(pos.y|0)*tileCollisionSize.x+pos.x|0] = data);
21
- const getTileBackgroundData = (pos)=>
22
- pos.arrayCheck(tileCollisionSize) ? tileBackground[(pos.y|0)*tileCollisionSize.x+pos.x|0] : 0;
19
+ const setTileData = (pos, layer, data)=>
20
+ pos.arrayCheck(tileCollisionSize) && (tileData[layer][(pos.y|0)*tileCollisionSize.x+pos.x|0] = data);
21
+ const getTileData = (pos, layer)=>
22
+ pos.arrayCheck(tileCollisionSize) ? tileData[layer][(pos.y|0)*tileCollisionSize.x+pos.x|0]: 0;
23
23
 
24
- ///////////////////////////////////////////////////////////////////////////////
25
- // level generation
26
-
27
- function buildTerrain(size)
24
+ function buildLevel()
28
25
  {
29
- tileBackground = [];
30
- initTileCollision(size);
31
- let startGroundLevel = 40;
32
- let groundLevel = startGroundLevel;
33
- let groundSlope = rand(-1,1);
34
- let backgroundDelta = 0, backgroundDeltaSlope = 0;
35
- for (let x=0; x < size.x; x++)
36
- {
37
- // pull slope towards start ground level
38
- groundLevel += groundSlope = rand() < .05 ? rand(-1,1) :
39
- groundSlope + (startGroundLevel - groundLevel)/1e3;
40
-
41
- // small jump
42
- if (rand() < .04)
43
- groundLevel += rand(9,-9);
44
-
45
- if (rand() < .03)
46
- {
47
- // big jump
48
- const jumpDelta = rand(19,-19);
49
- startGroundLevel = clamp(startGroundLevel + jumpDelta, 20, 80);
50
- groundLevel += jumpDelta;
51
- groundSlope = rand(-1,1);
52
- }
26
+ // create the level
27
+ levelColor = randColor(hsl(0,0,.2), hsl(0,0,.8));
28
+ levelBackgroundColor = levelColor.mutate().scale(.4,1);
29
+ levelOutlineColor = levelColor.mutate().add(hsl(0,0,.4)).clamp();
30
+ loadLevel();
31
+
32
+ // create sky object with gradient and stars
33
+ sky = new Sky;
34
+
35
+ // create parallax layers
36
+ for (let i=3; i--;)
37
+ new ParallaxLayer(i);
38
+
39
+ // apply decoration to all level tiles
40
+ const pos = vec2();
41
+ const layerCount = tileLayers.length;
42
+ for (let layer=layerCount; layer--;)
43
+ for (pos.x=levelSize.x; pos.x--;)
44
+ for (pos.y=levelSize.y; pos.y--;)
45
+ decorateTile(pos, layer);
53
46
 
54
- backgroundDelta += backgroundDeltaSlope;
55
- if (rand() < .1)
56
- backgroundDelta = rand(3, -1);
57
- if (rand() < .1)
58
- backgroundDelta = 0;
59
- if (rand() < .1)
60
- backgroundDeltaSlope = rand(-1,1);
61
- backgroundDelta = clamp(backgroundDelta, -1, 3)
62
-
63
- groundLevel = clamp(groundLevel, 20, levelSize.y-20);
64
- for (let y=0; y < size.y; y++)
65
- {
66
- const pos = vec2(x,y);
67
-
68
- let frontTile = tileType_empty;
69
- if (y < groundLevel)
70
- frontTile = tileType_solid;
71
-
72
- let backTile = tileType_empty;
73
- if (y < groundLevel + backgroundDelta)
74
- backTile = tileType_solid;
75
-
76
- setTileCollisionData(pos, frontTile);
77
- setTileBackgroundData(pos, backTile);
78
- }
79
- }
47
+ // warm up level
48
+ warmup = 1;
49
+ for (let i = 500; i--;)
50
+ engineObjectsUpdate();
51
+ warmup = 0;
52
+
53
+ // spawn player
54
+ player = new Player(playerStartPos);
55
+ }
56
+
57
+ function loadLevel()
58
+ {
59
+ // load level data from an exported Tiled js file
60
+ const dataName = Object.keys(TileMaps)[0];
61
+ const tileMapData = TileMaps[dataName];
62
+ levelSize = vec2(tileMapData.width, tileMapData.height);
63
+ initTileCollision(levelSize);
64
+ engineObjectsDestroy();
80
65
 
81
- // add random holes
82
- for (let i=levelSize.x; i--;)
66
+ // create table for tiles in the level tilemap
67
+ const tileLookup =
83
68
  {
84
- const pos = vec2(rand(levelSize.x), rand(levelSize.y/2, 9));
85
- const height = randInt(19,1);
86
- const offset = vec2();
87
- for (offset.x = randInt(19,1); --offset.x;)
88
- for (offset.y = height; --offset.y;)
89
- setTileCollisionData(pos.add(offset), tileType_empty);
69
+ circle: 1,
70
+ ground: 2,
71
+ ladder: 4,
72
+ metal: 5,
73
+ player: 17,
74
+ crate: 18,
75
+ enemy: 19,
76
+ coin: 20,
90
77
  }
91
78
 
92
- // add ladders
93
- for (let ladderCount = 40; ladderCount--;)
79
+ // set all level data tiles
80
+ tileData = [];
81
+ tileLayers = [];
82
+ playerStartPos = vec2(1, levelSize.y);
83
+ const layerCount = tileMapData.layers.length;
84
+ foregroundLayerIndex = layerCount-1;
85
+ for (let layer=layerCount; layer--;)
94
86
  {
95
- // pick random pos
96
- const pos = vec2(randInt(levelSize.x), randInt(levelSize.y))
97
-
98
- // find good place to put ladders
99
- let state = 0, ladderTop;
100
- for (; pos.y > 9; --pos.y)
87
+ const layerData = tileMapData.layers[layer].data;
88
+ const tileLayer = new TileLayer(vec2(), levelSize, tile(0,16,1));
89
+ tileLayer.renderOrder = -1e3+layer;
90
+ tileLayers[layer] = tileLayer;
91
+ tileData[layer] = [];
92
+
93
+ for (let x=levelSize.x; x--;)
94
+ for (let y=levelSize.y; y--;)
101
95
  {
102
- const data = getTileCollisionData(pos);
103
- if (state == 0 || state == 2)
104
- data || state++; // found empty, go to next state
105
- else if (state == 1)
96
+ const pos = vec2(x,levelSize.y-1-y);
97
+ const tile = layerData[y*levelSize.x+x];
98
+
99
+ if (tile >= tileLookup.player)
106
100
  {
107
- data && state++; // found solid, go to next state
108
- ladderTop = pos.y;
101
+ // create object instead of tile
102
+ const objectPos = pos.add(vec2(.5));
103
+ if (tile == tileLookup.player)
104
+ playerStartPos = objectPos;
105
+ if (tile == tileLookup.crate)
106
+ new Crate(objectPos);
107
+ if (tile == tileLookup.enemy)
108
+ new Enemy(objectPos);
109
+ if (tile == tileLookup.coin)
110
+ new Coin(objectPos);
111
+ setTileData(pos, layer, 0);
112
+ continue;
109
113
  }
110
- else if (state == 3 && data)
114
+
115
+ // set the tile data
116
+ setTileData(pos, layer, tile);
117
+
118
+ // get tile type for special tiles
119
+ let tileType = tileType_empty;
120
+ if (tile > 0)
121
+ tileType = tileType_breakable;
122
+ if (tile == tileLookup.ladder)
123
+ tileType = tileType_ladder;
124
+ if (tile == tileLookup.metal)
125
+ tileType = tileType_solid;
126
+ if (tileType)
111
127
  {
112
- // found solid again, build ladder
113
- for (; ++pos.y <= ladderTop;)
114
- setTileCollisionData(pos, tileType_ladder);
115
- break;
128
+ // set collision for solid tiles
129
+ if (layer == foregroundLayerIndex)
130
+ setTileCollisionData(pos, tileType);
131
+
132
+ // randomize tile appearance
133
+ let direction, mirror, color;
134
+ if (tileType == tileType_breakable)
135
+ {
136
+ direction = randInt(4);
137
+ mirror = randInt(2);
138
+ color = layer ? levelColor : levelBackgroundColor;
139
+ color = color.mutate(.03);
140
+ }
141
+
142
+ // set tile layer data
143
+ const data = new TileLayerData(tile-1, direction, mirror, color);
144
+ tileLayer.setData(pos, data);
116
145
  }
117
146
  }
147
+ tileLayer.redraw();
118
148
  }
119
-
120
- // spawn crates
121
- for (let crateCount=100; crateCount--;)
122
- new Crate(vec2((randInt(levelSize.x))+.5, randInt(levelSize.y)));
123
-
124
- // spawn enemies
125
- for (let enemyCount=10; enemyCount--;)
126
- new Enemy(vec2(rand(levelSize.x), rand(levelSize.y)));
127
149
  }
128
150
 
129
- function generateLevel()
151
+ function decorateTile(pos, layer=1)
130
152
  {
131
- // clear old level
132
- engineObjectsDestroy();
133
-
134
- // randomize ground level hills
135
- buildTerrain(levelSize);
153
+ ASSERT((pos.x|0) == pos.x && (pos.y|0)== pos.y);
154
+ const tileLayer = tileLayers[layer];
136
155
 
137
- // find starting poing for player
138
- playerStartPos = vec2(rand(levelSize.x), levelSize.y);
139
- const raycastHit = tileCollisionRaycast(playerStartPos, vec2(playerStartPos.x, 0));
140
- playerStartPos = raycastHit.add(vec2(0,1));
141
- }
142
-
143
- function makeTileLayers()
144
- {
145
- // create tile layers
146
- tileLayer = new TileLayer(vec2(), levelSize, tile(0,16,1));
147
- tileLayer.renderOrder = -1e3;
148
- tileBackgroundLayer = new TileLayer(vec2(), levelSize, tile(0,16,1));
149
- tileBackgroundLayer.renderOrder = -2e3;
150
-
151
- const pos = vec2();
152
- for (pos.x = levelSize.x; pos.x--;)
153
- for (pos.y = levelSize.y; pos.y--;)
156
+ if (layer == foregroundLayerIndex)
154
157
  {
155
- // foreground tiles
156
- let tileType = getTileCollisionData(pos);
157
- if (tileType)
158
+ const tileType = getTileCollisionData(pos);
159
+ if (tileType <= 0)
158
160
  {
159
- let direction, mirror, tileIndex, color;
160
- if (tileType == tileType_solid)
161
- {
162
- tileIndex = 1 + rand()**3*2|0;
163
- color = levelColor.mutate(.03);
164
- direction = randInt(4);
165
- mirror = randInt(2);
166
- }
167
- else if (tileType == tileType_ladder)
168
- tileIndex = 3;
169
-
170
- const data = new TileLayerData(tileIndex, direction, mirror, color);
171
- tileLayer.setData(pos, data);
161
+ // force it to clear if it is empty
162
+ tileType || tileLayer.setData(pos, new TileLayerData, 1);
163
+ return;
172
164
  }
173
-
174
- // background tiles
175
- tileType = getTileBackgroundData(pos);
176
- if (tileType)
165
+ if (tileType == tileType_breakable)
166
+ for (let i=4;i--;)
177
167
  {
178
- const data = new TileLayerData(1 + rand()**3*2|0, randInt(4), randInt(2), levelColor.mutate().scale(.4,1));
179
- tileBackgroundLayer.setData(pos, data);
168
+ // outline towards neighbors of differing type
169
+ const neighborTileType = getTileCollisionData(pos.add(vec2().setDirection(i)));
170
+ if (neighborTileType == tileType)
171
+ continue;
172
+
173
+ // make pixel perfect outlines
174
+ const size = i&1 ? vec2(2, 16) : vec2(16, 2);
175
+ tileLayer.context.fillStyle = levelOutlineColor.mutate(.1);
176
+ const drawPos = pos.scale(16)
177
+ .add(vec2(i==1?14:0,(i==0?14:0)))
178
+ .subtract((i&1? vec2(0,8-size.y/2) : vec2(8-size.x/2,0)));
179
+ tileLayer.context.fillRect(
180
+ drawPos.x, tileLayer.canvas.height - drawPos.y, size.x, -size.y);
180
181
  }
181
182
  }
182
- tileLayer.redraw();
183
- tileBackgroundLayer.redraw();
184
-
185
- if (!glEnable)
183
+ else
186
184
  {
187
- // get rid of background if webgl is not enabled
188
- tileBackgroundLayer.destroy();
189
- tileBackgroundLayer = 0;
185
+ // make round corners
186
+ for (let i=4;i--;)
187
+ {
188
+ // check corner neighbors
189
+ const neighborTileDataA = getTileData(pos.add(vec2().setDirection(i)), layer);
190
+ const neighborTileDataB = getTileData(pos.add(vec2().setAngle((i+1)%4*PI/2)), layer);
191
+ if (neighborTileDataA > 0 || neighborTileDataB > 0)
192
+ continue;
193
+
194
+ const directionVector = vec2().setAngle(i*PI/2+PI/4, 10).floor();
195
+ const drawPos = pos.add(vec2(.5)) // center
196
+ .scale(16).add(directionVector).floor(); // direction offset
197
+
198
+ // clear rect without any scaling to prevent blur from filtering
199
+ const s = 2;
200
+ tileLayer.context.clearRect(
201
+ drawPos.x - s/2, tileLayer.canvas.height - drawPos.y - s/2, s, s);
202
+ }
190
203
  }
191
204
  }
192
-
193
- function buildLevel()
194
- {
195
- levelSize = vec2(128);
196
- levelColor = randColor(new Color(.2,.2,.2), new Color(.8,.8,.8));
197
- levelGroundColor = levelColor.mutate().add(new Color(.4,.4,.4)).clamp();
198
-
199
- generateLevel();
200
- initSky();
201
- initParallaxLayers();
202
-
203
- // apply decoration to level tiles
204
- makeTileLayers();
205
- const pos = vec2();
206
- for (pos.x=levelSize.x; pos.x--;)
207
- for (pos.y=levelSize.y; pos.y-->1;)
208
- {
209
- decorateBackgroundTile(pos);
210
- decorateTile(pos);
211
- }
212
-
213
- // warm up level
214
- warmup = 1;
215
- for (let i = 500; i--;)
216
- engineObjectsUpdate();
217
- warmup = 0;
218
-
219
- // spawn player
220
- player = new Player(cameraPos = playerStartPos);
221
- }