diff --git a/examples/lib/stories/sprites/sprite_batch_load_example.dart b/examples/lib/stories/sprites/sprite_batch_load_example.dart index eaf02079243..ed242a0136d 100644 --- a/examples/lib/stories/sprites/sprite_batch_load_example.dart +++ b/examples/lib/stories/sprites/sprite_batch_load_example.dart @@ -20,6 +20,11 @@ class SpriteBatchLoadExample extends FlameGame { class MySpriteBatchComponent extends SpriteBatchComponent with HasGameReference { + MySpriteBatchComponent() + : super( + blendMode: BlendMode.srcOver, + ); + @override Future onLoad() async { final spriteBatch = await game.loadSpriteBatch('boom.png'); diff --git a/packages/flame/lib/src/sprite_batch.dart b/packages/flame/lib/src/sprite_batch.dart index 8dfddf95ee9..6b3beeec0ae 100644 --- a/packages/flame/lib/src/sprite_batch.dart +++ b/packages/flame/lib/src/sprite_batch.dart @@ -277,9 +277,7 @@ class SpriteBatch { _sources[index] = newBatchItem.source; _transforms[index] = newBatchItem.transform; - if (color != null) { - _colors[index] = color; - } + _colors[index] = color ?? _defaultColor; } /// Add a new batch item using a RSTransform. @@ -328,9 +326,7 @@ class SpriteBatch { : batchItem.source, ); _transforms.add(batchItem.transform); - if (color != null) { - _colors.add(color); - } + _colors.add(color ?? _defaultColor); } /// Add a new batch item. @@ -413,13 +409,19 @@ class SpriteBatch { final renderPaint = paint ?? _emptyPaint; + final hasNoColors = _colors.every((c) => c == _defaultColor); + final actualBlendMode = blendMode ?? defaultBlendMode; + if (!hasNoColors && actualBlendMode == null) { + throw 'When setting any colors, a blend mode must be provided.'; + } + if (useAtlas && !_flippedAtlasStatus.isGenerating) { canvas.drawAtlas( atlas, _transforms, _sources, - _colors.isEmpty ? null : _colors, - blendMode ?? defaultBlendMode, + hasNoColors ? null : _colors, + actualBlendMode, cullRect, renderPaint, ); @@ -441,4 +443,6 @@ class SpriteBatch { } } } + + static const _defaultColor = Color(0x00000000); } diff --git a/packages/flame/test/_goldens/sprite_batch_test_1.png b/packages/flame/test/_goldens/sprite_batch_test_1.png new file mode 100644 index 00000000000..7df99387445 Binary files /dev/null and b/packages/flame/test/_goldens/sprite_batch_test_1.png differ diff --git a/packages/flame/test/_goldens/sprite_batch_test_2.png b/packages/flame/test/_goldens/sprite_batch_test_2.png new file mode 100644 index 00000000000..fb7c6f5de7d Binary files /dev/null and b/packages/flame/test/_goldens/sprite_batch_test_2.png differ diff --git a/packages/flame/test/sprite_batch_test.dart b/packages/flame/test/sprite_batch_test.dart index 8ccc4694607..5b4897ff23f 100644 --- a/packages/flame/test/sprite_batch_test.dart +++ b/packages/flame/test/sprite_batch_test.dart @@ -1,10 +1,14 @@ import 'dart:ui'; +import 'package:flame/components.dart'; import 'package:flame/sprite.dart'; +import 'package:flame_test/flame_test.dart'; import 'package:flutter/material.dart' hide Image; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; +import '_resources/load_image.dart'; + class _MockImage extends Mock implements Image {} void main() { @@ -56,5 +60,68 @@ void main() { .having((t) => t.ty, 'ty', 1), ); }); + + const margin = 2.0; + const tileSize = 6.0; + + testGolden( + 'can render a batch with blend mode', + (game) async { + final spriteSheet = await loadImage('alphabet.png'); + final spriteBatch = SpriteBatch(spriteSheet); + + const source = Rect.fromLTWH(3 * tileSize, 0, tileSize, tileSize); + + spriteBatch.add( + source: source, + color: Colors.redAccent, + offset: Vector2.all(margin), + ); + + spriteBatch.add( + source: source, + offset: Vector2(2 * margin + tileSize, margin), + ); + + game.add( + SpriteBatchComponent( + spriteBatch: spriteBatch, + blendMode: BlendMode.srcOver, + ), + ); + }, + size: Vector2(3 * margin + 2 * tileSize, 2 * margin + tileSize), + backgroundColor: const Color(0xFFFFFFFF), + goldenFile: '_goldens/sprite_batch_test_1.png', + ); + + testGolden( + 'can render a batch without blend mode', + (game) async { + final spriteSheet = await loadImage('alphabet.png'); + final spriteBatch = SpriteBatch(spriteSheet); + + const source = Rect.fromLTWH(3 * tileSize, 0, tileSize, tileSize); + + spriteBatch.add( + source: source, + offset: Vector2.all(margin), + ); + + spriteBatch.add( + source: source, + offset: Vector2(2 * margin + tileSize, margin), + ); + + game.add( + SpriteBatchComponent( + spriteBatch: spriteBatch, + ), + ); + }, + size: Vector2(3 * margin + 2 * tileSize, 2 * margin + tileSize), + backgroundColor: const Color(0xFFFFFFFF), + goldenFile: '_goldens/sprite_batch_test_2.png', + ); }); } diff --git a/packages/flame_test/lib/src/test_golden.dart b/packages/flame_test/lib/src/test_golden.dart index 19bf55b7039..41061368882 100644 --- a/packages/flame_test/lib/src/test_golden.dart +++ b/packages/flame_test/lib/src/test_golden.dart @@ -34,13 +34,17 @@ void testGolden( PrepareGameFunction testBody, { required String goldenFile, Vector2? size, + Color? backgroundColor, FlameGame? game, bool skip = false, }) { testWidgets( testName, (tester) async { - final gameInstance = game ?? FlameGame(); + final gameInstance = game ?? + (backgroundColor != null + ? GameWithBackgroundColor(backgroundColor) + : FlameGame()); const myKey = ValueKey('game-instance'); await tester.runAsync(() async { @@ -73,3 +77,12 @@ void testGolden( } typedef PrepareGameFunction = Future Function(FlameGame game); + +class GameWithBackgroundColor extends FlameGame { + final Color _backgroundColor; + + GameWithBackgroundColor(this._backgroundColor); + + @override + Color backgroundColor() => _backgroundColor; +}