feat: Adding configurable padding to Tiled atlas packing (#2868)

Adds new configurations to Flame Tiled's atlas packing. Spacing which
can be used to tilesets are not too close to each other in the atlas,
something that can cause texture leaking causing weird rendering issues.
This commit is contained in:
Erick
2023-11-24 11:39:17 -03:00
committed by GitHub
parent 212cc206b9
commit d0c10cbbea
6 changed files with 52 additions and 3 deletions

View File

@ -216,6 +216,8 @@ class RenderableTiledMap {
bool Function(Tileset)? tsxPackingFilter,
bool useAtlas = true,
Paint Function(double opacity)? layerPaintFactory,
double atlasPackingSpacingX = 0,
double atlasPackingSpacingY = 0,
}) async {
final contents =
await (bundle ?? Flame.bundle).loadString('$prefix$fileName');
@ -232,6 +234,8 @@ class RenderableTiledMap {
tsxPackingFilter: tsxPackingFilter,
useAtlas: useAtlas,
layerPaintFactory: layerPaintFactory ?? _defaultLayerPaintFactory,
atlasPackingSpacingX: atlasPackingSpacingX,
atlasPackingSpacingY: atlasPackingSpacingY,
);
}
@ -253,6 +257,8 @@ class RenderableTiledMap {
bool Function(Tileset)? tsxPackingFilter,
bool useAtlas = true,
Paint Function(double opacity)? layerPaintFactory,
double atlasPackingSpacingX = 0,
double atlasPackingSpacingY = 0,
}) async {
final map = await TiledMap.fromString(
contents,
@ -270,6 +276,8 @@ class RenderableTiledMap {
tsxPackingFilter: tsxPackingFilter,
useAtlas: useAtlas,
layerPaintFactory: layerPaintFactory ?? _defaultLayerPaintFactory,
atlasPackingSpacingX: atlasPackingSpacingX,
atlasPackingSpacingY: atlasPackingSpacingY,
);
}
@ -288,6 +296,8 @@ class RenderableTiledMap {
bool Function(Tileset)? tsxPackingFilter,
bool useAtlas = true,
Paint Function(double opacity)? layerPaintFactory,
double atlasPackingSpacingX = 0,
double atlasPackingSpacingY = 0,
}) async {
// We're not going to load animation frames that are never referenced; but
// we do supply the common cache for all layers in this map, and maintain
@ -312,6 +322,8 @@ class RenderableTiledMap {
images: images,
tsxPackingFilter: tsxPackingFilter,
useAtlas: useAtlas,
spacingX: atlasPackingSpacingX,
spacingY: atlasPackingSpacingY,
),
ignoreFlip: ignoreFlip,
images: images,

View File

@ -111,6 +111,8 @@ class TiledAtlas {
Images? images,
bool Function(Tileset)? tsxPackingFilter,
bool useAtlas = true,
double spacingX = 0,
double spacingY = 0,
}) async {
final tilesetImageList = _onlyTileImages(
map,
@ -209,12 +211,17 @@ class TiledAtlas {
final tileImageSource = entry.$1;
final image = await imagesInstance.load(tileImageSource);
final rect = bin.pack(image.width.toDouble(), image.height.toDouble());
final rect = bin.pack(
image.width.toDouble() + spacingX,
image.height.toDouble() + spacingY,
);
pictureRect = pictureRect.expandToInclude(rect);
final offset =
offsetMap[tiledImage.source!] = Offset(rect.left, rect.top);
final offset = offsetMap[tiledImage.source!] = Offset(
rect.left - spacingX,
rect.top - spacingY,
);
canvas.drawImage(image, offset, emptyPaint);
}

View File

@ -117,6 +117,8 @@ class TiledComponent<T extends FlameGame> extends PositionComponent
bool Function(Tileset)? tsxPackingFilter,
bool useAtlas = true,
Paint Function(double opacity)? layerPaintFactory,
double atlasPackingSpacingX = 0,
double atlasPackingSpacingY = 0,
}) async {
return TiledComponent(
await RenderableTiledMap.fromFile(
@ -131,6 +133,8 @@ class TiledComponent<T extends FlameGame> extends PositionComponent
tsxPackingFilter: tsxPackingFilter,
useAtlas: useAtlas,
layerPaintFactory: layerPaintFactory,
atlasPackingSpacingX: atlasPackingSpacingX,
atlasPackingSpacingY: atlasPackingSpacingY,
),
priority: priority,
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -134,6 +134,32 @@ void main() {
);
});
test(
'packs complex maps with multiple images using a custom spacing',
() async {
final component = await TiledComponent.load(
'isometric_plain.tmx',
Vector2(128, 74),
bundle: bundle,
images: Images(bundle: bundle),
atlasPackingSpacingX: 2,
atlasPackingSpacingY: 2,
);
final atlas = TiledAtlas.atlasMap.values.first;
expect(
await imageToPng(atlas.atlas!),
matchesGoldenFile('goldens/larger_atlas_with_spacing.png'),
);
expect(
renderMapToPng(component),
matchesGoldenFile(
'goldens/larger_atlas_component_with_spacing.png',
),
);
},
);
test('can ignore tilesets in the packing', () async {
await TiledComponent.load(
'isometric_plain.tmx',