littlejsengine 1.18.22 → 1.18.24

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.
@@ -35,7 +35,7 @@ const engineName = 'LittleJS';
35
35
  * @type {string}
36
36
  * @default
37
37
  * @memberof Engine */
38
- const engineVersion = '1.18.22';
38
+ const engineVersion = '1.18.24';
39
39
 
40
40
  /** Frames per second to update
41
41
  * @type {number}
@@ -14307,13 +14307,23 @@ class TextureSheet
14307
14307
  * @param {Vector2} imageSize - Size of the source image in pixels
14308
14308
  * @param {Vector2} [frameSize] - Size of each frame, or the whole image if not passed
14309
14309
  * @param {number} [padding] - How many pixels padding around each frame
14310
+ * @param {number|Vector2} [sourcePadding] - How many pixels padding around each frame in the source image
14310
14311
  * @return {TileInfo} Tile for the packed image, or undefined if the sheet is full */
14311
- tryAdd(imageSize, frameSize=imageSize, padding=textureSheetPadding)
14312
+ tryAdd(imageSize, frameSize=imageSize, padding=textureSheetPadding, sourcePadding=0)
14312
14313
  {
14313
14314
  ASSERT(isVector2(imageSize) && isVector2(frameSize), 'sizes must be vec2');
14314
14315
  ASSERT(frameSize.x > 0 && frameSize.y > 0, 'frame size must be positive');
14315
- ASSERT(imageSize.x % frameSize.x === 0 && imageSize.y % frameSize.y === 0,
14316
- 'image size must be a multiple of the frame size');
14316
+
14317
+ if (isNumber(sourcePadding))
14318
+ sourcePadding = vec2(sourcePadding);
14319
+ ASSERT(isVector2(sourcePadding) && sourcePadding.x >= 0 && sourcePadding.y >= 0,
14320
+ 'sourcePadding must be a number or vec2 >= 0');
14321
+
14322
+ // the source may have its own padding baked in around each frame
14323
+ const sourceCellWidth = frameSize.x + sourcePadding.x*2;
14324
+ const sourceCellHeight = frameSize.y + sourcePadding.y*2;
14325
+ ASSERT(imageSize.x % sourceCellWidth === 0 && imageSize.y % sourceCellHeight === 0,
14326
+ 'image size must be a multiple of the padded frame size');
14317
14327
 
14318
14328
  const cellWidth = frameSize.x + padding*2;
14319
14329
  const cellHeight = frameSize.y + padding*2;
@@ -14322,8 +14332,8 @@ class TextureSheet
14322
14332
 
14323
14333
  // keep the layout of the source image, but narrow it if a row is too wide
14324
14334
  // frames wrap down to the next row, which TileInfo.frame handles via columns
14325
- const sourceColumns = imageSize.x / frameSize.x;
14326
- const frameCount = sourceColumns * (imageSize.y / frameSize.y);
14335
+ const sourceColumns = imageSize.x / sourceCellWidth;
14336
+ const frameCount = sourceColumns * (imageSize.y / sourceCellHeight);
14327
14337
  const columns = min(sourceColumns, maxColumns);
14328
14338
  const blockWidth = columns * cellWidth;
14329
14339
  const blockHeight = ceil(frameCount / columns) * cellHeight;
@@ -14352,23 +14362,29 @@ class TextureSheet
14352
14362
  /** Draw an image into this sheet at a tile returned by tryAdd
14353
14363
  * @param {HTMLImageElement} image - Source image to copy from
14354
14364
  * @param {TileInfo} tileInfo - Where to put it, from tryAdd
14355
- * @param {boolean} [update] - Upload to webgl now, pass false when batching */
14356
- drawImage(image, tileInfo, update=true)
14365
+ * @param {boolean} [update] - Upload to webgl now, pass false when batching
14366
+ * @param {number|Vector2} [sourcePadding] - How many pixels padding around each frame in the source image */
14367
+ drawImage(image, tileInfo, update=true, sourcePadding=0)
14357
14368
  {
14358
14369
  ASSERT(!!this.context, 'texture sheet has no canvas');
14359
14370
 
14371
+ if (isNumber(sourcePadding))
14372
+ sourcePadding = vec2(sourcePadding);
14373
+
14360
14374
  // copy frames in order, reading the source left to right, top to bottom
14361
14375
  // the destination wraps at tileInfo.columns which may be narrower than the source
14362
14376
  const frameSize = tileInfo.size;
14363
- const sourceColumns = image.width / frameSize.x;
14364
- const frameCount = sourceColumns * (image.height / frameSize.y);
14377
+ const sourceCellWidth = frameSize.x + sourcePadding.x*2;
14378
+ const sourceCellHeight = frameSize.y + sourcePadding.y*2;
14379
+ const sourceColumns = image.width / sourceCellWidth;
14380
+ const frameCount = sourceColumns * (image.height / sourceCellHeight);
14365
14381
  const columns = tileInfo.columns || frameCount;
14366
14382
  const cellWidth = frameSize.x + tileInfo.padding*2;
14367
14383
  const cellHeight = frameSize.y + tileInfo.padding*2;
14368
14384
  for (let i = frameCount; i--;)
14369
14385
  {
14370
- const sourceX = (i % sourceColumns) * frameSize.x;
14371
- const sourceY = (i / sourceColumns | 0) * frameSize.y;
14386
+ const sourceX = (i % sourceColumns) * sourceCellWidth + sourcePadding.x;
14387
+ const sourceY = (i / sourceColumns | 0) * sourceCellHeight + sourcePadding.y;
14372
14388
  this.context.drawImage(image,
14373
14389
  sourceX, sourceY, frameSize.x, frameSize.y,
14374
14390
  tileInfo.pos.x + (i % columns) * cellWidth,
@@ -14398,19 +14414,22 @@ class TextureSheet
14398
14414
  * - Nothing is visible until it loads, use spritesReady to wait for it
14399
14415
  * - Pass frameSize for animations, then step through them with TileInfo.frame
14400
14416
  * - Grid images keep their layout and frames wrap down to the next row
14417
+ * - Pass sourcePadding if the source image has padding baked in around frames
14401
14418
  * @param {string} src - Image source path
14402
14419
  * @param {Vector2|number} [frameSize] - Size of each animation frame in pixels
14403
14420
  * @param {number} [padding] - How many pixels padding around each frame
14421
+ * @param {number|Vector2} [sourcePadding] - How many pixels padding around each frame in the source image
14404
14422
  * @return {TileInfo}
14405
14423
  * @example
14406
14424
  * const playerTile = loadSprite('player.png'); // a single sprite
14407
14425
  * const runTile = loadSprite('run.png', vec2(16)); // a 16x16 frame animation
14408
14426
  * @memberof TextureSheets */
14409
- function loadSprite(src, frameSize, padding=textureSheetPadding)
14427
+ function loadSprite(src, frameSize, padding=textureSheetPadding, sourcePadding=0)
14410
14428
  {
14411
14429
  ASSERT(isStringLike(src), 'image src must be a string');
14412
14430
  ASSERT(!frameSize || isVector2(frameSize) || isNumber(frameSize), 'frameSize must be a vec2 or number');
14413
14431
  ASSERT(isNumber(padding), 'padding must be a number');
14432
+ ASSERT(isNumber(sourcePadding) || isVector2(sourcePadding), 'sourcePadding must be a number or vec2');
14414
14433
 
14415
14434
  if (isNumber(frameSize))
14416
14435
  frameSize = vec2(frameSize);
@@ -14441,9 +14460,9 @@ function loadSprite(src, frameSize, padding=textureSheetPadding)
14441
14460
  // pack onto a sheet, then fill in the tile that was already handed out,
14442
14461
  // copying every field so nothing is missed if TileInfo gains more of them
14443
14462
  const imageSize = vec2(image.width, image.height);
14444
- const {sheet, tile} = textureSheetAdd(imageSize, frameSize, padding);
14463
+ const {sheet, tile} = textureSheetAdd(imageSize, frameSize, padding, sourcePadding);
14445
14464
  Object.assign(tileInfo, tile);
14446
- sheet.drawImage(image, tileInfo, false); // upload once per batch below
14465
+ sheet.drawImage(image, tileInfo, false, sourcePadding); // upload once per batch below
14447
14466
  }
14448
14467
  else
14449
14468
  {
@@ -14644,16 +14663,16 @@ function textureSheetCreate()
14644
14663
  }
14645
14664
 
14646
14665
  // use the first sheet with enough space, or make a new one
14647
- function textureSheetAdd(imageSize, frameSize, padding)
14666
+ function textureSheetAdd(imageSize, frameSize, padding, sourcePadding)
14648
14667
  {
14649
14668
  let sheet, tile;
14650
14669
  for (sheet of textureSheets)
14651
- if (tile = sheet.tryAdd(imageSize, frameSize, padding))
14670
+ if (tile = sheet.tryAdd(imageSize, frameSize, padding, sourcePadding))
14652
14671
  break;
14653
14672
  if (!tile)
14654
14673
  {
14655
14674
  sheet = textureSheetCreate();
14656
- tile = sheet.tryAdd(imageSize, frameSize, padding);
14675
+ tile = sheet.tryAdd(imageSize, frameSize, padding, sourcePadding);
14657
14676
  ASSERT(!!tile, 'image is too large to fit on a texture sheet');
14658
14677
  }
14659
14678
  return {sheet, tile};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.18.22",
3
+ "version": "1.18.24",
4
4
  "description": "LittleJS - Tiny and Fast HTML5 Game Engine",
5
5
  "main": "dist/littlejs.esm.js",
6
6
  "types": "dist/littlejs.d.ts",
@@ -73,13 +73,23 @@ class TextureSheet
73
73
  * @param {Vector2} imageSize - Size of the source image in pixels
74
74
  * @param {Vector2} [frameSize] - Size of each frame, or the whole image if not passed
75
75
  * @param {number} [padding] - How many pixels padding around each frame
76
+ * @param {number|Vector2} [sourcePadding] - How many pixels padding around each frame in the source image
76
77
  * @return {TileInfo} Tile for the packed image, or undefined if the sheet is full */
77
- tryAdd(imageSize, frameSize=imageSize, padding=textureSheetPadding)
78
+ tryAdd(imageSize, frameSize=imageSize, padding=textureSheetPadding, sourcePadding=0)
78
79
  {
79
80
  ASSERT(isVector2(imageSize) && isVector2(frameSize), 'sizes must be vec2');
80
81
  ASSERT(frameSize.x > 0 && frameSize.y > 0, 'frame size must be positive');
81
- ASSERT(imageSize.x % frameSize.x === 0 && imageSize.y % frameSize.y === 0,
82
- 'image size must be a multiple of the frame size');
82
+
83
+ if (isNumber(sourcePadding))
84
+ sourcePadding = vec2(sourcePadding);
85
+ ASSERT(isVector2(sourcePadding) && sourcePadding.x >= 0 && sourcePadding.y >= 0,
86
+ 'sourcePadding must be a number or vec2 >= 0');
87
+
88
+ // the source may have its own padding baked in around each frame
89
+ const sourceCellWidth = frameSize.x + sourcePadding.x*2;
90
+ const sourceCellHeight = frameSize.y + sourcePadding.y*2;
91
+ ASSERT(imageSize.x % sourceCellWidth === 0 && imageSize.y % sourceCellHeight === 0,
92
+ 'image size must be a multiple of the padded frame size');
83
93
 
84
94
  const cellWidth = frameSize.x + padding*2;
85
95
  const cellHeight = frameSize.y + padding*2;
@@ -88,8 +98,8 @@ class TextureSheet
88
98
 
89
99
  // keep the layout of the source image, but narrow it if a row is too wide
90
100
  // frames wrap down to the next row, which TileInfo.frame handles via columns
91
- const sourceColumns = imageSize.x / frameSize.x;
92
- const frameCount = sourceColumns * (imageSize.y / frameSize.y);
101
+ const sourceColumns = imageSize.x / sourceCellWidth;
102
+ const frameCount = sourceColumns * (imageSize.y / sourceCellHeight);
93
103
  const columns = min(sourceColumns, maxColumns);
94
104
  const blockWidth = columns * cellWidth;
95
105
  const blockHeight = ceil(frameCount / columns) * cellHeight;
@@ -118,23 +128,29 @@ class TextureSheet
118
128
  /** Draw an image into this sheet at a tile returned by tryAdd
119
129
  * @param {HTMLImageElement} image - Source image to copy from
120
130
  * @param {TileInfo} tileInfo - Where to put it, from tryAdd
121
- * @param {boolean} [update] - Upload to webgl now, pass false when batching */
122
- drawImage(image, tileInfo, update=true)
131
+ * @param {boolean} [update] - Upload to webgl now, pass false when batching
132
+ * @param {number|Vector2} [sourcePadding] - How many pixels padding around each frame in the source image */
133
+ drawImage(image, tileInfo, update=true, sourcePadding=0)
123
134
  {
124
135
  ASSERT(!!this.context, 'texture sheet has no canvas');
125
136
 
137
+ if (isNumber(sourcePadding))
138
+ sourcePadding = vec2(sourcePadding);
139
+
126
140
  // copy frames in order, reading the source left to right, top to bottom
127
141
  // the destination wraps at tileInfo.columns which may be narrower than the source
128
142
  const frameSize = tileInfo.size;
129
- const sourceColumns = image.width / frameSize.x;
130
- const frameCount = sourceColumns * (image.height / frameSize.y);
143
+ const sourceCellWidth = frameSize.x + sourcePadding.x*2;
144
+ const sourceCellHeight = frameSize.y + sourcePadding.y*2;
145
+ const sourceColumns = image.width / sourceCellWidth;
146
+ const frameCount = sourceColumns * (image.height / sourceCellHeight);
131
147
  const columns = tileInfo.columns || frameCount;
132
148
  const cellWidth = frameSize.x + tileInfo.padding*2;
133
149
  const cellHeight = frameSize.y + tileInfo.padding*2;
134
150
  for (let i = frameCount; i--;)
135
151
  {
136
- const sourceX = (i % sourceColumns) * frameSize.x;
137
- const sourceY = (i / sourceColumns | 0) * frameSize.y;
152
+ const sourceX = (i % sourceColumns) * sourceCellWidth + sourcePadding.x;
153
+ const sourceY = (i / sourceColumns | 0) * sourceCellHeight + sourcePadding.y;
138
154
  this.context.drawImage(image,
139
155
  sourceX, sourceY, frameSize.x, frameSize.y,
140
156
  tileInfo.pos.x + (i % columns) * cellWidth,
@@ -164,19 +180,22 @@ class TextureSheet
164
180
  * - Nothing is visible until it loads, use spritesReady to wait for it
165
181
  * - Pass frameSize for animations, then step through them with TileInfo.frame
166
182
  * - Grid images keep their layout and frames wrap down to the next row
183
+ * - Pass sourcePadding if the source image has padding baked in around frames
167
184
  * @param {string} src - Image source path
168
185
  * @param {Vector2|number} [frameSize] - Size of each animation frame in pixels
169
186
  * @param {number} [padding] - How many pixels padding around each frame
187
+ * @param {number|Vector2} [sourcePadding] - How many pixels padding around each frame in the source image
170
188
  * @return {TileInfo}
171
189
  * @example
172
190
  * const playerTile = loadSprite('player.png'); // a single sprite
173
191
  * const runTile = loadSprite('run.png', vec2(16)); // a 16x16 frame animation
174
192
  * @memberof TextureSheets */
175
- function loadSprite(src, frameSize, padding=textureSheetPadding)
193
+ function loadSprite(src, frameSize, padding=textureSheetPadding, sourcePadding=0)
176
194
  {
177
195
  ASSERT(isStringLike(src), 'image src must be a string');
178
196
  ASSERT(!frameSize || isVector2(frameSize) || isNumber(frameSize), 'frameSize must be a vec2 or number');
179
197
  ASSERT(isNumber(padding), 'padding must be a number');
198
+ ASSERT(isNumber(sourcePadding) || isVector2(sourcePadding), 'sourcePadding must be a number or vec2');
180
199
 
181
200
  if (isNumber(frameSize))
182
201
  frameSize = vec2(frameSize);
@@ -207,9 +226,9 @@ function loadSprite(src, frameSize, padding=textureSheetPadding)
207
226
  // pack onto a sheet, then fill in the tile that was already handed out,
208
227
  // copying every field so nothing is missed if TileInfo gains more of them
209
228
  const imageSize = vec2(image.width, image.height);
210
- const {sheet, tile} = textureSheetAdd(imageSize, frameSize, padding);
229
+ const {sheet, tile} = textureSheetAdd(imageSize, frameSize, padding, sourcePadding);
211
230
  Object.assign(tileInfo, tile);
212
- sheet.drawImage(image, tileInfo, false); // upload once per batch below
231
+ sheet.drawImage(image, tileInfo, false, sourcePadding); // upload once per batch below
213
232
  }
214
233
  else
215
234
  {
@@ -410,16 +429,16 @@ function textureSheetCreate()
410
429
  }
411
430
 
412
431
  // use the first sheet with enough space, or make a new one
413
- function textureSheetAdd(imageSize, frameSize, padding)
432
+ function textureSheetAdd(imageSize, frameSize, padding, sourcePadding)
414
433
  {
415
434
  let sheet, tile;
416
435
  for (sheet of textureSheets)
417
- if (tile = sheet.tryAdd(imageSize, frameSize, padding))
436
+ if (tile = sheet.tryAdd(imageSize, frameSize, padding, sourcePadding))
418
437
  break;
419
438
  if (!tile)
420
439
  {
421
440
  sheet = textureSheetCreate();
422
- tile = sheet.tryAdd(imageSize, frameSize, padding);
441
+ tile = sheet.tryAdd(imageSize, frameSize, padding, sourcePadding);
423
442
  ASSERT(!!tile, 'image is too large to fit on a texture sheet');
424
443
  }
425
444
  return {sheet, tile};
package/src/engine.js CHANGED
@@ -32,7 +32,7 @@ const engineName = 'LittleJS';
32
32
  * @type {string}
33
33
  * @default
34
34
  * @memberof Engine */
35
- const engineVersion = '1.18.22';
35
+ const engineVersion = '1.18.24';
36
36
 
37
37
  /** Frames per second to update
38
38
  * @type {number}