diff --git a/.markdownlint.yaml b/.markdownlint.yaml
index fc185c6c385..40033f22b2e 100644
--- a/.markdownlint.yaml
+++ b/.markdownlint.yaml
@@ -57,7 +57,7 @@ MD011: true
# MD012/no-multiple-blanks - Multiple consecutive blank lines
MD012:
-# Consecutive blank lines
+ # Consecutive blank lines
maximum: 2
# MD013/line-length - Line length
@@ -235,6 +235,4 @@ MD052: true
# MD053/link-image-reference-definitions - Link and image reference definitions should be needed
MD053:
# Ignored definitions
- ignored_definitions: [
- "//"
- ]
+ ignored_definitions: ["//"]
diff --git a/examples/assets/images/0x72_DungeonTilesetII_v1.4.png b/examples/assets/images/0x72_DungeonTilesetII_v1.4.png
new file mode 100644
index 00000000000..0304491e02f
Binary files /dev/null and b/examples/assets/images/0x72_DungeonTilesetII_v1.4.png differ
diff --git a/examples/assets/tiles/0x72_DungeonTilesetII_v1.4.tsx b/examples/assets/tiles/0x72_DungeonTilesetII_v1.4.tsx
new file mode 100644
index 00000000000..3a0156ac274
--- /dev/null
+++ b/examples/assets/tiles/0x72_DungeonTilesetII_v1.4.tsx
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/assets/tiles/dungeon.tmx b/examples/assets/tiles/dungeon.tmx
new file mode 100644
index 00000000000..69bf487887e
--- /dev/null
+++ b/examples/assets/tiles/dungeon.tmx
@@ -0,0 +1,19 @@
+
+
diff --git a/examples/lib/main.dart b/examples/lib/main.dart
index 340f41832b9..baceed4dac1 100644
--- a/examples/lib/main.dart
+++ b/examples/lib/main.dart
@@ -14,6 +14,7 @@ import 'package:examples/stories/rendering/rendering.dart';
import 'package:examples/stories/sprites/sprites.dart';
import 'package:examples/stories/svg/svg.dart';
import 'package:examples/stories/system/system.dart';
+import 'package:examples/stories/tiled/tiled.dart';
import 'package:examples/stories/utils/utils.dart';
import 'package:examples/stories/widgets/widgets.dart';
import 'package:flutter/material.dart';
@@ -38,6 +39,7 @@ void main() {
addInputStories(dashbook);
addParallaxStories(dashbook);
addRenderingStories(dashbook);
+ addTiledStories(dashbook);
addSpritesStories(dashbook);
addSvgStories(dashbook);
addSystemStories(dashbook);
diff --git a/examples/lib/stories/tiled/flame_tiled_animation_example.dart b/examples/lib/stories/tiled/flame_tiled_animation_example.dart
new file mode 100644
index 00000000000..31c95d29a33
--- /dev/null
+++ b/examples/lib/stories/tiled/flame_tiled_animation_example.dart
@@ -0,0 +1,16 @@
+import 'package:flame/game.dart';
+import 'package:flame_tiled/flame_tiled.dart';
+
+class FlameTiledAnimationExample extends FlameGame {
+ static const String description = '''
+ Loads and displays an animated Tiled map.
+ ''';
+
+ late final TiledComponent map;
+
+ @override
+ Future onLoad() async {
+ map = await TiledComponent.load('dungeon.tmx', Vector2.all(32));
+ add(map);
+ }
+}
diff --git a/examples/lib/stories/tiled/tiled.dart b/examples/lib/stories/tiled/tiled.dart
new file mode 100644
index 00000000000..5152a6a96b6
--- /dev/null
+++ b/examples/lib/stories/tiled/tiled.dart
@@ -0,0 +1,14 @@
+import 'package:dashbook/dashbook.dart';
+import 'package:examples/commons/commons.dart';
+import 'package:examples/stories/tiled/flame_tiled_animation_example.dart';
+
+import 'package:flame/game.dart';
+
+void addTiledStories(Dashbook dashbook) {
+ dashbook.storiesOf('Tiled').add(
+ 'Flame Tiled Animation',
+ (_) => GameWidget(game: FlameTiledAnimationExample()),
+ codeLink: baseLink('effects/flame_tiled_animation_example.dart'),
+ info: FlameTiledAnimationExample.description,
+ );
+}
diff --git a/examples/pubspec.yaml b/examples/pubspec.yaml
index cb374015f66..ba747261c7c 100644
--- a/examples/pubspec.yaml
+++ b/examples/pubspec.yaml
@@ -1,7 +1,7 @@
name: examples
description: A set of small examples showcasing each feature provided by the Flame Engine.
homepage: https://github.com/flame-engine/flame/tree/main/examples
-publish_to: 'none'
+publish_to: "none"
version: 0.1.0
@@ -15,6 +15,7 @@ dependencies:
flame_audio: ^1.3.1
flame_forge2d: ^0.12.2
flame_svg: ^1.5.0
+ flame_tiled: ^1.7.2
flutter:
sdk: flutter
google_fonts: ^2.3.2
@@ -35,6 +36,8 @@ flutter:
- assets/images/tile_maps/
- assets/images/layers/
- assets/images/parallax/
+ - assets/images/parallax/
- assets/svgs/
+ - assets/tiles/
- assets/audio/music/
- assets/audio/sfx/
diff --git a/packages/flame_tiled/lib/src/mutable_rect.dart b/packages/flame_tiled/lib/src/mutable_rect.dart
new file mode 100644
index 00000000000..10a6e6e4fa8
--- /dev/null
+++ b/packages/flame_tiled/lib/src/mutable_rect.dart
@@ -0,0 +1,39 @@
+import 'dart:ui' show Rect;
+
+/// A mutable version of [Rect] for tile map animations.
+class MutableRect extends Rect {
+ /// Construct a rectangle from its left, top, right, and bottom edges.
+ MutableRect.fromLTRB(this.left, this.top, this.right, this.bottom)
+ : super.fromLTRB(left, top, right, bottom);
+
+ /// Create a new instance from [other].
+ factory MutableRect.fromRect(Rect other) =>
+ MutableRect.fromLTRB(other.left, other.top, other.right, other.bottom);
+
+ /// The offset of the left edge of this rectangle from the x axis.
+ @override
+ double left;
+
+ /// The offset of the top edge of this rectangle from the y axis.
+ @override
+ double top;
+
+ /// The offset of the right edge of this rectangle from the x axis.
+ @override
+ double right;
+
+ /// The offset of the bottom edge of this rectangle from the y axis.
+ @override
+ double bottom;
+
+ /// Update with [other]'s dimensions.
+ void copy(Rect other) {
+ left = other.left;
+ top = other.top;
+ right = other.right;
+ bottom = other.bottom;
+ }
+
+ /// Convert to immutable rectangle.
+ Rect toRect() => Rect.fromLTRB(left, top, right, bottom);
+}
diff --git a/packages/flame_tiled/lib/src/renderable_layers/group_layer.dart b/packages/flame_tiled/lib/src/renderable_layers/group_layer.dart
index 123fa2660c7..118a6006c93 100644
--- a/packages/flame_tiled/lib/src/renderable_layers/group_layer.dart
+++ b/packages/flame_tiled/lib/src/renderable_layers/group_layer.dart
@@ -39,4 +39,11 @@ class GroupLayer extends RenderableLayer {
child.render(canvas, camera);
}
}
+
+ @override
+ void update(double dt) {
+ for (final child in children) {
+ child.update(dt);
+ }
+ }
}
diff --git a/packages/flame_tiled/lib/src/renderable_layers/image_layer.dart b/packages/flame_tiled/lib/src/renderable_layers/image_layer.dart
index 29b7b176ea5..01f3d4138e4 100644
--- a/packages/flame_tiled/lib/src/renderable_layers/image_layer.dart
+++ b/packages/flame_tiled/lib/src/renderable_layers/image_layer.dart
@@ -80,4 +80,7 @@ class ImageLayer extends RenderableLayer {
@override
void refreshCache() {}
+
+ @override
+ void update(double dt) {}
}
diff --git a/packages/flame_tiled/lib/src/renderable_layers/object_layer.dart b/packages/flame_tiled/lib/src/renderable_layers/object_layer.dart
index b5d89c651ee..f0f2ab2f808 100644
--- a/packages/flame_tiled/lib/src/renderable_layers/object_layer.dart
+++ b/packages/flame_tiled/lib/src/renderable_layers/object_layer.dart
@@ -35,4 +35,7 @@ class ObjectLayer extends RenderableLayer {
@override
void refreshCache() {}
+
+ @override
+ void update(double dt) {}
}
diff --git a/packages/flame_tiled/lib/src/renderable_layers/renderable_layer.dart b/packages/flame_tiled/lib/src/renderable_layers/renderable_layer.dart
index b1dc196e434..8f49dde900e 100644
--- a/packages/flame_tiled/lib/src/renderable_layers/renderable_layer.dart
+++ b/packages/flame_tiled/lib/src/renderable_layers/renderable_layer.dart
@@ -28,6 +28,8 @@ abstract class RenderableLayer {
void refreshCache();
+ void update(double dt);
+
double get scaleX => destTileSize.x / map.tileWidth;
double get scaleY => destTileSize.y / map.tileHeight;
@@ -85,4 +87,7 @@ class UnsupportedLayer extends RenderableLayer {
@override
void refreshCache() {}
+
+ @override
+ void update(double dt) {}
}
diff --git a/packages/flame_tiled/lib/src/renderable_layers/tile_layer.dart b/packages/flame_tiled/lib/src/renderable_layers/tile_layer.dart
index 4d781751d17..e49eb8b63ef 100644
--- a/packages/flame_tiled/lib/src/renderable_layers/tile_layer.dart
+++ b/packages/flame_tiled/lib/src/renderable_layers/tile_layer.dart
@@ -2,19 +2,24 @@ import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/sprite.dart';
import 'package:flame_tiled/flame_tiled.dart';
+import 'package:flame_tiled/src/mutable_rect.dart';
import 'package:flame_tiled/src/mutable_transform.dart';
import 'package:flame_tiled/src/renderable_layers/group_layer.dart';
import 'package:flame_tiled/src/renderable_layers/renderable_layer.dart';
+import 'package:flame_tiled/src/tile_animation.dart';
import 'package:flame_tiled/src/tile_transform.dart';
import 'package:flutter/painting.dart';
import 'package:meta/meta.dart';
import 'package:tiled/tiled.dart' as tiled;
+import 'package:tiled/tiled.dart';
@internal
class TileLayer extends RenderableLayer {
late final _layerPaint = Paint();
late final Map _cachedSpriteBatches;
late List> indexes;
+ final animations = [];
+ final Map animationFrames;
TileLayer(
super.layer,
@@ -22,12 +27,14 @@ class TileLayer extends RenderableLayer {
super.map,
super.destTileSize,
this._cachedSpriteBatches,
+ this.animationFrames,
) {
_layerPaint.color = Color.fromRGBO(255, 255, 255, opacity);
}
@override
void refreshCache() {
+ animations.clear();
indexes = List.generate(
layer.width,
(index) => List.filled(layer.height, null),
@@ -36,6 +43,13 @@ class TileLayer extends RenderableLayer {
_cacheLayerTiles();
}
+ @override
+ void update(double dt) {
+ for (final animation in animations) {
+ animation.update(dt);
+ }
+ }
+
void _cacheLayerTiles() {
for (final batch in _cachedSpriteBatches.values) {
batch.clear();
@@ -65,6 +79,7 @@ class TileLayer extends RenderableLayer {
final tileData = layer.tileData!;
final batchMap = _cachedSpriteBatches;
final size = destTileSize;
+ final halfMapTile = Vector2(map.tileWidth / 2, map.tileHeight / 2);
for (var ty = 0; ty < tileData.length; ty++) {
final tileRow = tileData[ty];
@@ -88,11 +103,12 @@ class TileLayer extends RenderableLayer {
continue;
}
- final src = tileset.computeDrawRect(tile).toRect();
+ final src =
+ MutableRect.fromRect(tileset.computeDrawRect(tile).toRect());
final flips = SimpleFlips.fromFlips(tileGid.flips);
final scale = size.x / src.width;
- final anchorX = src.width / 2;
- final anchorY = src.height / 2;
+ final anchorX = src.width - halfMapTile.x;
+ final anchorY = src.height - halfMapTile.y;
late double offsetX;
late double offsetY;
@@ -116,6 +132,10 @@ class TileLayer extends RenderableLayer {
transform: indexes[tx][ty],
flip: flips.flip,
);
+
+ if (tile.animation.isNotEmpty) {
+ _addAnimation(tile, tileset, src);
+ }
}
}
}
@@ -125,7 +145,9 @@ class TileLayer extends RenderableLayer {
final batchMap = _cachedSpriteBatches;
final halfDestinationTile = destTileSize / 2;
final size = destTileSize;
- final isometricXShift = map.width * size.x * 0.5;
+ final isometricXShift = map.height * halfDestinationTile.x;
+ final isometricYShift = halfDestinationTile.y;
+ final halfMapTile = Vector2(map.tileWidth / 2, map.tileHeight / 2);
for (var ty = 0; ty < tileData.length; ty++) {
final tileRow = tileData[ty];
@@ -148,17 +170,18 @@ class TileLayer extends RenderableLayer {
continue;
}
- final src = tileset.computeDrawRect(tile).toRect();
+ final src =
+ MutableRect.fromRect(tileset.computeDrawRect(tile).toRect());
final flips = SimpleFlips.fromFlips(tileGid.flips);
final scale = size.x / src.width;
- final anchorX = src.width / 2;
- final anchorY = src.height / 2;
+ final anchorX = src.width - halfMapTile.x;
+ final anchorY = src.height - halfMapTile.y;
late double offsetX;
late double offsetY;
offsetX = halfDestinationTile.x * (tx - ty) + isometricXShift;
- offsetY = halfDestinationTile.y * (tx + ty) - size.y;
+ offsetY = halfDestinationTile.y * (tx + ty) + isometricYShift;
final scos = flips.cos * scale;
final ssin = flips.sin * scale;
@@ -177,6 +200,10 @@ class TileLayer extends RenderableLayer {
transform: indexes[tx][ty],
flip: flips.flip,
);
+
+ if (tile.animation.isNotEmpty) {
+ _addAnimation(tile, tileset, src);
+ }
}
}
}
@@ -190,11 +217,11 @@ class TileLayer extends RenderableLayer {
var staggerY = 0.0;
var staggerX = 0.0;
- // Hexagonal Ponity Tiles move down by a fractional amount.
+ // Isometric staggered tiles move down by a fractional amount.
if (map.staggerAxis == tiled.StaggerAxis.y) {
staggerY = size.y * 0.5;
} else
- // Hexagonal Flat Tiles move right by a fractional amount.
+ // Isometric staggered tiles move right by a fractional amount.
if (map.staggerAxis == tiled.StaggerAxis.x) {
staggerX = size.x * 0.5;
}
@@ -202,7 +229,7 @@ class TileLayer extends RenderableLayer {
for (var ty = 0; ty < tileData.length; ty++) {
final tileRow = tileData[ty];
- // Hexagonal Pointy Tiles shift left and right depending on the row
+ // Isometric staggered tiles shift left and right depending on the row
if (map.staggerAxis == tiled.StaggerAxis.y) {
if ((ty.isOdd && map.staggerIndex == tiled.StaggerIndex.odd) ||
(ty.isEven && map.staggerIndex == tiled.StaggerIndex.even)) {
@@ -245,7 +272,8 @@ class TileLayer extends RenderableLayer {
}
}
- final src = tileset.computeDrawRect(tile).toRect();
+ final src =
+ MutableRect.fromRect(tileset.computeDrawRect(tile).toRect());
final flips = SimpleFlips.fromFlips(tileGid.flips);
final scale = size.x / src.width;
final anchorX = src.width - halfMapTile.x;
@@ -291,6 +319,9 @@ class TileLayer extends RenderableLayer {
flip: flips.flip,
);
}
+ if (tile.animation.isNotEmpty) {
+ _addAnimation(tile, tileset, src);
+ }
}
for (final tile in xSecondPass) {
@@ -367,7 +398,8 @@ class TileLayer extends RenderableLayer {
}
}
- final src = tileset.computeDrawRect(tile).toRect();
+ final src =
+ MutableRect.fromRect(tileset.computeDrawRect(tile).toRect());
final flips = SimpleFlips.fromFlips(tileGid.flips);
final scale = size.x / src.width;
final anchorX = src.width - halfMapTile.x;
@@ -412,6 +444,9 @@ class TileLayer extends RenderableLayer {
flip: flips.flip,
);
}
+ if (tile.animation.isNotEmpty) {
+ _addAnimation(tile, tileset, src);
+ }
}
for (final tile in xSecondPass) {
@@ -446,6 +481,7 @@ class TileLayer extends RenderableLayer {
GroupLayer? parent,
tiled.TiledMap map,
Vector2 destTileSize,
+ Map animationFrames,
) async {
return TileLayer(
layer,
@@ -453,6 +489,7 @@ class TileLayer extends RenderableLayer {
map,
destTileSize,
await _loadImages(map),
+ animationFrames,
);
}
@@ -473,4 +510,19 @@ class TileLayer extends RenderableLayer {
@override
void handleResize(Vector2 canvasSize) {}
+
+ void _addAnimation(Tile tile, Tileset tileset, MutableRect source) {
+ final frames = animationFrames[tile] ??= () {
+ final rects = [];
+ final durations = [];
+ for (final frame in tile.animation) {
+ final newTile = tileset.tiles[frame.tileId];
+ final rect = tileset.computeDrawRect(newTile).toRect();
+ rects.add(rect);
+ durations.add(frame.duration / 1000);
+ }
+ return TileFrames(rects, durations);
+ }();
+ animations.add(TileAnimation(source, frames));
+ }
}
diff --git a/packages/flame_tiled/lib/src/renderable_tile_map.dart b/packages/flame_tiled/lib/src/renderable_tile_map.dart
index 5187383fae5..f6ba9532242 100644
--- a/packages/flame_tiled/lib/src/renderable_tile_map.dart
+++ b/packages/flame_tiled/lib/src/renderable_tile_map.dart
@@ -11,6 +11,7 @@ import 'package:flame_tiled/src/renderable_layers/image_layer.dart';
import 'package:flame_tiled/src/renderable_layers/object_layer.dart';
import 'package:flame_tiled/src/renderable_layers/renderable_layer.dart';
import 'package:flame_tiled/src/renderable_layers/tile_layer.dart';
+import 'package:flame_tiled/src/tile_animation.dart';
import 'package:flame_tiled/src/tile_stack.dart';
import 'package:flutter/painting.dart';
import 'package:tiled/tiled.dart' as tiled;
@@ -50,12 +51,15 @@ class RenderableTiledMap {
/// Paint for the map's background color, if there is one
late final ui.Paint? _backgroundPaint;
+ final Map animationFrames;
+
/// {@macro _renderable_tiled_map}
RenderableTiledMap(
this.map,
this.renderableLayers,
this.destTileSize, {
this.camera,
+ this.animationFrames = const {},
}) {
_refreshCache();
@@ -216,14 +220,26 @@ class RenderableTiledMap {
Vector2 destTileSize, {
Camera? camera,
}) async {
- final renderableLayers =
- await _renderableLayers(map.layers, null, map, destTileSize, camera);
+ // 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
+ // the update cycle for these in one place.
+ final animationFrames = {};
+
+ final renderableLayers = await _renderableLayers(
+ map.layers,
+ null,
+ map,
+ destTileSize,
+ camera,
+ animationFrames,
+ );
return RenderableTiledMap(
map,
renderableLayers,
destTileSize,
camera: camera,
+ animationFrames: animationFrames,
);
}
@@ -233,6 +249,7 @@ class RenderableTiledMap {
tiled.TiledMap map,
Vector2 destTileSize,
Camera? camera,
+ Map animationFrames,
) async {
return Future.wait(
layers.where((layer) => layer.visible).toList().map((layer) async {
@@ -243,6 +260,7 @@ class RenderableTiledMap {
parent,
map,
destTileSize,
+ animationFrames,
);
case tiled.ImageLayer:
return ImageLayer.load(
@@ -267,6 +285,7 @@ class RenderableTiledMap {
map,
destTileSize,
camera,
+ animationFrames,
);
renderableGroup.children = await children;
return renderableGroup;
@@ -323,6 +342,18 @@ class RenderableTiledMap {
return null;
}
}
+
+ void update(double dt) {
+ // First, update animation frames.
+ for (final frame in animationFrames.values) {
+ frame.update(dt);
+ }
+
+ // Then every layer.
+ for (final layer in renderableLayers) {
+ layer.update(dt);
+ }
+ }
}
Color? _parseTiledColor(String? tiledColor) {
diff --git a/packages/flame_tiled/lib/src/tile_animation.dart b/packages/flame_tiled/lib/src/tile_animation.dart
new file mode 100644
index 00000000000..b129caf3375
--- /dev/null
+++ b/packages/flame_tiled/lib/src/tile_animation.dart
@@ -0,0 +1,60 @@
+import 'dart:ui' show Rect;
+
+import 'package:flame_tiled/src/mutable_rect.dart';
+
+/// Records a single animation for tile on a layer.
+///
+/// This works because SpriteBatch holds a list of [Rect]. Those rectangles
+/// are usually immutable, but flame_tile uses a mutable rectangle to update
+/// the offsets in the image atlas.
+class TileAnimation {
+ /// Frames of the animation loop.
+ final TileFrames frames;
+
+ /// Rectangle that gets updated for each new frame in the animation.
+ final MutableRect batchedSource;
+
+ /// Current frame counter.
+ int frame = 0;
+
+ TileAnimation(
+ this.batchedSource,
+ this.frames,
+ );
+
+ void update(double dt) {
+ if (frame != frames.frame) {
+ frame = frames.frame;
+ batchedSource.copy(frames.sources[frame]);
+ }
+ }
+}
+
+/// Records the list of frames for a tile so that it can be reused.
+class TileFrames {
+ /// Rectangles for each frame in the animation.
+ final List sources;
+
+ /// Duration, in seconds, for each frame in the animation.
+ final List durations;
+
+ /// Current frame lifetime.
+ double frameTime = 0.0;
+
+ /// Current frame counter for all frames sharing this animation.
+ int frame = 0;
+
+ TileFrames(this.sources, this.durations);
+
+ void update(double dt) {
+ frameTime += dt;
+
+ // Track really long jank by skipping ahead.
+ while (durations[frame] <= frameTime) {
+ final currentFrameTime = durations[frame];
+ frame = (frame + 1) % durations.length;
+ // We still have time to add to this, even if we're late.
+ frameTime = frameTime - currentFrameTime;
+ }
+ }
+}
diff --git a/packages/flame_tiled/lib/src/tiled_component.dart b/packages/flame_tiled/lib/src/tiled_component.dart
index da54adeaefe..5d12a35cc7b 100644
--- a/packages/flame_tiled/lib/src/tiled_component.dart
+++ b/packages/flame_tiled/lib/src/tiled_component.dart
@@ -4,6 +4,7 @@ import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame_tiled/src/renderable_tile_map.dart';
+import 'package:meta/meta.dart';
import 'package:tiled/tiled.dart';
/// {@template _tiled_component}
@@ -47,7 +48,17 @@ class TiledComponent extends PositionComponent
super.anchor,
super.children,
super.priority,
- }) : super(size: _computeSize(tileMap));
+ }) : super(
+ size: computeSize(
+ tileMap.map.orientation,
+ tileMap.destTileSize,
+ tileMap.map.tileWidth,
+ tileMap.map.tileHeight,
+ tileMap.map.width,
+ tileMap.map.height,
+ tileMap.map.staggerAxis,
+ ),
+ );
@override
Future? onLoad() async {
@@ -56,6 +67,11 @@ class TiledComponent extends PositionComponent
tileMap.camera ??= gameRef.camera;
}
+ @override
+ void update(double dt) {
+ tileMap.update(dt);
+ }
+
@override
void render(Canvas canvas) {
tileMap.render(canvas);
@@ -79,49 +95,59 @@ class TiledComponent extends PositionComponent
);
}
- static Vector2 _computeSize(RenderableTiledMap tileMap) {
- final tMap = tileMap.map;
-
- final xScale = tileMap.destTileSize.x / tMap.tileWidth;
- final yScale = tileMap.destTileSize.y / tMap.tileHeight;
+ @visibleForTesting
+ static Vector2 computeSize(
+ MapOrientation? orientation,
+ Vector2 destTileSize,
+ int tileWidth,
+ int tileHeight,
+ int mapWidth,
+ int mapHeight,
+ StaggerAxis? staggerAxis,
+ ) {
+ if (orientation == null) {
+ return NotifyingVector2.zero();
+ }
+ final xScale = destTileSize.x / tileWidth;
+ final yScale = destTileSize.y / tileHeight;
final tileScaled = Vector2(
- tileMap.map.tileWidth * xScale,
- tileMap.map.tileHeight * yScale,
+ tileWidth * xScale,
+ tileHeight * yScale,
);
- if (tMap.orientation == null) {
- return NotifyingVector2.zero();
- }
-
- switch (tMap.orientation!) {
+ switch (orientation) {
case MapOrientation.staggered:
- return tMap.staggerAxis == StaggerAxis.y
+ return staggerAxis == StaggerAxis.y
? Vector2(
- tileScaled.x * tileMap.map.width + tileScaled.x / 2,
- tileScaled.y + ((tileMap.map.height - 1) * tileScaled.y / 2),
+ tileScaled.x * mapWidth + tileScaled.x / 2,
+ (mapHeight + 1) * (tileScaled.y / 2),
)
: Vector2(
- tileScaled.x + ((tileMap.map.width - 1) * tileScaled.x / 2),
- tileScaled.y * tileMap.map.height + tileScaled.y / 2,
+ (mapWidth + 1) * (tileScaled.x / 2),
+ tileScaled.y * mapHeight + tileScaled.y / 2,
);
case MapOrientation.hexagonal:
- return tMap.staggerAxis == StaggerAxis.y
+ return staggerAxis == StaggerAxis.y
? Vector2(
- tileMap.map.width * tileScaled.x + tileScaled.x / 2,
- tileScaled.y + ((tileMap.map.height - 1) * tileScaled.y * 0.75),
+ mapWidth * tileScaled.x + tileScaled.x / 2,
+ tileScaled.y + ((mapHeight - 1) * tileScaled.y * 0.75),
)
: Vector2(
- tileScaled.x + ((tileMap.map.width - 1) * tileScaled.x * 0.75),
- (tileMap.map.height * tileScaled.y) + tileScaled.y / 2,
+ tileScaled.x + ((mapWidth - 1) * tileScaled.x * 0.75),
+ (mapHeight * tileScaled.y) + tileScaled.y / 2,
);
case MapOrientation.isometric:
+ final halfTile = tileScaled / 2;
+ final dimensions = mapWidth + mapHeight;
+ return halfTile..scale(dimensions.toDouble());
+
case MapOrientation.orthogonal:
return Vector2(
- tileMap.map.width * tileScaled.x,
- tileMap.map.height * tileScaled.y,
+ mapWidth * tileScaled.x,
+ mapHeight * tileScaled.y,
);
}
}
diff --git a/packages/flame_tiled/test/README.md b/packages/flame_tiled/test/README.md
index aaf88b88b56..fc6e419b261 100644
--- a/packages/flame_tiled/test/README.md
+++ b/packages/flame_tiled/test/README.md
@@ -5,3 +5,8 @@ They were released as Public domain.
- Tileset_Hexagonal_FlatTop_60x39_60x60.png
- Tileset_Hexagonal_PointyTop_60x52_60x80.png
+
+The following assets were [downloaded here](https://0x72.itch.io/dungeontileset-ii).
+They were released as [CC0 1.0 Universal (CC0 1.0) - Public Domain Dedication](https://creativecommons.org/publicdomain/zero/1.0/).
+
+- 0x72_DungeonTilesetII_v1.4.png
diff --git a/packages/flame_tiled/test/assets/0x72_DungeonTilesetII_v1.4.png b/packages/flame_tiled/test/assets/0x72_DungeonTilesetII_v1.4.png
new file mode 100644
index 00000000000..0304491e02f
Binary files /dev/null and b/packages/flame_tiled/test/assets/0x72_DungeonTilesetII_v1.4.png differ
diff --git a/packages/flame_tiled/test/assets/dungeon_animation_hexagonal.tmx b/packages/flame_tiled/test/assets/dungeon_animation_hexagonal.tmx
new file mode 100644
index 00000000000..f2d85907b03
--- /dev/null
+++ b/packages/flame_tiled/test/assets/dungeon_animation_hexagonal.tmx
@@ -0,0 +1,58 @@
+
+
diff --git a/packages/flame_tiled/test/assets/dungeon_animation_isometric.png b/packages/flame_tiled/test/assets/dungeon_animation_isometric.png
new file mode 100644
index 00000000000..d220467c4d0
Binary files /dev/null and b/packages/flame_tiled/test/assets/dungeon_animation_isometric.png differ
diff --git a/packages/flame_tiled/test/assets/dungeon_animation_isometric.tmx b/packages/flame_tiled/test/assets/dungeon_animation_isometric.tmx
new file mode 100644
index 00000000000..8503e5f6197
--- /dev/null
+++ b/packages/flame_tiled/test/assets/dungeon_animation_isometric.tmx
@@ -0,0 +1,58 @@
+
+
diff --git a/packages/flame_tiled/test/assets/dungeon_animation_orthogonal.tmx b/packages/flame_tiled/test/assets/dungeon_animation_orthogonal.tmx
new file mode 100644
index 00000000000..dd6f83b0175
--- /dev/null
+++ b/packages/flame_tiled/test/assets/dungeon_animation_orthogonal.tmx
@@ -0,0 +1,58 @@
+
+
diff --git a/packages/flame_tiled/test/assets/dungeon_animation_staggered.tmx b/packages/flame_tiled/test/assets/dungeon_animation_staggered.tmx
new file mode 100644
index 00000000000..97ecf02154a
--- /dev/null
+++ b/packages/flame_tiled/test/assets/dungeon_animation_staggered.tmx
@@ -0,0 +1,58 @@
+
+
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_0.png b/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_0.png
new file mode 100644
index 00000000000..7d24d31bffa
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_0.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_1.png b/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_1.png
new file mode 100644
index 00000000000..c21bf2b8526
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_1.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_2.png b/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_2.png
new file mode 100644
index 00000000000..faccabff66d
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_2.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_3.png b/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_3.png
new file mode 100644
index 00000000000..e8b2eb37014
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_hexagonal_3.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_isometric_0.png b/packages/flame_tiled/test/goldens/dungeon_animation_isometric_0.png
new file mode 100644
index 00000000000..86e7cbaa2ce
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_isometric_0.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_isometric_1.png b/packages/flame_tiled/test/goldens/dungeon_animation_isometric_1.png
new file mode 100644
index 00000000000..56c5253a465
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_isometric_1.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_isometric_2.png b/packages/flame_tiled/test/goldens/dungeon_animation_isometric_2.png
new file mode 100644
index 00000000000..229f71853ba
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_isometric_2.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_isometric_3.png b/packages/flame_tiled/test/goldens/dungeon_animation_isometric_3.png
new file mode 100644
index 00000000000..a7a2e0175b7
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_isometric_3.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_0.png b/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_0.png
new file mode 100644
index 00000000000..b86b2652993
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_0.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_1.png b/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_1.png
new file mode 100644
index 00000000000..b5eab7dafce
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_1.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_2.png b/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_2.png
new file mode 100644
index 00000000000..d02470cf34b
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_2.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_3.png b/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_3.png
new file mode 100644
index 00000000000..bf675ae1216
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_orthogonal_3.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_staggered_0.png b/packages/flame_tiled/test/goldens/dungeon_animation_staggered_0.png
new file mode 100644
index 00000000000..7d24d31bffa
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_staggered_0.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_staggered_1.png b/packages/flame_tiled/test/goldens/dungeon_animation_staggered_1.png
new file mode 100644
index 00000000000..c21bf2b8526
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_staggered_1.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_staggered_2.png b/packages/flame_tiled/test/goldens/dungeon_animation_staggered_2.png
new file mode 100644
index 00000000000..faccabff66d
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_staggered_2.png differ
diff --git a/packages/flame_tiled/test/goldens/dungeon_animation_staggered_3.png b/packages/flame_tiled/test/goldens/dungeon_animation_staggered_3.png
new file mode 100644
index 00000000000..e8b2eb37014
Binary files /dev/null and b/packages/flame_tiled/test/goldens/dungeon_animation_staggered_3.png differ
diff --git a/packages/flame_tiled/test/goldens/flat_hex_even.png b/packages/flame_tiled/test/goldens/flat_hex_even.png
index aa17df9e4a9..cdbca424a5c 100644
Binary files a/packages/flame_tiled/test/goldens/flat_hex_even.png and b/packages/flame_tiled/test/goldens/flat_hex_even.png differ
diff --git a/packages/flame_tiled/test/goldens/flat_hex_odd.png b/packages/flame_tiled/test/goldens/flat_hex_odd.png
index b258765b22d..ac90a245501 100644
Binary files a/packages/flame_tiled/test/goldens/flat_hex_odd.png and b/packages/flame_tiled/test/goldens/flat_hex_odd.png differ
diff --git a/packages/flame_tiled/test/goldens/isometric.png b/packages/flame_tiled/test/goldens/isometric.png
index 4101bb6e79b..88488c768f0 100644
Binary files a/packages/flame_tiled/test/goldens/isometric.png and b/packages/flame_tiled/test/goldens/isometric.png differ
diff --git a/packages/flame_tiled/test/goldens/shifted_scaled_smaller.png b/packages/flame_tiled/test/goldens/shifted_scaled_smaller.png
index 7c383cdfc53..ea3345669fc 100644
Binary files a/packages/flame_tiled/test/goldens/shifted_scaled_smaller.png and b/packages/flame_tiled/test/goldens/shifted_scaled_smaller.png differ
diff --git a/packages/flame_tiled/test/goldens/tile_stack_all_move.png b/packages/flame_tiled/test/goldens/tile_stack_all_move.png
index 9f25b485b99..837d12aaea1 100644
Binary files a/packages/flame_tiled/test/goldens/tile_stack_all_move.png and b/packages/flame_tiled/test/goldens/tile_stack_all_move.png differ
diff --git a/packages/flame_tiled/test/tiled_component_sizes_test.dart b/packages/flame_tiled/test/tiled_component_sizes_test.dart
new file mode 100644
index 00000000000..ff1bffab8c3
--- /dev/null
+++ b/packages/flame_tiled/test/tiled_component_sizes_test.dart
@@ -0,0 +1,258 @@
+import 'package:flame/components.dart';
+import 'package:flame/extensions.dart';
+import 'package:flame/game.dart';
+import 'package:flame_tiled/flame_tiled.dart';
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:tiled/tiled.dart';
+
+void main() {
+ group('TiledComponent.computeSize', () {
+ test('orthogonal', () {
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.orthogonal,
+ Vector2(16, 16),
+ 16,
+ 16,
+ 5,
+ 5,
+ null,
+ ),
+ Vector2(80, 80),
+ reason: 'full sized tiles',
+ );
+
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.orthogonal,
+ Vector2(8, 8),
+ 16,
+ 16,
+ 5,
+ 4,
+ null,
+ ),
+ Vector2(40, 32),
+ reason: 'half sized tiles',
+ );
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.orthogonal,
+ Vector2(8, 32),
+ 16,
+ 16,
+ 4,
+ 5,
+ null,
+ ),
+ Vector2(32, 160),
+ reason: 'odd sized tiles',
+ );
+ });
+
+ test('isometric', () {
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.isometric,
+ Vector2(16, 16),
+ 16,
+ 16,
+ 5,
+ 4,
+ null,
+ ),
+ Vector2(72, 72),
+ reason: 'full sized tiles',
+ );
+
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.isometric,
+ Vector2(8, 8),
+ 16,
+ 16,
+ 4,
+ 5,
+ null,
+ ),
+ Vector2(36, 36),
+ reason: 'full sized tiles',
+ );
+
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.isometric,
+ Vector2(8, 32),
+ 16,
+ 16,
+ 7,
+ 5,
+ null,
+ ),
+ Vector2(48, 192),
+ reason: 'odd sized tiles',
+ );
+ });
+
+ test('isometric', () {
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.isometric,
+ Vector2(16, 16),
+ 16,
+ 16,
+ 5,
+ 4,
+ null,
+ ),
+ Vector2(72, 72),
+ reason: 'full sized tiles',
+ );
+
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.isometric,
+ Vector2(8, 8),
+ 16,
+ 16,
+ 4,
+ 5,
+ null,
+ ),
+ Vector2(36, 36),
+ reason: 'full sized tiles',
+ );
+
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.isometric,
+ Vector2(8, 32),
+ 16,
+ 16,
+ 7,
+ 5,
+ null,
+ ),
+ Vector2(48, 192),
+ reason: 'odd sized tiles',
+ );
+ });
+
+ test('hexagonal', () {
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.hexagonal,
+ Vector2(16, 16),
+ 16,
+ 16,
+ 4,
+ 5,
+ StaggerAxis.x,
+ ),
+ Vector2(52, 88),
+ reason: 'full sized tiles, stagger x',
+ );
+
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.hexagonal,
+ Vector2(16, 16),
+ 16,
+ 16,
+ 4,
+ 5,
+ StaggerAxis.y,
+ ),
+ Vector2(72, 64),
+ reason: 'full sized tiles, stagger y',
+ );
+
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.hexagonal,
+ Vector2(8, 8),
+ 16,
+ 16,
+ 4,
+ 5,
+ StaggerAxis.x,
+ ),
+ Vector2(26, 44),
+ reason: 'half sized tiles, stagger x',
+ );
+
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.hexagonal,
+ Vector2(8, 8),
+ 16,
+ 16,
+ 4,
+ 5,
+ StaggerAxis.y,
+ ),
+ Vector2(36, 32),
+ reason: 'full sized tiles, stagger y',
+ );
+ });
+
+ test('staggered', () {
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.staggered,
+ Vector2(16, 16),
+ 16,
+ 16,
+ 4,
+ 5,
+ StaggerAxis.x,
+ ),
+ Vector2(40, 88),
+ reason: 'full sized tiles, stagger x',
+ );
+
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.staggered,
+ Vector2(16, 16),
+ 16,
+ 16,
+ 4,
+ 5,
+ StaggerAxis.y,
+ ),
+ Vector2(72, 48),
+ reason: 'full sized tiles, stagger y',
+ );
+
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.staggered,
+ Vector2(8, 8),
+ 16,
+ 16,
+ 4,
+ 5,
+ StaggerAxis.x,
+ ),
+ Vector2(20, 44),
+ reason: 'half sized tiles, stagger x',
+ );
+
+ expect(
+ TiledComponent.computeSize(
+ MapOrientation.staggered,
+ Vector2(8, 8),
+ 16,
+ 16,
+ 4,
+ 5,
+ StaggerAxis.y,
+ ),
+ Vector2(36, 24),
+ reason: 'half sized tiles, stagger y',
+ );
+ });
+ });
+}
diff --git a/packages/flame_tiled/test/tiled_test.dart b/packages/flame_tiled/test/tiled_test.dart
index 60324b59e60..628a9297ad6 100644
--- a/packages/flame_tiled/test/tiled_test.dart
+++ b/packages/flame_tiled/test/tiled_test.dart
@@ -7,6 +7,9 @@ import 'package:flame/extensions.dart';
import 'package:flame/flame.dart';
import 'package:flame/game.dart';
import 'package:flame_tiled/flame_tiled.dart';
+import 'package:flame_tiled/src/renderable_layers/tile_layer.dart'
+ as renderable;
+
import 'package:flutter/services.dart' show CachingAssetBundle;
import 'package:flutter_test/flutter_test.dart';
import 'package:tiled/tiled.dart';
@@ -310,17 +313,16 @@ void main() {
Future renderMapToPng(
TiledComponent component,
- num width,
- num height,
) async {
final canvasRecorder = PictureRecorder();
final canvas = Canvas(canvasRecorder);
component.tileMap.render(canvas);
final picture = canvasRecorder.endRecording();
- // Map size is now 320 wide, but it has 1 extra tile of height becusae
+ final size = component.size;
+ // Map size is now 320 wide, but it has 1 extra tile of height because
// its actually double-height tiles.
- final image = await picture.toImageSafe(width.toInt(), height.toInt());
+ final image = await picture.toImageSafe(size.x.toInt(), size.y.toInt());
return (await image.toByteData(format: ImageByteFormat.png))!
.buffer
.asUint8List();
@@ -358,7 +360,7 @@ void main() {
});
test('renders', () async {
- final pngData = await renderMapToPng(component, 32 * 16, 128 * 16);
+ final pngData = await renderMapToPng(component);
expect(pngData, matchesGoldenFile('goldens/orthogonal.png'));
});
@@ -382,14 +384,13 @@ void main() {
test('component size', () {
expect(component.tileMap.destTileSize, Vector2(64, 32));
- expect(component.size, Vector2(64 * 5, 32 * 5));
+ expect(component.size, Vector2(320, 160));
});
test('renders', () async {
- // Map size is now 320 wide, but it has 1 extra tile of height becusae
+ // Map size is now 320 wide, but it has 1 extra tile of height because
// its actually double-height tiles.
- final pngData =
- await renderMapToPng(component, 256 * 5 ~/ 4, (128 * 5 + 128) ~/ 4);
+ final pngData = await renderMapToPng(component);
expect(pngData, matchesGoldenFile('goldens/isometric.png'));
});
@@ -424,7 +425,7 @@ void main() {
expect(component.size, Vector2(240, 214.5));
- final pngData = await renderMapToPng(component, 240, 215);
+ final pngData = await renderMapToPng(component);
expect(pngData, matchesGoldenFile('goldens/flat_hex_even.png'));
});
@@ -438,7 +439,7 @@ void main() {
expect(component.size, Vector2(240, 214.5));
- final pngData = await renderMapToPng(component, 240, 215);
+ final pngData = await renderMapToPng(component);
expect(pngData, matchesGoldenFile('goldens/flat_hex_odd.png'));
});
@@ -452,7 +453,7 @@ void main() {
expect(component.size, Vector2(330, 208));
- final pngData = await renderMapToPng(component, 330, 208);
+ final pngData = await renderMapToPng(component);
expect(pngData, matchesGoldenFile('goldens/pointy_hex_even.png'));
});
@@ -466,7 +467,7 @@ void main() {
expect(component.size, Vector2(330, 208));
- final pngData = await renderMapToPng(component, 330, 208);
+ final pngData = await renderMapToPng(component);
expect(pngData, matchesGoldenFile('goldens/pointy_hex_odd.png'));
});
@@ -501,7 +502,7 @@ void main() {
expect(component.size, Vector2(320, 288));
- final pngData = await renderMapToPng(component, 320, 288);
+ final pngData = await renderMapToPng(component);
expect(
pngData,
@@ -518,7 +519,7 @@ void main() {
expect(component.size, Vector2(320 / 2, 288 / 2));
- final pngData = await renderMapToPng(component, 160, 144);
+ final pngData = await renderMapToPng(component);
expect(
pngData,
@@ -535,7 +536,7 @@ void main() {
expect(component.size, Vector2(576 / 2, 160 / 2));
- final pngData = await renderMapToPng(component, 288, 80);
+ final pngData = await renderMapToPng(component);
expect(
pngData,
@@ -552,7 +553,7 @@ void main() {
expect(component.size, Vector2(576, 160));
- final pngData = await renderMapToPng(component, 576, 160);
+ final pngData = await renderMapToPng(component);
expect(
pngData,
@@ -582,11 +583,7 @@ void main() {
test('regular', () async {
await setupMap(size);
- final pngData = await renderMapToPng(
- component,
- size.x.toInt() * 5,
- size.y.toInt() * 5,
- );
+ final pngData = await renderMapToPng(component);
expect(
pngData,
@@ -597,11 +594,7 @@ void main() {
test('smaller', () async {
final smallSize = size / 3;
await setupMap(smallSize);
- final pngData = await renderMapToPng(
- component,
- smallSize.x.toInt() * 5,
- smallSize.y.toInt() * 5,
- );
+ final pngData = await renderMapToPng(component);
expect(
pngData,
@@ -612,11 +605,7 @@ void main() {
test('larger', () async {
final largeSize = size * 2;
await setupMap(largeSize);
- final pngData = await renderMapToPng(
- component,
- largeSize.x.toInt() * 5,
- largeSize.y.toInt() * 5,
- );
+ final pngData = await renderMapToPng(component);
expect(
pngData,
@@ -664,8 +653,7 @@ void main() {
final stack = component.tileMap.tileStack(0, 0, all: true);
stack.position = stack.position + Vector2.all(20);
- final pngData =
- await renderMapToPng(component, size.x * 5, size.y * 5 + size.y / 2);
+ final pngData = await renderMapToPng(component);
expect(
pngData,
matchesGoldenFile('goldens/tile_stack_all_move.png'),
@@ -676,13 +664,140 @@ void main() {
final stack = component.tileMap.tileStack(0, 0, named: {'item'});
stack.position = stack.position + Vector2(-20, 20);
- final pngData = await renderMapToPng(component, size.x * 5, size.y * 5);
+ final pngData = await renderMapToPng(component);
expect(
pngData,
matchesGoldenFile('goldens/tile_stack_single_move.png'),
);
});
});
+
+ group('animated tiles', () {
+ late TiledComponent component;
+ late RenderableTiledMap map;
+ final size = Vector2(16, 16);
+
+ for (final mapType in [
+ 'orthogonal',
+ 'isometric',
+ 'hexagonal',
+ 'staggered'
+ ]) {
+ group(mapType, () {
+ setUp(() async {
+ Flame.bundle = TestAssetBundle(
+ imageNames: [
+ '0x72_DungeonTilesetII_v1.4.png',
+ ],
+ mapPath: 'test/assets/dungeon_animation_$mapType.tmx',
+ );
+ component = await TiledComponent.load(
+ 'dungeon_animation_$mapType.tmx',
+ size,
+ );
+ map = component.tileMap;
+ });
+
+ test('handle single frame animations ($mapType)', () {
+ expect(
+ map.renderableLayers.first,
+ isInstanceOf(),
+ );
+ final layer = map.renderableLayers.first as renderable.TileLayer;
+ expect(
+ layer.animations,
+ hasLength(1),
+ reason: 'layer has only one animation',
+ );
+ expect(
+ layer.animationFrames,
+ hasLength(4),
+ reason: 'layer only caches frames in use',
+ );
+ expect(layer.animations.first.frames.sources, hasLength(1));
+ });
+
+ test('handle single frame animations ($mapType)', () {
+ expect(map.renderableLayers[1], isInstanceOf());
+ final layer = map.renderableLayers[1] as renderable.TileLayer;
+ expect(
+ layer.animations,
+ hasLength(2),
+ reason: 'two animations on this layer',
+ );
+ expect(
+ layer.animationFrames,
+ hasLength(4),
+ reason: 'layer only caches frames in use',
+ );
+
+ final waterAnimation = layer.animations.first;
+ final spikeAnimation = layer.animations.last;
+ expect(waterAnimation.frames.durations, [.18, .17, .15]);
+ expect(spikeAnimation.frames.durations, [.176, .176, .176, .176]);
+
+ map.update(.177);
+ expect(waterAnimation.frame, 0);
+ expect(waterAnimation.frames.frameTime, .177);
+ expect(
+ waterAnimation.batchedSource.toRect(),
+ waterAnimation.frames.sources[0],
+ );
+
+ expect(spikeAnimation.frame, 1);
+ expect(spikeAnimation.frames.frameTime, moreOrLessEquals(.001));
+ expect(
+ spikeAnimation.batchedSource.toRect(),
+ spikeAnimation.frames.sources[1],
+ );
+
+ map.update(.003);
+ expect(waterAnimation.frame, 1);
+ expect(waterAnimation.frames.frameTime, moreOrLessEquals(.0));
+ expect(spikeAnimation.frame, 1);
+ expect(spikeAnimation.frames.frameTime, moreOrLessEquals(0.004));
+
+ map.update(0.17 + 0.15);
+ expect(waterAnimation.frame, 0, reason: 'wraps around');
+ expect(
+ waterAnimation.batchedSource.toRect(),
+ waterAnimation.frames.sources[0],
+ );
+ });
+
+ /// This will not produce a pretty map for non-orthogonal, but that's
+ /// OK, we're looking for parsing and handling of animations.
+ test('renders ($mapType)', () async {
+ var pngData = await renderMapToPng(component);
+ await expectLater(
+ pngData,
+ matchesGoldenFile('goldens/dungeon_animation_${mapType}_0.png'),
+ );
+
+ component.update(0.18);
+ pngData = await renderMapToPng(component);
+ await expectLater(
+ pngData,
+ matchesGoldenFile('goldens/dungeon_animation_${mapType}_1.png'),
+ );
+
+ component.update(0.18);
+ pngData = await renderMapToPng(component);
+ await expectLater(
+ pngData,
+ matchesGoldenFile('goldens/dungeon_animation_${mapType}_2.png'),
+ );
+
+ component.update(0.18);
+ pngData = await renderMapToPng(component);
+ await expectLater(
+ pngData,
+ matchesGoldenFile('goldens/dungeon_animation_${mapType}_3.png'),
+ );
+ });
+ });
+ }
+ });
}
class TestAssetBundle extends CachingAssetBundle {