littlejsengine 1.23.0 → 1.23.1

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.
package/COPYRIGHT.txt CHANGED
@@ -1,38 +1,65 @@
1
- LittleJS Notices and Third-Party Licenses
2
- =========================================
3
-
4
- This repository is licensed under the MIT License. See the file LICENSE.
5
-
6
- This file collects copyright notices and third-party license texts for
7
- software included with or used by this repository. If you redistribute
8
- LittleJS with third-party components, keep the relevant notices below.
9
-
10
- -------------------------------------------------------------------------------
11
-
12
- LittleJS Engine License
13
-
14
- Copyright (c) 2021 Frank Force
15
- http://www.frankforce.com
16
- Licensed under the MIT License. See LICENSE.
17
-
18
- -------------------------------------------------------------------------------
19
-
20
- Box2D License
21
-
22
- Copyright (c) 2006-2013 Erin Catto http://www.gphysics.com
23
-
24
- This software is provided 'as-is', without any express or implied
25
- warranty. In no event will the authors be held liable for any damages
26
- arising from the use of this software.
27
-
28
- Permission is granted to anyone to use this software for any purpose,
29
- including commercial applications, and to alter it and redistribute it
30
- freely, subject to the following restrictions:
31
-
32
- 1. The origin of this software must not be misrepresented; you must not
33
- claim that you wrote the original software. If you use this software
34
- in a product, an acknowledgment in the product documentation would be
35
- appreciated but is not required.
36
- 2. Altered source versions must be plainly marked as such, and must not be
37
- misrepresented as being the original software.
38
- 3. This notice may not be removed or altered from any source distribution.
1
+ LittleJS Notices and Third-Party Licenses
2
+ =========================================
3
+
4
+ This repository is licensed under the MIT License. See the file LICENSE.
5
+
6
+ This file collects copyright notices and third-party license texts for
7
+ software included with or used by this repository. If you redistribute
8
+ LittleJS with third-party components, keep the relevant notices below.
9
+
10
+ -------------------------------------------------------------------------------
11
+
12
+ LittleJS Engine License
13
+
14
+ Copyright (c) 2021 Frank Force
15
+ http://www.frankforce.com
16
+ Licensed under the MIT License. See LICENSE.
17
+
18
+ -------------------------------------------------------------------------------
19
+
20
+ Box2D License
21
+
22
+ Copyright (c) 2006-2013 Erin Catto http://www.gphysics.com
23
+
24
+ This software is provided 'as-is', without any express or implied
25
+ warranty. In no event will the authors be held liable for any damages
26
+ arising from the use of this software.
27
+
28
+ Permission is granted to anyone to use this software for any purpose,
29
+ including commercial applications, and to alter it and redistribute it
30
+ freely, subject to the following restrictions:
31
+
32
+ 1. The origin of this software must not be misrepresented; you must not
33
+ claim that you wrote the original software. If you use this software
34
+ in a product, an acknowledgment in the product documentation would be
35
+ appreciated but is not required.
36
+ 2. Altered source versions must be plainly marked as such, and must not be
37
+ misrepresented as being the original software.
38
+ 3. This notice may not be removed or altered from any source distribution.
39
+
40
+ -------------------------------------------------------------------------------
41
+
42
+ ZzFXM License
43
+
44
+ The ZzFXM plugin (plugins/zzfxm.js) is a port of ZzFXM, the Zuper Zmall Zound Zynth music player.
45
+ https://github.com/keithclark/ZzFXM
46
+
47
+ Copyright (c) 2020 Keith Clark, Frank Force
48
+
49
+ Permission is hereby granted, free of charge, to any person obtaining a copy
50
+ of this software and associated documentation files (the "Software"), to deal
51
+ in the Software without restriction, including without limitation the rights
52
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
53
+ copies of the Software, and to permit persons to whom the Software is
54
+ furnished to do so, subject to the following conditions:
55
+
56
+ The above copyright notice and this permission notice shall be included in all
57
+ copies or substantial portions of the Software.
58
+
59
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
60
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
61
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
62
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
63
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
64
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
65
+ SOFTWARE.
package/EDITOR.md ADDED
@@ -0,0 +1,359 @@
1
+ # Making your own level editor with LittleJS
2
+
3
+ LittleJS has two level editors built in, one for 2D tile maps and one for 3D levels. They are meant to be a
4
+ starting point: a game adds its own object types, properties, keys, buttons and tools, and ends up with an
5
+ editor made for that one game. Everything goes through one object, `levelEditor`, and the same calls work in
6
+ both editors.
7
+
8
+ This page is written to be followed by a person or by an AI coding assistant. Every code block marked "tested"
9
+ is run by `test/editorCustom.test.mjs` straight from this file, so it works as written.
10
+
11
+ ## The rules
12
+
13
+ 1. **Everything is on `levelEditor`**, or on a class of your own that extends `LevelEditor`. It is exported
14
+ from every build, script tag and ES module alike.
15
+ 2. **No guards are needed.** In release builds `LevelEditor` is a stub: it takes what you add and never calls
16
+ it. The editor's code is only in the debug builds (`dist/littlejs.js`, `dist/littlejs.esm.js`).
17
+ 3. **The level data is the source of truth.** The editor changes the level object (3D) or the Tiled map (2D) the
18
+ game loaded, and brings the game's objects in line. Change the level through `edit3D` or `edit2D`, never the
19
+ game's objects.
20
+ 4. **An edit is a stroke.** Make changes with `edit3D.change(...)`, `edit3D.place(...)`, `edit2D.paint(...)` or
21
+ `edit2D.changeObjects(...)`, then call `strokeEnd()`: that is one undo step, autosaved. A tool's press, drag
22
+ and release are ended for you.
23
+ 5. **A type is named by a string**, never by its class, because minified builds rename classes.
24
+ 6. **Use `levelEditor.edit3D` and `levelEditor.edit2D` only inside what the editor calls**: a key's action, a
25
+ button's click, a tool's callbacks, `onUpdate`, `onDraw`. In a release build they are undefined, and none of
26
+ those are ever called.
27
+ 7. **`levelEditor.is3D`** says which editor is in use, for a game that has both kinds of level.
28
+
29
+ ## The hooks
30
+
31
+ ```javascript
32
+ levelEditor.open(); // start in the editor; close() plays on with the changes
33
+ levelEditor.isOpen; // true while editing
34
+ levelEditor.onRestart = ()=> buildLevel(); // adds a Restart button: load the level again
35
+ levelEditor.onPlayFrom = (pos)=> player.pos = pos; // adds "Play from mouse": put the player at the mouse
36
+ levelEditor.onOpen = ()=> {}; // the editor opened
37
+ levelEditor.onClose = ()=> {}; // it closed, the game plays on
38
+ levelEditor.onUpdate = ()=> {}; // each frame while it is open
39
+ levelEditor.onDraw = ()=> {}; // while it draws the level, for overlays of your own
40
+ levelEditor.onPanel = (box)=> {}; // once, when the panel is made: a box in it for your own controls
41
+ levelEditor.onSave = (text, name)=> false; // Save: return true when you kept the file yourself
42
+ levelEditor.use3D = true; // force the 3D editor (false, the 2D one); undefined picks by the level
43
+ levelEditor.onTile = (layer, pos, tile)=> {}; // 2D: a tile was painted, tile undefined when erased
44
+ levelEditor.paletteTiles = [0, 1, 10]; // 2D: the tiles the palette shows, undefined for the whole sheet
45
+ ```
46
+
47
+ Without code, `0` on the debug overlay (`Esc`) opens the editor, and `Esc` then switches between editing and
48
+ playing.
49
+
50
+ ## Your own object types, and their properties as panel inputs
51
+
52
+ A type is a name, something to make, and default properties. The editor lists the type to place, and shows an
53
+ input for each default whose value is a number, a true or false, a string, a `Color` or a vector. What the
54
+ level designer sets is saved in the level and set on the object when the level loads.
55
+
56
+ 3D, tested:
57
+
58
+ <!-- test:type3d -->
59
+ ```javascript
60
+ class Spawner extends EngineObject3D
61
+ {
62
+ // a level type's class is made with (pos3D, properties)
63
+ constructor(pos, properties)
64
+ {
65
+ super(pos, render3D.boxMesh, undefined, properties.color);
66
+ }
67
+ }
68
+ level3DAddType('Spawner', Spawner, {rate: 2, enemy: 'bat', color: hsl(0, 1, .5), active: true});
69
+ level3DAddType('PlayerStart', (pos)=> playerStart = pos); // a function, for what is not an object
70
+ ```
71
+
72
+ 2D, tested:
73
+
74
+ <!-- test:type2d -->
75
+ ```javascript
76
+ class Coin extends EngineObject
77
+ {
78
+ constructor(pos) { super(pos, vec2(1)); }
79
+ }
80
+ objectLayersAddType('Coin', Coin, {value: 1});
81
+ ```
82
+
83
+ - In 3D a class needs its own constructor that takes the position: one that passes every argument on to
84
+ `EngineObject3D` would pass the properties as its mesh.
85
+ - `level3DAddMesh('Tree', mesh, tileInfo)` is a 3D type with no class: a static prop with `color` and `solid`.
86
+ - Changing a 3D object's property in the editor makes the object again, since its constructor takes them.
87
+ - 3D prefabs, small levels placed many times, are types too: see "Prefabs" in [REFERENCE.md](REFERENCE.md). The
88
+ panel's Load prefab button adds one from a file to the level being edited.
89
+
90
+ ## A key of your own
91
+
92
+ `addKey(key, action, helpLine)`. The key is a letter or digit (`'k'`), a key's name in any case (`'Delete'`,
93
+ `'F2'`, `'Enter'`, `'Space'`), or either with `'ctrl+'` in front. The action is called with whether Shift is
94
+ held. Return `false` when it did nothing.
95
+
96
+ - A key the editor already uses is replaced by yours, with a warning in the console when the editor opens. The
97
+ two editors use different keys, so check the `?` help of the one your game uses.
98
+ - `Escape` and `0` are the editor's own, to play and to exit, and are not taken.
99
+ - Ctrl is the one modifier. For Shift, read the argument the action is given.
100
+
101
+ 3D, tested:
102
+
103
+ <!-- test:key3d -->
104
+ ```javascript
105
+ // K lifts every selected object one unit, as one undo
106
+ levelEditor.addKey('k', ()=>
107
+ {
108
+ const edit = levelEditor.edit3D;
109
+ const changed = edit.change((list)=>
110
+ {
111
+ for (const object of list)
112
+ if (edit.selection.has(object.id))
113
+ edit.setTransform(object, edit.pos(object).add(vec3(0, 1, 0)));
114
+ });
115
+ edit.strokeEnd();
116
+ return changed; // false when nothing was selected
117
+ }, 'K: lift the selection one unit');
118
+ ```
119
+
120
+ 2D, tested:
121
+
122
+ <!-- test:key2d -->
123
+ ```javascript
124
+ // T paints tile 5 under the mouse on the selected tile layer, as one undo
125
+ levelEditor.addKey('t', ()=>
126
+ {
127
+ const edit = levelEditor.edit2D;
128
+ if (!edit.hover) return false; // the mouse is over no cell
129
+ edit.paint(edit.hover, 5);
130
+ edit.strokeEnd();
131
+ }, 'T: paint tile 5 under the mouse');
132
+ ```
133
+
134
+ ## A panel button of your own
135
+
136
+ `addButton(label, onClick, title)`. Tested:
137
+
138
+ <!-- test:button3d -->
139
+ ```javascript
140
+ levelEditor.addButton('Scatter 5 spawners', ()=>
141
+ {
142
+ const edit = levelEditor.edit3D;
143
+ for (let i = 0; i < 5; ++i)
144
+ edit.place('Spawner', vec3(rand(-8, 8), .5, rand(-8, 8)));
145
+ edit.strokeEnd(); // the five are one undo
146
+ }, 'Put five spawners at random places');
147
+ ```
148
+
149
+ For anything more than a button, `onPanel` gives you a box in the panel to fill with your own HTML.
150
+
151
+ ## A tool of your own
152
+
153
+ `addTool(name, tool)` adds a tool beside the editor's own: a button in the panel by its name, and a key if you
154
+ give one. While it is on, the left button in the level is your tool's. Each callback is given
155
+ `{pos, cell, ray, shift, ctrl}`:
156
+
157
+ - `pos`: where the mouse is in the level. In 3D a `Vector3` on the level or the ground, in 2D a `Vector2`.
158
+ - `cell`: 2D, the tile cell under the mouse on the selected tile layer, or undefined.
159
+ - `ray`: 3D, the mouse's ray.
160
+
161
+ What the callbacks change is one undo: the editor ends the stroke at the release, and takes it back when the
162
+ right button or Escape ends the press. Do not call `strokeEnd` in a tool.
163
+
164
+ 3D, a tool that lays a row of posts along a drag, tested:
165
+
166
+ <!-- test:tool3d -->
167
+ ```javascript
168
+ let lastPost;
169
+ levelEditor.addTool('Posts',
170
+ {
171
+ key: 'o',
172
+ hint: 'Posts: drag along the ground to lay a row',
173
+ onPress(at)
174
+ {
175
+ if (!at.pos) return false; // not on the level
176
+ levelEditor.edit3D.place('Cylinder', lastPost = at.pos.add(vec3(0, .5, 0)));
177
+ },
178
+ onDrag(at)
179
+ {
180
+ // another post each 2 units along the drag
181
+ if (at.pos && at.pos.distance(lastPost) >= 2)
182
+ levelEditor.edit3D.place('Cylinder', lastPost = at.pos.add(vec3(0, .5, 0)));
183
+ },
184
+ });
185
+ ```
186
+
187
+ 2D, a tool that paints a tile wherever it is dragged, Shift erasing, tested:
188
+
189
+ <!-- test:tool2d -->
190
+ ```javascript
191
+ levelEditor.addTool('Water',
192
+ {
193
+ key: 'w',
194
+ hint: 'Water: drag to fill cells · Shift erases',
195
+ onPress(at) { at.cell && levelEditor.edit2D.paint(at.cell, at.shift ? -1 : 7); },
196
+ onDrag(at) { at.cell && levelEditor.edit2D.paint(at.cell, at.shift ? -1 : 7); },
197
+ onDraw(at)
198
+ {
199
+ // a cell is counted from its layer's corner, which is at the layer's pos in the world
200
+ const corner = levelEditor.edit2D.layer.pos;
201
+ at.cell && drawRect(corner.add(at.cell).add(vec2(.5)), vec2(1), hsl(.55, 1, .5, .4));
202
+ },
203
+ });
204
+ ```
205
+
206
+ A tool is put down by its key or button again, or by picking one of the editor's own tools, layers or types.
207
+ `levelEditor.tool` is the name of the one that is on.
208
+
209
+ `onDraw`, a tool's and the editor's, draws in the level as the editor does: in the 2D editor with the 2D draws
210
+ (`drawRect`, `drawLine`), and in the 3D editor inside the 3D pass, with `render3D.drawBox`, `render3D.drawLine`
211
+ and the rest.
212
+
213
+ ## An editor class of your own
214
+
215
+ Everything above can be a class instead, with the hooks as methods. Tested:
216
+
217
+ <!-- test:class -->
218
+ ```javascript
219
+ class MyEditor extends LevelEditor
220
+ {
221
+ constructor()
222
+ {
223
+ super();
224
+ this.showZones = true;
225
+ this.addKey('z', ()=> { this.showZones = !this.showZones; }, 'Z: show the spawn zones');
226
+ this.addButton('Clear spawners', ()=> this.clearSpawners());
227
+ }
228
+
229
+ // the hooks are methods
230
+ onRestart() { restarts++; }
231
+ onDraw() { this.showZones && drawnZones++; }
232
+
233
+ clearSpawners()
234
+ {
235
+ const edit = this.edit3D;
236
+ edit.change((list)=>
237
+ {
238
+ for (let i = list.length; i--;)
239
+ list[i].type === 'Spawner' && list.splice(i, 1);
240
+ });
241
+ edit.strokeEnd();
242
+ }
243
+ }
244
+ setLevelEditor(new MyEditor);
245
+ ```
246
+
247
+ - Call `setLevelEditor` before the editor opens, and use `levelEditor` itself afterwards, not a copy of it
248
+ taken earlier.
249
+ - `keys`, `buttons`, `tools` and `tool` are fields the class keeps what you added in: do not name your own
250
+ fields those.
251
+ - A method of your class is a hook the same as one you set: defining `onRestart` shows the Restart button.
252
+
253
+ ## The edit functions
254
+
255
+ ### `levelEditor.edit3D`
256
+
257
+ | Member | What it is |
258
+ | --- | --- |
259
+ | `level` | The level object being edited |
260
+ | `objects` | Its objects, the list in the level: read it, edit through `change` |
261
+ | `selection` | A `Set` of the selected objects' ids, yours to add to and delete from |
262
+ | `selected()` | The selected objects |
263
+ | `made(id)` | What the game made for an object |
264
+ | `change((list)=> {...})` | Edit a copy of the object list; false when nothing changed |
265
+ | `changePart(name, (part)=> newPart)` | Edit the level's `scene`, `voxels`, `terrain` or `prefabs` block |
266
+ | `strokeEnd()` | End the edit: one undo step, and the autosave |
267
+ | `strokeCancel()` | Take the edit being made back |
268
+ | `place(type, pos3D)` | Add an object of a type, selected; its id, or undefined when there is no such type or the level can not be edited |
269
+ | `setTransform(object, pos3D, rotation, scale3D)` | Inside `change`, on an object of its list: write a place, a rotation in degrees and a scale; leave one out to keep it |
270
+ | `setProperty(object, name, value)` | Inside `change`, on an object of its list: write a property, left out of the file when it is the type's default |
271
+ | `pos(object)`, `rotation(object)`, `scale(object)` | Read them as vectors, the rotation in degrees |
272
+ | `mousePoint()` | Where the mouse is on the level or the ground, undefined while it is over the panel |
273
+ | `undo(redo)` | Undo, or redo with true |
274
+ | `toJSON()` | The level as the text Save writes |
275
+
276
+ ### `levelEditor.edit2D`
277
+
278
+ | Member | What it is |
279
+ | --- | --- |
280
+ | `map` | The Tiled map of the selected layer |
281
+ | `layer` | The selected tile layer, a `TileLayer`, undefined on an object layer |
282
+ | `objects` | A copy of the selected object layer's objects, as Tiled has them |
283
+ | `hover` | The cell under the mouse on the selected tile layer |
284
+ | `selection` | A `Set` of the selected objects' ids |
285
+ | `paint(cell, tile, direction, mirror)` | Set a cell of the selected tile layer to a tile index, -1 erases; false off the layer |
286
+ | `changeObjects((list)=> {...})` | Edit a copy of the selected object layer's objects |
287
+ | `strokeEnd()` | End the edit: one undo step, and the autosave |
288
+ | `strokeCancel()` | Take the edit being made back |
289
+ | `bulk(()=> {...})` | Many `paint` calls with one redraw a layer |
290
+ | `undo(redo)` | Undo, or redo with true |
291
+ | `toJSON()` | The map as the text Save writes |
292
+
293
+ ## The files
294
+
295
+ - 3D: plain JSON, `{littlejs3D: 1, scene, voxels, terrain, prefabs, objects: [{id, type, pos, rotation, scale,
296
+ properties}]}`. Load with `level3DLoad(await fetchJSON('level.json'))`.
297
+ - 2D: Tiled JSON. Load with `tileLayersLoad(map)` and `objectLayersLoad(map)`. Tiled itself can open the file.
298
+ - A game that loads the same level object again, as a restart does, gets the edits.
299
+ - A 3D level's block map shows the tiles of the sheet given to `level3DVoxelSetup`, or of the game's first image;
300
+ a game that has loaded no image gets plain colored blocks.
301
+ - To keep levels somewhere of your own, a server or your own format, use `onSave`:
302
+
303
+ ```javascript
304
+ levelEditor.onSave = (text, fileName)=>
305
+ {
306
+ fetch('/levels/' + fileName, {method: 'PUT', body: text});
307
+ return true; // kept, the editor writes no file
308
+ };
309
+ ```
310
+
311
+ `onSave` may be an async function. In the 3D editor it is asked for a prefab too, when Save is pressed inside one.
312
+
313
+ ### A game opened from disk
314
+
315
+ A page opened straight from a folder, with no server, can not `fetch` a JSON file. Keep the level as a script
316
+ instead: a file that sets a variable, loaded with a script tag before the game. `onSave` writes it. Tested:
317
+
318
+ <!-- test:disklevel -->
319
+ ```javascript
320
+ // index.html has <script src="level.js"></script> before the game's own script, and level.js is one line:
321
+ // var level = {"littlejs3D": 1, "objects": [...]};
322
+ levelEditor.onSave = (text, fileName)=>
323
+ {
324
+ saveText('var level = ' + text.trim() + ';\n', 'level.js', 'text/javascript');
325
+ return true; // kept, the editor writes no JSON file
326
+ };
327
+ level3DLoad(level); // the same object the script made, so the editor's changes are the game's
328
+ ```
329
+
330
+ Save then downloads `level.js`, to put in place of the old one. The autosave still brings unsaved edits back
331
+ after a reload.
332
+
333
+ ## A checklist for a custom editor
334
+
335
+ 1. Register the game's types with their defaults, before the level loads.
336
+ 2. Set `onRestart` and `onPlayFrom`, so Restart and Play from mouse work.
337
+ 3. Add keys and buttons for what the game's designers do most, each ending its stroke.
338
+ 4. Add a tool for what is done with the mouse in the level: paths, zones, links between objects.
339
+ 5. Draw what the level does not show by itself in `onDraw`: zones, ranges, links.
340
+ 6. Ship the release build: the editor and everything you added to it are gone.
341
+
342
+ ## Where the working examples are
343
+
344
+ - `examples/shorts/render3dLevelEditor.js`: a 3D level with its own Coin and PlayerStart types and both hooks.
345
+ - `examples/shorts/render3dPrefab.js`: the 3D editor used as a prefab maker.
346
+ - `examples/shorts/levelEditor.js` and `examples/platformer/`: the 2D editor with a game's own types, palette
347
+ and hooks.
348
+
349
+ ## What it does not do
350
+
351
+ - **It does not replace the editor's own tools.** Your keys, buttons and tools are added beside them; how the
352
+ built-in Move tool snaps, or how painting works, is not something a game changes. That takes an engine build
353
+ of your own: get the LittleJS source at the version you use, edit `src/engineEditor.js` or
354
+ `plugins/render3dEditor.js`, run `npm run build`, and use the `dist/littlejs.js` it makes. A changed copy
355
+ of the file can not be loaded beside the engine, the debug build has the editor in it already, and a build of
356
+ your own has to be made again for each engine update.
357
+ - **A tool has no handles of its own.** It has the press, the drag and the release, and it can draw.
358
+ - **The editors' inner functions**, the ones named `editor...` and `editor3D...`, are not part of this. They can
359
+ be reached in a script-tag debug build, but they change between versions.
package/README.md CHANGED
@@ -40,7 +40,8 @@ The code is very clean and well documented with many examples to get you started
40
40
  - Blazing fast WebGL2 + Canvas2D hybrid rendering system
41
41
  - Apply [Shadertoy](https://www.shadertoy.com) style shaders for post-processing effects, or on any object or draw
42
42
  - 2D lights with soft shadows, tinted glass and emissive objects
43
- - Robust particle effect system and [effect design tool](https://killedbyapixel.github.io/LittleJS/examples/particles/)
43
+ - Robust particle effect system with a library of ready made effects and an [effect design tool](https://killedbyapixel.github.io/LittleJS/examples/particles/)
44
+ - Parallax background layers that repeat across a level of any size
44
45
  - Load sprites and animations into texture sheets at runtime, or import [TexturePacker](https://www.codeandweb.com/texturepacker) and [Aseprite](https://www.aseprite.org) atlases
45
46
 
46
47
  ### 🧊 LittleJS 3D
@@ -48,8 +49,9 @@ The code is very clean and well documented with many examples to get you started
48
49
  - Built-in 3D renderer that shares the canvas with your 2D game
49
50
  - 3D objects with the same physics, children and timers as 2D
50
51
  - Shape builders, extruded sprites and text, and OBJ and glTF model loading
51
- - Height map terrain with collision and raycasts
52
- - Shadow maps, colored lights, specular, emissive glow and fog
52
+ - Height map terrain and voxel maps with collision and raycasts
53
+ - 3D levels and prefabs, built in the 3D level editor and loaded with one call
54
+ - Shadow maps, colored lights, spotlights, lens flares, specular, emissive glow and fog
53
55
  - Particles, trails, billboards and instanced drawing
54
56
  - Orbit, chase and first person cameras with mouse picking
55
57
  - Custom shaders on any object
@@ -66,6 +68,7 @@ The code is very clean and well documented with many examples to get you started
66
68
 
67
69
  - Comprehensive input handling for mouse, keyboard, gamepad, and touch
68
70
  - Customizable on screen gamepad designed for mobile devices
71
+ - Pinch to zoom on touch screens, read the same way as the mouse wheel
69
72
 
70
73
  ### 💥 Physics
71
74
 
@@ -85,9 +88,11 @@ The code is very clean and well documented with many examples to get you started
85
88
  ### 🛠️ Developer Tools
86
89
 
87
90
  - Live example browser with code editor
88
- - Import level editor data from [Tiled](https://github.com/mapeditor/tiled) or other JSON
91
+ - Built-in 2D and 3D level editors that edit your game while it runs, with undo, autosave and prefabs
92
+ - Make the editors your own with your game's own object types, keys, buttons and tools, see the [editor guide](https://github.com/KilledByAPixel/LittleJS/blob/main/EDITOR.md)
93
+ - 2D levels load and save as [Tiled](https://github.com/mapeditor/tiled) JSON, so Tiled can edit them too
89
94
  - UI system with buttons, sliders, text input and nine-slice skins
90
- - Tween system with easing curves
95
+ - Tween system with easing curves, and a scene system for titles, menus and game states
91
96
  - Debug overlay and primitive rendering system
92
97
  - Medal tracking system with [Newgrounds](https://www.newgrounds.com/) support
93
98
  - Node.js build system
@@ -146,7 +151,7 @@ LittleJS comes with several demos both for learning and using as starter project
146
151
 
147
152
  - [Example Browser](https://killedbyapixel.github.io/LittleJS/examples/) - Live example browser with all examples and editable source
148
153
  - [LittleJS Arcade](https://killedbyapixel.github.io/LittleJSArcade/) - Over 50 example games you can use as starter projects
149
- - [Short Examples](https://github.com/KilledByAPixel/LittleJS/tree/main/examples/shorts) - 80+ single-file demos showing off individual engine features
154
+ - [Short Examples](https://github.com/KilledByAPixel/LittleJS/tree/main/examples/shorts) - 100+ single-file demos showing off individual engine features
150
155
  - [Breakout](https://killedbyapixel.github.io/LittleJS/examples/breakout/) - Block breaking game with post-processing effects
151
156
  - [Puzzle Game](https://killedbyapixel.github.io/LittleJS/examples/puzzle/) - Match 3 puzzle game with HD rendering and high score tracking
152
157
  - [Platformer](https://killedbyapixel.github.io/LittleJS/examples/platformer/) - Platformer/shooter demo that loads level data