fix: rendering with `FilterQuality.medium` (#334)

* feat: defined HighFilterQualityCanvas

* refactor: removed trailing comma

* refactor: started defining CanvasComponent

* feat: implemented CanvasComponent

* docs: fixed typos

* docs: changed template name

* fix: merge conflict

* refactor: set filterQuality to Medium

* refactor: removed nullable from typdef

* test: updated tests to FilterQuality.medium
pull/344/head
Alejandro Santiago 3 years ago committed by GitHub
parent 155e316ba1
commit 5fb9a40e66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -40,13 +40,14 @@ class ScoringBehavior extends Component with HasGameRef<PinballGame> {
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
gameRef.read<GameBloc>().add(Scored(points: _points.value)); gameRef.read<GameBloc>().add(Scored(points: _points.value));
await gameRef.firstChild<ZCanvasComponent>()!.add( final canvas = gameRef.descendants().whereType<ZCanvasComponent>().single;
ScoreComponent( await canvas.add(
points: _points, ScoreComponent(
position: _position, points: _points,
effectController: _effectController, position: _position,
), effectController: _effectController,
); ),
);
} }
} }

@ -17,7 +17,7 @@ class FlutterForestBonusBehavior extends Component
final bumpers = parent.children.whereType<DashNestBumper>(); final bumpers = parent.children.whereType<DashNestBumper>();
final signpost = parent.firstChild<Signpost>()!; final signpost = parent.firstChild<Signpost>()!;
final animatronic = parent.firstChild<DashAnimatronic>()!; final animatronic = parent.firstChild<DashAnimatronic>()!;
final canvas = gameRef.firstChild<ZCanvasComponent>()!; final canvas = gameRef.descendants().whereType<ZCanvasComponent>().single;
for (final bumper in bumpers) { for (final bumper in bumpers) {
// TODO(alestiago): Refactor subscription management once the following is // TODO(alestiago): Refactor subscription management once the following is

@ -72,14 +72,23 @@ class PinballGame extends PinballForge2DGame
]; ];
await add( await add(
ZCanvasComponent( CanvasComponent(
onSpritePainted: (paint) {
if (paint.filterQuality != FilterQuality.medium) {
paint.filterQuality = FilterQuality.medium;
}
},
children: [ children: [
...machine, ZCanvasComponent(
...decals, children: [
...characterAreas, ...machine,
Drain(), ...decals,
BottomGroup(), ...characterAreas,
Launcher(), Drain(),
BottomGroup(),
Launcher(),
],
),
], ],
), ),
); );
@ -169,7 +178,7 @@ class _GameBallsController extends ComponentController<PinballGame>
plunger.body.position.x, plunger.body.position.x,
plunger.body.position.y - Ball.size.y, plunger.body.position.y - Ball.size.y,
); );
component.firstChild<ZCanvasComponent>()?.add(ball); component.descendants().whereType<ZCanvasComponent>().single.add(ball);
}); });
} }
} }
@ -203,9 +212,10 @@ class DebugPinballGame extends PinballGame with FPSCounter, PanDetector {
super.onTapUp(pointerId, info); super.onTapUp(pointerId, info);
if (info.raw.kind == PointerDeviceKind.mouse) { if (info.raw.kind == PointerDeviceKind.mouse) {
final canvas = descendants().whereType<ZCanvasComponent>().single;
final ball = ControlledBall.debug() final ball = ControlledBall.debug()
..initialPosition = info.eventPosition.game; ..initialPosition = info.eventPosition.game;
firstChild<ZCanvasComponent>()?.add(ball); canvas.add(ball);
} }
} }
@ -230,10 +240,11 @@ class DebugPinballGame extends PinballGame with FPSCounter, PanDetector {
} }
void _turboChargeBall(Vector2 line) { void _turboChargeBall(Vector2 line) {
final canvas = descendants().whereType<ZCanvasComponent>().single;
final ball = ControlledBall.debug()..initialPosition = lineStart!; final ball = ControlledBall.debug()..initialPosition = lineStart!;
final impulse = line * -1 * 10; final impulse = line * -1 * 10;
ball.add(BallTurboChargingBehavior(impulse: impulse)); ball.add(BallTurboChargingBehavior(impulse: impulse));
firstChild<ZCanvasComponent>()?.add(ball); canvas.add(ball);
} }
} }

@ -1,9 +1,9 @@
library pinball_flame; library pinball_flame;
export 'src/canvas/canvas.dart';
export 'src/component_controller.dart'; export 'src/component_controller.dart';
export 'src/contact_behavior.dart'; export 'src/contact_behavior.dart';
export 'src/keyboard_input_controller.dart'; export 'src/keyboard_input_controller.dart';
export 'src/parent_is_a.dart'; export 'src/parent_is_a.dart';
export 'src/pinball_forge2d_game.dart'; export 'src/pinball_forge2d_game.dart';
export 'src/sprite_animation.dart'; export 'src/sprite_animation.dart';
export 'src/z_canvas_component.dart';

@ -0,0 +1,2 @@
export 'canvas_component.dart';
export 'z_canvas_component.dart';

@ -0,0 +1,47 @@
import 'dart:ui';
import 'package:flame/components.dart';
import 'package:pinball_flame/src/canvas/canvas_wrapper.dart';
/// Called right before [Canvas.drawImageRect] is called.
///
/// This is useful since [Sprite.render] uses [Canvas.drawImageRect] to draw
/// the [Sprite].
typedef PaintFunction = void Function(Paint);
/// {@template canvas_component}
/// Allows listening before the rendering of [Sprite]s.
///
/// The existance of this class is to hack around the fact that Flame doesn't
/// provide a global way to modify the default [Paint] before rendering a
/// [Sprite].
/// {@endtemplate}
class CanvasComponent extends Component {
/// {@macro canvas_component}
CanvasComponent({
PaintFunction? onSpritePainted,
Iterable<Component>? children,
}) : _canvas = _Canvas(onSpritePainted: onSpritePainted),
super(children: children);
final _Canvas _canvas;
@override
void renderTree(Canvas canvas) {
_canvas.canvas = canvas;
super.renderTree(_canvas);
}
}
class _Canvas extends CanvasWrapper {
_Canvas({PaintFunction? onSpritePainted})
: _onSpritePainted = onSpritePainted;
final PaintFunction? _onSpritePainted;
@override
void drawImageRect(Image image, Rect src, Rect dst, Paint paint) {
_onSpritePainted?.call(paint);
super.drawImageRect(image, src, dst, paint);
}
}

@ -1,85 +1,11 @@
// ignore_for_file: public_member_api_docs
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui'; import 'dart:ui';
import 'package:flame/components.dart'; class CanvasWrapper implements Canvas {
/// {@template z_canvas_component}
/// Draws [ZIndex] components after the all non-[ZIndex] components have been
/// drawn.
/// {@endtemplate}
class ZCanvasComponent extends Component {
/// {@macro z_canvas_component}
ZCanvasComponent({
Iterable<Component>? children,
}) : _zCanvas = ZCanvas(),
super(children: children);
final ZCanvas _zCanvas;
@override
void renderTree(Canvas canvas) {
_zCanvas.canvas = canvas;
super.renderTree(_zCanvas);
_zCanvas.render();
}
}
/// Apply to any [Component] that will be rendered according to a
/// [ZIndex.zIndex].
///
/// [ZIndex] components must be descendants of a [ZCanvasComponent].
///
/// {@macro z_canvas.render}
mixin ZIndex on Component {
/// The z-index of this component.
///
/// The higher the value, the later the component will be drawn. Hence,
/// rendering in front of [Component]s with lower [zIndex] values.
int zIndex = 0;
@override
void renderTree(
Canvas canvas,
) {
if (canvas is ZCanvas) {
canvas.buffer(this);
} else {
super.renderTree(canvas);
}
}
}
/// The [ZCanvas] allows to postpone the rendering of [ZIndex] components.
///
/// You should not use this class directly.
class ZCanvas implements Canvas {
/// The [Canvas] to render to.
///
/// This is set by [ZCanvasComponent] when rendering.
late Canvas canvas; late Canvas canvas;
final List<ZIndex> _zBuffer = [];
/// Postpones the rendering of [ZIndex] component and its children.
void buffer(ZIndex component) => _zBuffer.add(component);
/// Renders all [ZIndex] components and their children.
///
/// {@template z_canvas.render}
/// The rendering order is defined by the parent [ZIndex]. The children of
/// the same parent are rendered in the order they were added.
///
/// If two [Component]s ever overlap each other, and have the same
/// [ZIndex.zIndex], there is no guarantee that the first one will be rendered
/// before the second one.
/// {@endtemplate}
void render() => _zBuffer
..sort((a, b) => a.zIndex.compareTo(b.zIndex))
..whereType<Component>().forEach(_render)
..clear();
void _render(Component component) => component.renderTree(canvas);
@override @override
void clipPath(Path path, {bool doAntiAlias = true}) => void clipPath(Path path, {bool doAntiAlias = true}) =>
canvas.clipPath(path, doAntiAlias: doAntiAlias); canvas.clipPath(path, doAntiAlias: doAntiAlias);

@ -0,0 +1,77 @@
import 'dart:ui';
import 'package:flame/components.dart';
import 'package:pinball_flame/src/canvas/canvas_wrapper.dart';
/// {@template z_canvas_component}
/// Draws [ZIndex] components after the all non-[ZIndex] components have been
/// drawn.
/// {@endtemplate}
class ZCanvasComponent extends Component {
/// {@macro z_canvas_component}
ZCanvasComponent({
Iterable<Component>? children,
}) : _zCanvas = _ZCanvas(),
super(children: children);
final _ZCanvas _zCanvas;
@override
void renderTree(Canvas canvas) {
_zCanvas.canvas = canvas;
super.renderTree(_zCanvas);
_zCanvas.render();
}
}
/// Apply to any [Component] that will be rendered according to a
/// [ZIndex.zIndex].
///
/// [ZIndex] components must be descendants of a [ZCanvasComponent].
///
/// {@macro z_canvas.render}
mixin ZIndex on Component {
/// The z-index of this component.
///
/// The higher the value, the later the component will be drawn. Hence,
/// rendering in front of [Component]s with lower [zIndex] values.
int zIndex = 0;
@override
void renderTree(
Canvas canvas,
) {
if (canvas is _ZCanvas) {
canvas.buffer(this);
} else {
super.renderTree(canvas);
}
}
}
/// The [_ZCanvas] allows to postpone the rendering of [ZIndex] components.
///
/// You should not use this class directly.
class _ZCanvas extends CanvasWrapper {
final List<ZIndex> _zBuffer = [];
/// Postpones the rendering of [ZIndex] component and its children.
void buffer(ZIndex component) => _zBuffer.add(component);
/// Renders all [ZIndex] components and their children.
///
/// {@template z_canvas.render}
/// The rendering order is defined by the parent [ZIndex]. The children of
/// the same parent are rendered in the order they were added.
///
/// If two [Component]s ever overlap each other, and have the same
/// [ZIndex.zIndex], there is no guarantee that the first one will be rendered
/// before the second one.
/// {@endtemplate}
void render() => _zBuffer
..sort((a, b) => a.zIndex.compareTo(b.zIndex))
..whereType<Component>().forEach(_render)
..clear();
void _render(Component component) => component.renderTree(canvas);
}

@ -0,0 +1,144 @@
// ignore_for_file: cascade_invocations
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_flame/src/canvas/canvas_component.dart';
class _TestSpriteComponent extends SpriteComponent {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('CanvasComponent', () {
final flameTester = FlameTester(FlameGame.new);
test('can be instantiated', () {
expect(
CanvasComponent(),
isA<CanvasComponent>(),
);
});
flameTester.test('loads correctly', (game) async {
final component = CanvasComponent();
await game.ensureAdd(component);
expect(game.contains(component), isTrue);
});
flameTester.test(
'adds children',
(game) async {
final component = Component();
final canvas = CanvasComponent(
onSpritePainted: (paint) => paint.filterQuality = FilterQuality.high,
children: [component],
);
await game.ensureAdd(canvas);
expect(
canvas.children.contains(component),
isTrue,
);
},
);
flameTester.testGameWidget(
'calls onSpritePainted when paiting a sprite',
setUp: (game, tester) async {
final spriteComponent = _TestSpriteComponent();
final completer = Completer<Image>();
decodeImageFromList(
Uint8List.fromList(_image),
completer.complete,
);
spriteComponent.sprite = Sprite(await completer.future);
var calls = 0;
final canvas = CanvasComponent(
onSpritePainted: (paint) => calls++,
children: [spriteComponent],
);
await game.ensureAdd(canvas);
await tester.pump();
expect(calls, equals(1));
},
);
});
}
const List<int> _image = <int>[
0x89,
0x50,
0x4E,
0x47,
0x0D,
0x0A,
0x1A,
0x0A,
0x00,
0x00,
0x00,
0x0D,
0x49,
0x48,
0x44,
0x52,
0x00,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x01,
0x08,
0x06,
0x00,
0x00,
0x00,
0x1F,
0x15,
0xC4,
0x89,
0x00,
0x00,
0x00,
0x0A,
0x49,
0x44,
0x41,
0x54,
0x78,
0x9C,
0x63,
0x00,
0x01,
0x00,
0x00,
0x05,
0x00,
0x01,
0x0D,
0x0A,
0x2D,
0xB4,
0x00,
0x00,
0x00,
0x00,
0x49,
0x45,
0x4E,
0x44,
0xAE,
];

@ -0,0 +1,353 @@
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/material.dart' hide Image;
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_flame/src/canvas/canvas_wrapper.dart';
class _MockCanvas extends Mock implements Canvas {}
class _MockImage extends Mock implements Image {}
class _MockPicture extends Mock implements Picture {}
class _MockParagraph extends Mock implements Paragraph {}
class _MockVertices extends Mock implements Vertices {}
void main() {
group('CanvasWrapper', () {
group('CanvasWrapper', () {
late Canvas canvas;
late Path path;
late RRect rRect;
late Rect rect;
late Paint paint;
late Image atlas;
late BlendMode blendMode;
late Color color;
late Offset offset;
late Float64List float64list;
late Float32List float32list;
late Int32List int32list;
late Picture picture;
late Paragraph paragraph;
late Vertices vertices;
setUp(() {
canvas = _MockCanvas();
path = Path();
rRect = RRect.zero;
rect = Rect.zero;
paint = Paint();
atlas = _MockImage();
blendMode = BlendMode.clear;
color = Colors.black;
offset = Offset.zero;
float64list = Float64List(1);
float32list = Float32List(1);
int32list = Int32List(1);
picture = _MockPicture();
paragraph = _MockParagraph();
vertices = _MockVertices();
});
test("clipPath calls Canvas's clipPath", () {
CanvasWrapper()
..canvas = canvas
..clipPath(path, doAntiAlias: false);
verify(
() => canvas.clipPath(path, doAntiAlias: false),
).called(1);
});
test("clipRRect calls Canvas's clipRRect", () {
CanvasWrapper()
..canvas = canvas
..clipRRect(rRect, doAntiAlias: false);
verify(
() => canvas.clipRRect(rRect, doAntiAlias: false),
).called(1);
});
test("clipRect calls Canvas's clipRect", () {
CanvasWrapper()
..canvas = canvas
..clipRect(rect, doAntiAlias: false);
verify(
() => canvas.clipRect(rect, doAntiAlias: false),
).called(1);
});
test("drawArc calls Canvas's drawArc", () {
CanvasWrapper()
..canvas = canvas
..drawArc(rect, 0, 1, false, paint);
verify(
() => canvas.drawArc(rect, 0, 1, false, paint),
).called(1);
});
test("drawAtlas calls Canvas's drawAtlas", () {
CanvasWrapper()
..canvas = canvas
..drawAtlas(atlas, [], [], [], blendMode, rect, paint);
verify(
() => canvas.drawAtlas(atlas, [], [], [], blendMode, rect, paint),
).called(1);
});
test("drawCircle calls Canvas's drawCircle", () {
CanvasWrapper()
..canvas = canvas
..drawCircle(offset, 0, paint);
verify(
() => canvas.drawCircle(offset, 0, paint),
).called(1);
});
test("drawColor calls Canvas's drawColor", () {
CanvasWrapper()
..canvas = canvas
..drawColor(color, blendMode);
verify(
() => canvas.drawColor(color, blendMode),
).called(1);
});
test("drawDRRect calls Canvas's drawDRRect", () {
CanvasWrapper()
..canvas = canvas
..drawDRRect(rRect, rRect, paint);
verify(
() => canvas.drawDRRect(rRect, rRect, paint),
).called(1);
});
test("drawImage calls Canvas's drawImage", () {
CanvasWrapper()
..canvas = canvas
..drawImage(atlas, offset, paint);
verify(
() => canvas.drawImage(atlas, offset, paint),
).called(1);
});
test("drawImageNine calls Canvas's drawImageNine", () {
CanvasWrapper()
..canvas = canvas
..drawImageNine(atlas, rect, rect, paint);
verify(
() => canvas.drawImageNine(atlas, rect, rect, paint),
).called(1);
});
test("drawImageRect calls Canvas's drawImageRect", () {
CanvasWrapper()
..canvas = canvas
..drawImageRect(atlas, rect, rect, paint);
verify(
() => canvas.drawImageRect(atlas, rect, rect, paint),
).called(1);
});
test("drawLine calls Canvas's drawLine", () {
CanvasWrapper()
..canvas = canvas
..drawLine(offset, offset, paint);
verify(
() => canvas.drawLine(offset, offset, paint),
).called(1);
});
test("drawOval calls Canvas's drawOval", () {
CanvasWrapper()
..canvas = canvas
..drawOval(rect, paint);
verify(
() => canvas.drawOval(rect, paint),
).called(1);
});
test("drawPaint calls Canvas's drawPaint", () {
CanvasWrapper()
..canvas = canvas
..drawPaint(paint);
verify(
() => canvas.drawPaint(paint),
).called(1);
});
test("drawParagraph calls Canvas's drawParagraph", () {
CanvasWrapper()
..canvas = canvas
..drawParagraph(paragraph, offset);
verify(
() => canvas.drawParagraph(paragraph, offset),
).called(1);
});
test("drawPath calls Canvas's drawPath", () {
CanvasWrapper()
..canvas = canvas
..drawPath(path, paint);
verify(
() => canvas.drawPath(path, paint),
).called(1);
});
test("drawPicture calls Canvas's drawPicture", () {
CanvasWrapper()
..canvas = canvas
..drawPicture(picture);
verify(
() => canvas.drawPicture(picture),
).called(1);
});
test("drawPoints calls Canvas's drawPoints", () {
CanvasWrapper()
..canvas = canvas
..drawPoints(PointMode.points, [offset], paint);
verify(
() => canvas.drawPoints(PointMode.points, [offset], paint),
).called(1);
});
test("drawRRect calls Canvas's drawRRect", () {
CanvasWrapper()
..canvas = canvas
..drawRRect(rRect, paint);
verify(
() => canvas.drawRRect(rRect, paint),
).called(1);
});
test("drawRawAtlas calls Canvas's drawRawAtlas", () {
CanvasWrapper()
..canvas = canvas
..drawRawAtlas(
atlas,
float32list,
float32list,
int32list,
BlendMode.clear,
rect,
paint,
);
verify(
() => canvas.drawRawAtlas(
atlas,
float32list,
float32list,
int32list,
BlendMode.clear,
rect,
paint,
),
).called(1);
});
test("drawRawPoints calls Canvas's drawRawPoints", () {
CanvasWrapper()
..canvas = canvas
..drawRawPoints(PointMode.points, float32list, paint);
verify(
() => canvas.drawRawPoints(PointMode.points, float32list, paint),
).called(1);
});
test("drawRect calls Canvas's drawRect", () {
CanvasWrapper()
..canvas = canvas
..drawRect(rect, paint);
verify(
() => canvas.drawRect(rect, paint),
).called(1);
});
test("drawShadow calls Canvas's drawShadow", () {
CanvasWrapper()
..canvas = canvas
..drawShadow(path, color, 0, false);
verify(
() => canvas.drawShadow(path, color, 0, false),
).called(1);
});
test("drawVertices calls Canvas's drawVertices", () {
CanvasWrapper()
..canvas = canvas
..drawVertices(vertices, blendMode, paint);
verify(
() => canvas.drawVertices(vertices, blendMode, paint),
).called(1);
});
test("getSaveCount calls Canvas's getSaveCount", () {
final canvasWrapper = CanvasWrapper()..canvas = canvas;
when(() => canvas.getSaveCount()).thenReturn(1);
canvasWrapper.getSaveCount();
verify(() => canvas.getSaveCount()).called(1);
expect(canvasWrapper.getSaveCount(), 1);
});
test("restore calls Canvas's restore", () {
CanvasWrapper()
..canvas = canvas
..restore();
verify(() => canvas.restore()).called(1);
});
test("rotate calls Canvas's rotate", () {
CanvasWrapper()
..canvas = canvas
..rotate(0);
verify(() => canvas.rotate(0)).called(1);
});
test("save calls Canvas's save", () {
CanvasWrapper()
..canvas = canvas
..save();
verify(() => canvas.save()).called(1);
});
test("saveLayer calls Canvas's saveLayer", () {
CanvasWrapper()
..canvas = canvas
..saveLayer(rect, paint);
verify(() => canvas.saveLayer(rect, paint)).called(1);
});
test("scale calls Canvas's scale", () {
CanvasWrapper()
..canvas = canvas
..scale(0, 0);
verify(() => canvas.scale(0, 0)).called(1);
});
test("skew calls Canvas's skew", () {
CanvasWrapper()
..canvas = canvas
..skew(0, 0);
verify(() => canvas.skew(0, 0)).called(1);
});
test("transform calls Canvas's transform", () {
CanvasWrapper()
..canvas = canvas
..transform(float64list);
verify(() => canvas.transform(float64list)).called(1);
});
test("translate calls Canvas's translate", () {
CanvasWrapper()
..canvas = canvas
..translate(0, 0);
verify(() => canvas.translate(0, 0)).called(1);
});
});
});
}

@ -0,0 +1,80 @@
// ignore_for_file: cascade_invocations
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter/material.dart' hide Image;
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_flame/pinball_flame.dart';
class _TestCircleComponent extends CircleComponent with ZIndex {
_TestCircleComponent(Color color)
: super(
paint: Paint()..color = color,
radius: 10,
);
}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('ZCanvasComponent', () {
final flameTester = FlameTester(FlameGame.new);
const goldensFilePath = '../goldens/rendering/';
test('can be instantiated', () {
expect(
ZCanvasComponent(),
isA<ZCanvasComponent>(),
);
});
flameTester.test('loads correctly', (game) async {
final component = ZCanvasComponent();
await game.ensureAdd(component);
expect(game.contains(component), isTrue);
});
flameTester.testGameWidget(
'red circle renders behind blue circle',
setUp: (game, tester) async {
final canvas = ZCanvasComponent(
children: [
_TestCircleComponent(Colors.blue)..zIndex = 1,
_TestCircleComponent(Colors.red)..zIndex = 0,
],
);
await game.ensureAdd(canvas);
game.camera.followVector2(Vector2.zero());
},
verify: (game, tester) async {
await expectLater(
find.byGame<FlameGame>(),
matchesGoldenFile('${goldensFilePath}red_blue.png'),
);
},
);
flameTester.testGameWidget(
'blue circle renders behind red circle',
setUp: (game, tester) async {
final canvas = ZCanvasComponent(
children: [
_TestCircleComponent(Colors.blue)..zIndex = 0,
_TestCircleComponent(Colors.red)..zIndex = 1
],
);
await game.ensureAdd(canvas);
game.camera.followVector2(Vector2.zero());
},
verify: (game, tester) async {
await expectLater(
find.byGame<FlameGame>(),
matchesGoldenFile('${goldensFilePath}blue_red.png'),
);
},
);
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

@ -1,385 +0,0 @@
// ignore_for_file: cascade_invocations
import 'dart:typed_data';
import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/game.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 'package:pinball_flame/pinball_flame.dart';
class _TestCircleComponent extends CircleComponent with ZIndex {
_TestCircleComponent(Color color)
: super(
paint: Paint()..color = color,
radius: 10,
);
}
class _MockCanvas extends Mock implements Canvas {}
class _MockImage extends Mock implements Image {}
class _MockPicture extends Mock implements Picture {}
class _MockParagraph extends Mock implements Paragraph {}
class _MockVertices extends Mock implements Vertices {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(FlameGame.new);
const goldenPrefix = 'golden/rendering/';
group('ZCanvasComponent', () {
flameTester.test('loads correctly', (game) async {
final component = ZCanvasComponent();
await game.ensureAdd(component);
expect(game.contains(component), isTrue);
});
flameTester.testGameWidget(
'red circle renders behind blue circle',
setUp: (game, tester) async {
final canvas = ZCanvasComponent(
children: [
_TestCircleComponent(Colors.blue)..zIndex = 1,
_TestCircleComponent(Colors.red)..zIndex = 0,
],
);
await game.ensureAdd(canvas);
game.camera.followVector2(Vector2.zero());
},
verify: (game, tester) async {
await expectLater(
find.byGame<FlameGame>(),
matchesGoldenFile('${goldenPrefix}red_blue.png'),
);
},
);
flameTester.testGameWidget(
'blue circle renders behind red circle',
setUp: (game, tester) async {
final canvas = ZCanvasComponent(
children: [
_TestCircleComponent(Colors.blue)..zIndex = 0,
_TestCircleComponent(Colors.red)..zIndex = 1
],
);
await game.ensureAdd(canvas);
game.camera.followVector2(Vector2.zero());
},
verify: (game, tester) async {
await expectLater(
find.byGame<FlameGame>(),
matchesGoldenFile('${goldenPrefix}blue_red.png'),
);
},
);
});
group('ZCanvas', () {
late Canvas canvas;
late Path path;
late RRect rRect;
late Rect rect;
late Paint paint;
late Image atlas;
late BlendMode blendMode;
late Color color;
late Offset offset;
late Float64List float64list;
late Float32List float32list;
late Int32List int32list;
late Picture picture;
late Paragraph paragraph;
late Vertices vertices;
setUp(() {
canvas = _MockCanvas();
path = Path();
rRect = RRect.zero;
rect = Rect.zero;
paint = Paint();
atlas = _MockImage();
blendMode = BlendMode.clear;
color = Colors.black;
offset = Offset.zero;
float64list = Float64List(1);
float32list = Float32List(1);
int32list = Int32List(1);
picture = _MockPicture();
paragraph = _MockParagraph();
vertices = _MockVertices();
});
test("clipPath calls Canvas's clipPath", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.clipPath(path, doAntiAlias: false);
verify(
() => canvas.clipPath(path, doAntiAlias: false),
).called(1);
});
test("clipRRect calls Canvas's clipRRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.clipRRect(rRect, doAntiAlias: false);
verify(
() => canvas.clipRRect(rRect, doAntiAlias: false),
).called(1);
});
test("clipRect calls Canvas's clipRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.clipRect(rect, doAntiAlias: false);
verify(
() => canvas.clipRect(rect, doAntiAlias: false),
).called(1);
});
test("drawArc calls Canvas's drawArc", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawArc(rect, 0, 1, false, paint);
verify(
() => canvas.drawArc(rect, 0, 1, false, paint),
).called(1);
});
test("drawAtlas calls Canvas's drawAtlas", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawAtlas(atlas, [], [], [], blendMode, rect, paint);
verify(
() => canvas.drawAtlas(atlas, [], [], [], blendMode, rect, paint),
).called(1);
});
test("drawCircle calls Canvas's drawCircle", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawCircle(offset, 0, paint);
verify(
() => canvas.drawCircle(offset, 0, paint),
).called(1);
});
test("drawColor calls Canvas's drawColor", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawColor(color, blendMode);
verify(
() => canvas.drawColor(color, blendMode),
).called(1);
});
test("drawDRRect calls Canvas's drawDRRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawDRRect(rRect, rRect, paint);
verify(
() => canvas.drawDRRect(rRect, rRect, paint),
).called(1);
});
test("drawImage calls Canvas's drawImage", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawImage(atlas, offset, paint);
verify(
() => canvas.drawImage(atlas, offset, paint),
).called(1);
});
test("drawImageNine calls Canvas's drawImageNine", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawImageNine(atlas, rect, rect, paint);
verify(
() => canvas.drawImageNine(atlas, rect, rect, paint),
).called(1);
});
test("drawImageRect calls Canvas's drawImageRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawImageRect(atlas, rect, rect, paint);
verify(
() => canvas.drawImageRect(atlas, rect, rect, paint),
).called(1);
});
test("drawLine calls Canvas's drawLine", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawLine(offset, offset, paint);
verify(
() => canvas.drawLine(offset, offset, paint),
).called(1);
});
test("drawOval calls Canvas's drawOval", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawOval(rect, paint);
verify(
() => canvas.drawOval(rect, paint),
).called(1);
});
test("drawPaint calls Canvas's drawPaint", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawPaint(paint);
verify(
() => canvas.drawPaint(paint),
).called(1);
});
test("drawParagraph calls Canvas's drawParagraph", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawParagraph(paragraph, offset);
verify(
() => canvas.drawParagraph(paragraph, offset),
).called(1);
});
test("drawPath calls Canvas's drawPath", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawPath(path, paint);
verify(
() => canvas.drawPath(path, paint),
).called(1);
});
test("drawPicture calls Canvas's drawPicture", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawPicture(picture);
verify(
() => canvas.drawPicture(picture),
).called(1);
});
test("drawPoints calls Canvas's drawPoints", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawPoints(PointMode.points, [offset], paint);
verify(
() => canvas.drawPoints(PointMode.points, [offset], paint),
).called(1);
});
test("drawRRect calls Canvas's drawRRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawRRect(rRect, paint);
verify(
() => canvas.drawRRect(rRect, paint),
).called(1);
});
test("drawRawAtlas calls Canvas's drawRawAtlas", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawRawAtlas(
atlas,
float32list,
float32list,
int32list,
BlendMode.clear,
rect,
paint,
);
verify(
() => canvas.drawRawAtlas(
atlas,
float32list,
float32list,
int32list,
BlendMode.clear,
rect,
paint,
),
).called(1);
});
test("drawRawPoints calls Canvas's drawRawPoints", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawRawPoints(PointMode.points, float32list, paint);
verify(
() => canvas.drawRawPoints(PointMode.points, float32list, paint),
).called(1);
});
test("drawRect calls Canvas's drawRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawRect(rect, paint);
verify(
() => canvas.drawRect(rect, paint),
).called(1);
});
test("drawShadow calls Canvas's drawShadow", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawShadow(path, color, 0, false);
verify(
() => canvas.drawShadow(path, color, 0, false),
).called(1);
});
test("drawVertices calls Canvas's drawVertices", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawVertices(vertices, blendMode, paint);
verify(
() => canvas.drawVertices(vertices, blendMode, paint),
).called(1);
});
test("getSaveCount calls Canvas's getSaveCount", () {
final zcanvas = ZCanvas()..canvas = canvas;
when(() => canvas.getSaveCount()).thenReturn(1);
zcanvas.getSaveCount();
verify(() => canvas.getSaveCount()).called(1);
});
test("restore calls Canvas's restore", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.restore();
verify(() => canvas.restore()).called(1);
});
test("rotate calls Canvas's rotate", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.rotate(0);
verify(() => canvas.rotate(0)).called(1);
});
test("save calls Canvas's save", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.save();
verify(() => canvas.save()).called(1);
});
test("saveLayer calls Canvas's saveLayer", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.saveLayer(rect, paint);
verify(() => canvas.saveLayer(rect, paint)).called(1);
});
test("scale calls Canvas's scale", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.scale(0, 0);
verify(() => canvas.scale(0, 0)).called(1);
});
test("skew calls Canvas's skew", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.skew(0, 0);
verify(() => canvas.skew(0, 0)).called(1);
});
test("transform calls Canvas's transform", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.transform(float64list);
verify(() => canvas.transform(float64list)).called(1);
});
test("translate calls Canvas's translate", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.translate(0, 0);
verify(() => canvas.translate(0, 0)).called(1);
});
});
}

@ -1,6 +1,9 @@
// ignore_for_file: cascade_invocations // ignore_for_file: cascade_invocations
import 'dart:ui';
import 'package:bloc_test/bloc_test.dart'; import 'package:bloc_test/bloc_test.dart';
import 'package:flame/components.dart';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
@ -233,14 +236,40 @@ void main() {
}, },
); );
flameBlocTester.test( flameBlocTester.test('one SkillShot', (game) async {
'one SkillShot', await game.ready();
(game) async { expect(
game.descendants().whereType<SkillShot>().length,
equals(1),
);
});
flameBlocTester.testGameWidget(
'paints sprites with FilterQuality.medium',
setUp: (game, tester) async {
await game.images.loadAll(assets);
await game.ready(); await game.ready();
final descendants = game.descendants();
final components = [
...descendants.whereType<SpriteComponent>(),
...descendants.whereType<SpriteGroupComponent>(),
];
expect(components, isNotEmpty);
expect( expect(
game.descendants().whereType<SkillShot>().length, components.whereType<HasPaint>().length,
equals(1), equals(components.length),
); );
await tester.pump();
for (final component in components) {
if (component is! HasPaint) return;
expect(
component.paint.filterQuality,
equals(FilterQuality.medium),
);
}
}, },
); );
@ -308,28 +337,25 @@ void main() {
); );
}); });
group( group('onNewState', () {
'onNewState', flameTester.test(
() { 'spawns a ball',
flameTester.test( (game) async {
'spawns a ball', final previousBalls =
(game) async { game.descendants().whereType<ControlledBall>().toList();
final previousBalls =
game.descendants().whereType<ControlledBall>().toList(); game.controller.onNewState(_MockGameState());
await game.ready();
game.controller.onNewState(_MockGameState()); final currentBalls =
await game.ready(); game.descendants().whereType<ControlledBall>().toList();
final currentBalls =
game.descendants().whereType<ControlledBall>().toList(); expect(
currentBalls.length,
expect( equals(previousBalls.length + 1),
currentBalls.length, );
equals(previousBalls.length + 1), },
); );
}, });
);
},
);
}); });
}); });

Loading…
Cancel
Save