littlejsengine 1.23.0 → 1.24.0
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/CHANGELOG.md +234 -0
- package/COPYRIGHT.txt +38 -38
- package/EDITOR.md +359 -0
- package/FAQ.md +4 -9
- package/README.md +14 -8
- package/REFERENCE.md +2327 -0
- package/dist/littlejs.d.ts +149 -104
- package/dist/littlejs.esm.js +1652 -1332
- package/dist/littlejs.esm.min.js +3 -3
- package/dist/littlejs.js +1651 -1328
- package/dist/littlejs.min.js +2 -2
- package/dist/littlejs.release.js +1553 -1294
- package/package.json +5 -3
- package/plugins/gltf.js +29 -13
- package/plugins/pluginExport.js +0 -4
- package/plugins/postProcess.js +5 -2
- package/plugins/render3d.js +136 -31
- package/plugins/render3dEditor.js +51 -17
- package/plugins/render3dExtras.js +166 -70
- package/plugins/render3dLevel.js +52 -6
- package/plugins/textureSheet.js +1 -0
- package/plugins/tweenSystem.js +39 -34
- package/plugins/uiSystem.js +7 -3
- package/src/engine.js +1 -1
- package/src/engineAudio.js +9 -3
- package/src/engineBuild.mjs +17 -3
- package/src/engineDraw.js +11 -7
- package/src/engineEditor.js +52 -18
- package/src/engineExport.js +1 -0
- package/src/engineObject.js +675 -675
- package/src/engineRelease.js +5 -1
- package/src/engineTileLayer.js +119 -1
- package/src/engineUtilities.js +291 -291
- package/src/engineWebGL.js +12 -3
- package/plugins/zzfxm.js +0 -160
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/FAQ.md
CHANGED
|
@@ -261,7 +261,6 @@ Plugins are self-contained features that live alongside the engine but aren't pa
|
|
|
261
261
|
| `tweakables.js` | A debug panel to change values live while the game runs, press 9 with the debug overlay open |
|
|
262
262
|
| `medalSystem.js` | Achievement / medal tracking with popup notifications |
|
|
263
263
|
| `newgrounds.js` | Newgrounds.io integration (medals held on the server, scoreboards) |
|
|
264
|
-
| `zzfxm.js` | Procedural chiptune music via the `ZzFXMusic` class |
|
|
265
264
|
| `audioEffects.js` | Web Audio effects like filter, reverb, delay, distortion and compressor, for a group of sounds or everything |
|
|
266
265
|
| `drawUtilities.js` | Higher-level drawing helpers like nine-slice and three-slice |
|
|
267
266
|
|
|
@@ -365,7 +364,7 @@ LittleJS has a dedicated [js13k branch](https://github.com/KilledByAPixel/Little
|
|
|
365
364
|
|
|
366
365
|
- **Use Canvas2D instead of WebGL**: call `setGLEnable(false)` before `engineInit`. Stripping the WebGL renderer saves a significant chunk of bytes, and for most pixel-art and low-sprite-count games the visual difference is imperceptible. A nice side effect: once the jam is over, you can flip WebGL back on with a single setting change for a big performance boost when publishing the game elsewhere.
|
|
367
366
|
- **Drop unused plugins**: every plugin is self-contained, so only include what you actually use. Skip `uiSystem.js`, `box2d.js`, `pathFinder.js`, etc. unless you need them.
|
|
368
|
-
- **Lean on built-ins to avoid asset files**: `Sound` with ZzFX parameters generates sound effects from a tiny array,
|
|
367
|
+
- **Lean on built-ins to avoid asset files**: `Sound` with ZzFX parameters generates sound effects from a tiny array, and built-in shape drawing avoids shipping image assets.
|
|
369
368
|
- **Use a packer**: the js13k branch is set up to compress the final bundle with Roadroller or a similar packer. Check the branch's build script for the current toolchain.
|
|
370
369
|
|
|
371
370
|
For normal production (non-13KB) builds, the engine ships `dist/littlejs.release.js` (asserts stripped) and `dist/littlejs.min.js` (minified) — both well under 100KB.
|
|
@@ -538,19 +537,15 @@ sound_click.play(pos, volume, pitch); // play a sound
|
|
|
538
537
|
|
|
539
538
|
### How do I play music?
|
|
540
539
|
|
|
541
|
-
|
|
540
|
+
Load an mp3 or ogg file with the regular `Sound` class and play it with `playMusic`, which loops by default.
|
|
542
541
|
|
|
543
542
|
```javascript
|
|
544
|
-
//
|
|
545
|
-
const song = new ZzFXMusic(zzfxmSongData);
|
|
546
|
-
const songInstance = song.playMusic();
|
|
547
|
-
|
|
548
|
-
// or load an mp3/ogg and loop it
|
|
543
|
+
// load an mp3/ogg and loop it
|
|
549
544
|
const track = new Sound('music.mp3');
|
|
550
545
|
const trackInstance = track.playMusic(1, true); // volume, loop
|
|
551
546
|
```
|
|
552
547
|
|
|
553
|
-
|
|
548
|
+
Music pauses automatically when the tab loses focus (audio context suspends) and resumes when it comes back. `playMusic` returns a `SoundInstance`; keep it and call `trackInstance.stop()` to halt playback.
|
|
554
549
|
|
|
555
550
|
### Why doesn't my audio play until the user clicks something?
|
|
556
551
|
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ The code is very clean and well documented with many examples to get you started
|
|
|
25
25
|
|
|
26
26
|
<div align='center' markdown='1'>
|
|
27
27
|
|
|
28
|
-
## [Demos](https://killedbyapixel.github.io/LittleJS/examples) | [Arcade](https://killedbyapixel.github.io/LittleJSArcade) | [Docs](https://killedbyapixel.github.io/LittleJS/docs) | [FAQ](https://github.com/KilledByAPixel/LittleJS/blob/main/FAQ.md) | [Trailer](https://youtu.be/chuBzGjv7Ms) | [Discord](https://discord.gg/zb7hcGkyZe) | [AI](https://github.com/KilledByAPixel/LittleJS-AI)
|
|
28
|
+
## [Demos](https://killedbyapixel.github.io/LittleJS/examples) | [Arcade](https://killedbyapixel.github.io/LittleJSArcade) | [Docs](https://killedbyapixel.github.io/LittleJS/docs) | [Reference](https://github.com/KilledByAPixel/LittleJS/blob/main/REFERENCE.md) | [FAQ](https://github.com/KilledByAPixel/LittleJS/blob/main/FAQ.md) | [Trailer](https://youtu.be/chuBzGjv7Ms) | [Discord](https://discord.gg/zb7hcGkyZe) | [AI](https://github.com/KilledByAPixel/LittleJS-AI)
|
|
29
29
|
|
|
30
30
|
</div>
|
|
31
31
|
|
|
@@ -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
|
-
-
|
|
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
|
-
-
|
|
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
|
|
@@ -135,9 +140,10 @@ engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost);
|
|
|
135
140
|
- [Making Awesome Games With LittleJS](https://youtu.be/_dXKU0WgAj8) - A short talk about LittleJS with some tips on how to use it
|
|
136
141
|
- [Tutorial: Breakout](https://github.com/KilledByAPixel/LittleJS/tree/main/examples/breakoutTutorial) - Learn how to make a simple game from scratch
|
|
137
142
|
- [Tutorial: Make a ski game](https://eoinmcgrath.com/little-ski/tutorial.html) - A tutorial by eoinmcg that shows how to make a pixel art style game
|
|
138
|
-
- [LittleJS Quick Reference Sheet](https://github.com/KilledByAPixel/LittleJS/blob/main/REFERENCE.md) -
|
|
143
|
+
- [LittleJS Quick Reference Sheet](https://github.com/KilledByAPixel/LittleJS/blob/main/REFERENCE.md) - The whole API in one file, made to be read by people and by AI coding tools
|
|
139
144
|
- [Making Your Own Level Editor](https://github.com/KilledByAPixel/LittleJS/blob/main/EDITOR.md) - How to turn the built in 2D and 3D level editors into an editor for your own game
|
|
140
145
|
- [LittleJS FAQ](https://github.com/KilledByAPixel/LittleJS/blob/main/FAQ.md) - Answers to common questions about LittleJS
|
|
146
|
+
- [Changelog](https://github.com/KilledByAPixel/LittleJS/blob/main/CHANGELOG.md) - What changed in each release
|
|
141
147
|
- [JS13k Branch](https://github.com/KilledByAPixel/LittleJS/tree/js13k) - A special branch for size coding events that builds to a 7KB zip
|
|
142
148
|
|
|
143
149
|
## Examples
|
|
@@ -146,7 +152,7 @@ LittleJS comes with several demos both for learning and using as starter project
|
|
|
146
152
|
|
|
147
153
|
- [Example Browser](https://killedbyapixel.github.io/LittleJS/examples/) - Live example browser with all examples and editable source
|
|
148
154
|
- [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) -
|
|
155
|
+
- [Short Examples](https://github.com/KilledByAPixel/LittleJS/tree/main/examples/shorts) - 100+ single-file demos showing off individual engine features
|
|
150
156
|
- [Breakout](https://killedbyapixel.github.io/LittleJS/examples/breakout/) - Block breaking game with post-processing effects
|
|
151
157
|
- [Puzzle Game](https://killedbyapixel.github.io/LittleJS/examples/puzzle/) - Match 3 puzzle game with HD rendering and high score tracking
|
|
152
158
|
- [Platformer](https://killedbyapixel.github.io/LittleJS/examples/platformer/) - Platformer/shooter demo that loads level data
|