littlejsengine 1.24.0 → 1.24.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/CHANGELOG.md +28 -1
- package/README.md +1 -1
- package/REFERENCE.md +7 -4
- package/dist/littlejs.d.ts +29 -18
- package/dist/littlejs.esm.js +117 -53
- package/dist/littlejs.esm.min.js +3 -3
- package/dist/littlejs.js +117 -53
- package/dist/littlejs.min.js +2 -2
- package/dist/littlejs.release.js +96 -45
- package/package.json +1 -1
- package/plugins/box2d.js +1 -1
- package/plugins/render3d.js +3 -2
- package/plugins/render3dEditor.js +4 -2
- package/plugins/render3dExtras.js +9 -6
- package/plugins/render3dVoxels.js +15 -8
- package/src/engine.js +1 -1
- package/src/engineEditor.js +17 -6
- package/src/engineSettings.js +5 -1
- package/src/engineTileLayer.js +62 -26
package/src/engineTileLayer.js
CHANGED
|
@@ -141,7 +141,7 @@ const tileLayersTiledFlips = [[0,0], [3,1], [2,1], [3,0], [0,1], [1,0], [2,0], [
|
|
|
141
141
|
* @param {Object} tileMapData - Level data from exported data
|
|
142
142
|
* @param {TileInfo} [tileInfo] - Default tile info (used for size and texture), tile() by default, none when no image is loaded
|
|
143
143
|
* @param {number} [renderOrder] - Render order of the top layer
|
|
144
|
-
* @param {number}
|
|
144
|
+
* @param {number|string} [collisionLayer] - Layer to use for collision if any, by its index or its name
|
|
145
145
|
* @param {boolean} [draw] - Should the layer be drawn automatically
|
|
146
146
|
* @return {Array<TileCollisionLayer>}
|
|
147
147
|
* @memberof TileLayers */
|
|
@@ -173,7 +173,8 @@ function tileLayersLoad(tileMapData, tileInfo=tileLayerDefaultTile(), renderOrde
|
|
|
173
173
|
const margin = tileset.margin || 0, spacing = tileset.spacing || 0, size = tileInfo.size;
|
|
174
174
|
const width = tileInfo.textureInfo?.size.x || 0;
|
|
175
175
|
const columns = tileset.columns || max(1, floor((width - margin*2 + spacing) / (size.x + spacing)));
|
|
176
|
-
|
|
176
|
+
const origin = tileInfo.pos.subtract(vec2(tileInfo.padding)); // where the sheet starts, in an atlas too
|
|
177
|
+
tileInfo = new TileInfo(origin.add(vec2(margin)), size, tileInfo.textureInfo, spacing / 2, tileInfo.bleed, columns);
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
// flatten group layers in order, a group's color and visibility carry to the layers inside it
|
|
@@ -204,6 +205,14 @@ function tileLayersLoad(tileMapData, tileInfo=tileLayerDefaultTile(), renderOrde
|
|
|
204
205
|
};
|
|
205
206
|
addLayers(tileMapData.layers, WHITE, true);
|
|
206
207
|
|
|
208
|
+
// a layer named for collision is the first tile layer of that name, which stays the same when layers are added
|
|
209
|
+
if (typeof collisionLayer === 'string')
|
|
210
|
+
{
|
|
211
|
+
const name = collisionLayer;
|
|
212
|
+
collisionLayer = layers.findIndex((l)=> l.color && l.dataLayer.name === name);
|
|
213
|
+
ASSERT(collisionLayer >= 0, 'no tile layer named ' + name);
|
|
214
|
+
}
|
|
215
|
+
|
|
207
216
|
// create tile layers and fill with data
|
|
208
217
|
const tileLayers = [];
|
|
209
218
|
const levelSize = vec2(tileMapData.width, tileMapData.height);
|
|
@@ -259,31 +268,38 @@ const tileLayersLDtkTypes = {Int: 'int', Float: 'float', Bool: 'bool', String: '
|
|
|
259
268
|
|
|
260
269
|
/**
|
|
261
270
|
* Make a Tiled map of a level of an LDtk project, to load with tileLayersLoad and objectLayersLoad
|
|
262
|
-
* - Each Tiles, AutoLayer and IntGrid layer is a tile layer, the bottom one first as in Tiled
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
* - An IntGrid layer
|
|
271
|
+
* - Each Tiles, AutoLayer and IntGrid layer is a tile layer, the bottom one first as in Tiled; where LDtk stacks
|
|
272
|
+
* tiles in a cell, an edge over a fill, each tile over another goes in a layer of its own just above, named with
|
|
273
|
+
* (2), (3) and so on, and a tile LDtk draws see-through goes in one of its own with that opacity, times the
|
|
274
|
+
* layer's, named with it, like Ground .25
|
|
275
|
+
* - An IntGrid layer is a hidden layer of its values under its own name, for collision, whatever tiles its rules
|
|
276
|
+
* make, which are layers over it named with tiles, like Collisions tiles; pass the name as collisionLayer, since
|
|
277
|
+
* the stacked and see-through layers change the indices from level to level:
|
|
278
|
+
* tileLayersLoad(map, tile(0, 16), 0, 'Collisions')
|
|
267
279
|
* - An Entities layer is an object layer: an entity's name is its type for objectLayersAddType, it is placed at
|
|
268
280
|
* its middle, and its Int, Float, Bool, String, Color and FilePath fields are its properties (an enum is a string)
|
|
269
281
|
* - The tileset is the first tile layer's, with its padding and spacing; give tileLayersLoad a tile info of its image
|
|
270
282
|
* - The level is in the project file (not saved as separate level files), its layers of one grid size; a layer of
|
|
271
|
-
* another grid size or another tileset is left out, with a warning in debug builds
|
|
283
|
+
* another grid size or another tileset is left out, with a warning in debug builds, and a project with no tileset
|
|
284
|
+
* keeps its tiles for the tile info the game gives; a project of several worlds has its levels counted in order
|
|
285
|
+
* - Layer offsets are not read
|
|
272
286
|
* - The level editor edits the map this returns, and saves it as a Tiled map
|
|
273
287
|
* @param {Object} ldtk - The LDtk project, its JSON
|
|
274
288
|
* @param {number|string} [level] - Which level, by its index or its identifier
|
|
275
289
|
* @return {Object} - A Tiled map: width, height, tilewidth, tileheight, tilesets and layers
|
|
276
290
|
* @example
|
|
277
291
|
* const map = tileLayersFromLDtk(await fetchJSON('world.ldtk'), 'Level_0');
|
|
278
|
-
* const layers = tileLayersLoad(map, tile(0, 16), 0,
|
|
292
|
+
* const layers = tileLayersLoad(map, tile(0, 16), 0, 'Collisions'); // its IntGrid layer is solid
|
|
279
293
|
* objectLayersLoad(map);
|
|
280
294
|
* @memberof TileLayers */
|
|
281
295
|
function tileLayersFromLDtk(ldtk, level=0)
|
|
282
296
|
{
|
|
283
|
-
const levels = ldtk?.levels || [];
|
|
297
|
+
const levels = ldtk?.levels?.length ? ldtk.levels : (ldtk?.worlds || []).flatMap((w)=> w.levels || []);
|
|
284
298
|
const data = typeof level === 'string' ? levels.find((l)=> l.identifier === level) : levels[level];
|
|
285
|
-
|
|
286
|
-
|
|
299
|
+
if (!data)
|
|
300
|
+
throw new Error('tileLayersFromLDtk: no level ' + level);
|
|
301
|
+
if (!isArray(data.layerInstances))
|
|
302
|
+
throw new Error('tileLayersFromLDtk: level ' + data.identifier + ' has no layers, levels saved as separate files are not read');
|
|
287
303
|
const instances = data.layerInstances;
|
|
288
304
|
const tiled = (l)=> l.__type !== 'Entities';
|
|
289
305
|
const first = instances.find((l)=> tiled(l) && l.__tilesetDefUid != undefined) || instances.find(tiled) || instances[0];
|
|
@@ -323,33 +339,53 @@ function tileLayersFromLDtk(ldtk, level=0)
|
|
|
323
339
|
continue;
|
|
324
340
|
}
|
|
325
341
|
const tiles = [...(instance.autoLayerTiles || []), ...(instance.gridTiles || [])];
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
if (!
|
|
342
|
+
const ownTiles = !tiles.length || !tileset ||
|
|
343
|
+
instance.__tilesetDefUid === tileset.uid && tileset.tileGridSize === grid;
|
|
344
|
+
if (instance.__gridSize !== grid || instance.__cWid !== width || instance.__cHei !== height || !ownTiles)
|
|
329
345
|
{
|
|
330
346
|
debug && console.warn(`tileLayersFromLDtk: layer ${name} has another grid or tileset, left out`);
|
|
331
347
|
continue;
|
|
332
348
|
}
|
|
333
|
-
|
|
334
|
-
|
|
349
|
+
// an IntGrid layer's values, hidden, under its own name, for collision whatever its tiles do
|
|
350
|
+
const empty = ()=> new Array(width * height).fill(0), opacity = instance.__opacity ?? 1;
|
|
351
|
+
const values = instance.__type === 'IntGrid';
|
|
352
|
+
if (values)
|
|
353
|
+
{
|
|
354
|
+
const data = empty();
|
|
355
|
+
(instance.intGridCsv || []).forEach((value, k)=> k < data.length && (data[k] = value));
|
|
356
|
+
map.layers.push({id, name, type: 'tilelayer', width, height, data, opacity, visible: false, x: 0, y: 0});
|
|
357
|
+
if (!tiles.length) continue;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// a layer for each depth in a cell and each opacity, the first tiles drawn solid first
|
|
361
|
+
const tilesName = values ? name + ' tiles' : name;
|
|
362
|
+
const stacks = [{depth: 0, alpha: 1, data: empty()}], stack = (depth, alpha)=>
|
|
363
|
+
{
|
|
364
|
+
let s = stacks.find((s)=> s.depth === depth && s.alpha === alpha);
|
|
365
|
+
s || stacks.push(s = {depth, alpha, data: empty()});
|
|
366
|
+
return s.data;
|
|
367
|
+
};
|
|
335
368
|
{
|
|
336
|
-
// a tile's place in pixels, its tile in the sheet,
|
|
337
|
-
// Tiled's two top bits; LDtk lists them in the order it draws them, so the first in a
|
|
338
|
-
// bottom one and each after it goes a layer higher
|
|
369
|
+
// a tile's place in pixels, its tile in the sheet, its flips, bit 0 across and bit 1 down, which are
|
|
370
|
+
// Tiled's two top bits, and its opacity; LDtk lists them in the order it draws them, so the first in a
|
|
371
|
+
// cell is the bottom one and each after it goes a layer higher
|
|
339
372
|
const depth = new Uint16Array(width * height);
|
|
340
373
|
for (const t of tiles)
|
|
341
374
|
{
|
|
342
375
|
const x = floor(t.px[0] / grid), y = floor(t.px[1] / grid), cell = x + y * width;
|
|
343
376
|
if (x >= 0 && x < width && y >= 0 && y < height)
|
|
344
|
-
(
|
|
377
|
+
stack(depth[cell]++, round(clamp(t.a ?? 1) * 1e3) / 1e3)[cell] =
|
|
345
378
|
(t.t + 1 | (t.f & 1 ? 0x80000000 : 0) | (t.f & 2 ? 0x40000000 : 0)) >>> 0;
|
|
346
379
|
}
|
|
347
380
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
381
|
+
const visible = instance.visible !== false;
|
|
382
|
+
stacks.sort((a, b)=> a.depth - b.depth || b.alpha - a.alpha);
|
|
383
|
+
for (const {depth, alpha, data} of stacks)
|
|
384
|
+
{
|
|
385
|
+
const layerName = tilesName + (depth ? ` (${depth + 1})` : '') + (alpha < 1 ? ' ' + String(alpha).replace(/^0/, '') : '');
|
|
386
|
+
map.layers.push({id: values || depth || alpha < 1 ? map.nextlayerid++ : id, name: layerName, type: 'tilelayer',
|
|
387
|
+
width, height, data, opacity: opacity * alpha, visible, x: 0, y: 0});
|
|
388
|
+
}
|
|
353
389
|
}
|
|
354
390
|
return map;
|
|
355
391
|
}
|