diff --git a/lib/game/components/alien_zone.dart b/lib/game/components/alien_zone.dart new file mode 100644 index 00000000..3d8b75ae --- /dev/null +++ b/lib/game/components/alien_zone.dart @@ -0,0 +1,95 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball/flame/flame.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// {@template alien_zone} +/// Area positioned below [Spaceship] where the [Ball] +/// can bounce off [AlienBumper]s. +/// +/// When a [Ball] hits [AlienBumper]s, they toggle between activated and +/// deactivated states. +/// {@endtemplate} +class AlienZone extends Component with HasGameRef { + /// {@macro alien_zone} + AlienZone(); + + @override + Future onLoad() async { + await super.onLoad(); + + gameRef.addContactCallback(_ControlledAlienBumperBallContactCallback()); + + final lowerBumper = ControlledAlienBumper.a() + ..initialPosition = Vector2(-32.52, 9.34); + final upperBumper = ControlledAlienBumper.b() + ..initialPosition = Vector2(-22.89, 17.43); + + await addAll([ + lowerBumper, + upperBumper, + ]); + } +} + +/// {@template controlled_alien_bumper} +/// [AlienBumper] with [_AlienBumperController] attached. +/// {@endtemplate} +@visibleForTesting +class ControlledAlienBumper extends AlienBumper + with Controls<_AlienBumperController>, ScorePoints { + /// {@macro controlled_alien_bumper} + ControlledAlienBumper.a() : super.a() { + controller = _AlienBumperController(this); + } + + /// {@macro controlled_alien_bumper} + ControlledAlienBumper.b() : super.b() { + controller = _AlienBumperController(this); + } + + @override + // TODO(ruimiguel): change points when get final points map. + int get points => 20; +} + +/// {@template alien_bumper_controller} +/// Controls a [AlienBumper]. +/// {@endtemplate} +class _AlienBumperController extends ComponentController + with HasGameRef { + /// {@macro alien_bumper_controller} + _AlienBumperController(AlienBumper alienBumper) : super(alienBumper); + + /// Flag for activated state of the [AlienBumper]. + /// + /// Used to toggle [AlienBumper]s' state between activated and deactivated. + bool isActivated = false; + + /// Registers when a [AlienBumper] is hit by a [Ball]. + void hit() { + if (isActivated) { + component.deactivate(); + } else { + component.activate(); + } + isActivated = !isActivated; + } +} + +/// Listens when a [Ball] bounces bounces against a [AlienBumper]. +class _ControlledAlienBumperBallContactCallback + extends ContactCallback, Ball> { + @override + void begin( + Controls<_AlienBumperController> controlledAlienBumper, + Ball _, + Contact __, + ) { + controlledAlienBumper.controller.hit(); + } +} diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index a5540614..6a9d6a3d 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -1,3 +1,4 @@ +export 'alien_zone.dart'; export 'board.dart'; export 'bonus_word.dart'; export 'camera_controller.dart'; diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index df602184..cad2eac5 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -53,6 +53,7 @@ class PinballGame extends Forge2DGame await add(plunger); unawaited(add(Board())); + unawaited(add(AlienZone())); unawaited(add(SparkyFireZone())); unawaited(addFromBlueprint(Slingshots())); unawaited(addFromBlueprint(DinoWalls())); diff --git a/packages/pinball_components/assets/images/alien_bumper/a/active.png b/packages/pinball_components/assets/images/alien_bumper/a/active.png new file mode 100644 index 00000000..92943dfc Binary files /dev/null and b/packages/pinball_components/assets/images/alien_bumper/a/active.png differ diff --git a/packages/pinball_components/assets/images/alien_bumper/a/inactive.png b/packages/pinball_components/assets/images/alien_bumper/a/inactive.png new file mode 100644 index 00000000..99745fcf Binary files /dev/null and b/packages/pinball_components/assets/images/alien_bumper/a/inactive.png differ diff --git a/packages/pinball_components/assets/images/alien_bumper/b/active.png b/packages/pinball_components/assets/images/alien_bumper/b/active.png new file mode 100644 index 00000000..ec7dc0cb Binary files /dev/null and b/packages/pinball_components/assets/images/alien_bumper/b/active.png differ diff --git a/packages/pinball_components/assets/images/alien_bumper/b/inactive.png b/packages/pinball_components/assets/images/alien_bumper/b/inactive.png new file mode 100644 index 00000000..23f7f4b7 Binary files /dev/null and b/packages/pinball_components/assets/images/alien_bumper/b/inactive.png differ diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index 7b535ce8..63e28183 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -10,6 +10,8 @@ import 'package:flutter/widgets.dart'; class $AssetsImagesGen { const $AssetsImagesGen(); + $AssetsImagesAlienBumperGen get alienBumper => + const $AssetsImagesAlienBumperGen(); $AssetsImagesBackboardGen get backboard => const $AssetsImagesBackboardGen(); /// File path: assets/images/ball.png @@ -38,6 +40,13 @@ class $AssetsImagesGen { $AssetsImagesSparkyGen get sparky => const $AssetsImagesSparkyGen(); } +class $AssetsImagesAlienBumperGen { + const $AssetsImagesAlienBumperGen(); + + $AssetsImagesAlienBumperAGen get a => const $AssetsImagesAlienBumperAGen(); + $AssetsImagesAlienBumperBGen get b => const $AssetsImagesAlienBumperBGen(); +} + class $AssetsImagesBackboardGen { const $AssetsImagesBackboardGen(); @@ -226,6 +235,30 @@ class $AssetsImagesSparkyGen { const $AssetsImagesSparkyComputerGen(); } +class $AssetsImagesAlienBumperAGen { + const $AssetsImagesAlienBumperAGen(); + + /// File path: assets/images/alien_bumper/a/active.png + AssetGenImage get active => + const AssetGenImage('assets/images/alien_bumper/a/active.png'); + + /// File path: assets/images/alien_bumper/a/inactive.png + AssetGenImage get inactive => + const AssetGenImage('assets/images/alien_bumper/a/inactive.png'); +} + +class $AssetsImagesAlienBumperBGen { + const $AssetsImagesAlienBumperBGen(); + + /// File path: assets/images/alien_bumper/b/active.png + AssetGenImage get active => + const AssetGenImage('assets/images/alien_bumper/b/active.png'); + + /// File path: assets/images/alien_bumper/b/inactive.png + AssetGenImage get inactive => + const AssetGenImage('assets/images/alien_bumper/b/inactive.png'); +} + class $AssetsImagesDashBumperGen { const $AssetsImagesDashBumperGen(); @@ -283,42 +316,6 @@ class $AssetsImagesSparkyComputerGen { const AssetGenImage('assets/images/sparky/computer/top.png'); } -class $AssetsImagesSparkyBumperAGen { - const $AssetsImagesSparkyBumperAGen(); - - /// File path: assets/images/sparky/bumper/a/active.png - AssetGenImage get active => - const AssetGenImage('assets/images/sparky/bumper/a/active.png'); - - /// File path: assets/images/sparky/bumper/a/inactive.png - AssetGenImage get inactive => - const AssetGenImage('assets/images/sparky/bumper/a/inactive.png'); -} - -class $AssetsImagesSparkyBumperBGen { - const $AssetsImagesSparkyBumperBGen(); - - /// File path: assets/images/sparky/bumper/b/active.png - AssetGenImage get active => - const AssetGenImage('assets/images/sparky/bumper/b/active.png'); - - /// File path: assets/images/sparky/bumper/b/inactive.png - AssetGenImage get inactive => - const AssetGenImage('assets/images/sparky/bumper/b/inactive.png'); -} - -class $AssetsImagesSparkyBumperCGen { - const $AssetsImagesSparkyBumperCGen(); - - /// File path: assets/images/sparky/bumper/c/active.png - AssetGenImage get active => - const AssetGenImage('assets/images/sparky/bumper/c/active.png'); - - /// File path: assets/images/sparky/bumper/c/inactive.png - AssetGenImage get inactive => - const AssetGenImage('assets/images/sparky/bumper/c/inactive.png'); -} - class $AssetsImagesDashBumperAGen { const $AssetsImagesDashBumperAGen(); @@ -355,6 +352,42 @@ class $AssetsImagesDashBumperMainGen { const AssetGenImage('assets/images/dash/bumper/main/inactive.png'); } +class $AssetsImagesSparkyBumperAGen { + const $AssetsImagesSparkyBumperAGen(); + + /// File path: assets/images/sparky/bumper/a/active.png + AssetGenImage get active => + const AssetGenImage('assets/images/sparky/bumper/a/active.png'); + + /// File path: assets/images/sparky/bumper/a/inactive.png + AssetGenImage get inactive => + const AssetGenImage('assets/images/sparky/bumper/a/inactive.png'); +} + +class $AssetsImagesSparkyBumperBGen { + const $AssetsImagesSparkyBumperBGen(); + + /// File path: assets/images/sparky/bumper/b/active.png + AssetGenImage get active => + const AssetGenImage('assets/images/sparky/bumper/b/active.png'); + + /// File path: assets/images/sparky/bumper/b/inactive.png + AssetGenImage get inactive => + const AssetGenImage('assets/images/sparky/bumper/b/inactive.png'); +} + +class $AssetsImagesSparkyBumperCGen { + const $AssetsImagesSparkyBumperCGen(); + + /// File path: assets/images/sparky/bumper/c/active.png + AssetGenImage get active => + const AssetGenImage('assets/images/sparky/bumper/c/active.png'); + + /// File path: assets/images/sparky/bumper/c/inactive.png + AssetGenImage get inactive => + const AssetGenImage('assets/images/sparky/bumper/c/inactive.png'); +} + class Assets { Assets._(); diff --git a/packages/pinball_components/lib/src/components/alien_bumper.dart b/packages/pinball_components/lib/src/components/alien_bumper.dart new file mode 100644 index 00000000..75b0560d --- /dev/null +++ b/packages/pinball_components/lib/src/components/alien_bumper.dart @@ -0,0 +1,109 @@ +import 'dart:math' as math; + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// {@template alien_bumper} +/// Bumper for Alien area. +/// {@endtemplate} +// TODO(ruimiguel): refactor later to unify with DashBumpers. +class AlienBumper extends BodyComponent with InitialPosition { + /// {@macro alien_bumper} + AlienBumper._({ + required double majorRadius, + required double minorRadius, + required String activeAssetPath, + required String inactiveAssetPath, + required SpriteComponent spriteComponent, + }) : _majorRadius = majorRadius, + _minorRadius = minorRadius, + _activeAssetPath = activeAssetPath, + _inactiveAssetPath = inactiveAssetPath, + _spriteComponent = spriteComponent; + + /// {@macro alien_bumper} + AlienBumper.a() + : this._( + majorRadius: 3.52, + minorRadius: 2.97, + activeAssetPath: Assets.images.alienBumper.a.active.keyName, + inactiveAssetPath: Assets.images.alienBumper.a.inactive.keyName, + spriteComponent: SpriteComponent( + anchor: Anchor.center, + position: Vector2(0, -0.1), + ), + ); + + /// {@macro alien_bumper} + AlienBumper.b() + : this._( + majorRadius: 3.19, + minorRadius: 2.79, + activeAssetPath: Assets.images.alienBumper.b.active.keyName, + inactiveAssetPath: Assets.images.alienBumper.b.inactive.keyName, + spriteComponent: SpriteComponent( + anchor: Anchor.center, + position: Vector2(0, -0.1), + ), + ); + + final double _majorRadius; + final double _minorRadius; + final String _activeAssetPath; + late final Sprite _activeSprite; + final String _inactiveAssetPath; + late final Sprite _inactiveSprite; + final SpriteComponent _spriteComponent; + + @override + Future onLoad() async { + await super.onLoad(); + renderBody = false; + + await _loadSprites(); + + deactivate(); + await add(_spriteComponent); + } + + @override + Body createBody() { + final shape = EllipseShape( + center: Vector2.zero(), + majorRadius: _majorRadius, + minorRadius: _minorRadius, + )..rotate(15.9 * math.pi / 180); + final fixtureDef = FixtureDef(shape) + ..friction = 0 + ..restitution = 4; + + final bodyDef = BodyDef() + ..position = initialPosition + ..userData = this; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } + + Future _loadSprites() async { + // TODO(alestiago): I think ideally we would like to do: + // Sprite(path).load so we don't require to store the activeAssetPath and + // the inactive assetPath. + _inactiveSprite = await gameRef.loadSprite(_inactiveAssetPath); + _activeSprite = await gameRef.loadSprite(_activeAssetPath); + } + + /// Activates the [AlienBumper]. + void activate() { + _spriteComponent + ..sprite = _activeSprite + ..size = _activeSprite.originalSize / 10; + } + + /// Deactivates the [AlienBumper]. + void deactivate() { + _spriteComponent + ..sprite = _inactiveSprite + ..size = _inactiveSprite.originalSize / 10; + } +} diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index 0348e4df..acb8a7c5 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -1,3 +1,4 @@ +export 'alien_bumper.dart'; export 'backboard.dart'; export 'ball.dart'; export 'baseboard.dart'; diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index fc44a74a..1c54095a 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -52,6 +52,8 @@ flutter: - assets/images/kicker/ - assets/images/plunger/ - assets/images/slingshot/ + - assets/images/alien_bumper/a/ + - assets/images/alien_bumper/b/ - assets/images/sparky/computer/ - assets/images/sparky/bumper/a/ - assets/images/sparky/bumper/b/ diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index 1e4aab5e..0bfcd175 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -24,6 +24,7 @@ void main() { addPlungerStories(dashbook); addSlingshotStories(dashbook); addSparkyBumperStories(dashbook); + addAlienZoneStories(dashbook); addZoomStories(dashbook); addBoundariesStories(dashbook); addGoogleWordStories(dashbook); diff --git a/packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_a_game.dart b/packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_a_game.dart new file mode 100644 index 00000000..007d67e1 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_a_game.dart @@ -0,0 +1,28 @@ +import 'dart:async'; + +import 'package:flame/extensions.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; + +class AlienBumperAGame extends BasicBallGame { + AlienBumperAGame() : super(color: const Color(0xFF0000FF)); + + static const info = ''' + Shows how a AlienBumperA is rendered. + + - Activate the "trace" parameter to overlay the body. +'''; + + @override + Future onLoad() async { + await super.onLoad(); + + final center = screenToWorld(camera.viewport.canvasSize! / 2); + final alienBumperA = AlienBumper.a() + ..initialPosition = Vector2(center.x - 20, center.y - 20) + ..priority = 1; + await add(alienBumperA); + + await traceAllBodies(); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_b_game.dart b/packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_b_game.dart new file mode 100644 index 00000000..fada1dd9 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_b_game.dart @@ -0,0 +1,28 @@ +import 'dart:async'; + +import 'package:flame/extensions.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; + +class AlienBumperBGame extends BasicBallGame { + AlienBumperBGame() : super(color: const Color(0xFF0000FF)); + + static const info = ''' + Shows how a AlienBumperB is rendered. + + - Activate the "trace" parameter to overlay the body. +'''; + + @override + Future onLoad() async { + await super.onLoad(); + + final center = screenToWorld(camera.viewport.canvasSize! / 2); + final alienBumperB = AlienBumper.b() + ..initialPosition = Vector2(center.x - 10, center.y + 10) + ..priority = 1; + await add(alienBumperB); + + await traceAllBodies(); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/alien_zone/stories.dart b/packages/pinball_components/sandbox/lib/stories/alien_zone/stories.dart new file mode 100644 index 00000000..4bc758f9 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/alien_zone/stories.dart @@ -0,0 +1,25 @@ +import 'package:dashbook/dashbook.dart'; +import 'package:flame/game.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/alien_zone/alien_bumper_a_game.dart'; +import 'package:sandbox/stories/alien_zone/alien_bumper_b_game.dart'; + +void addAlienZoneStories(Dashbook dashbook) { + dashbook.storiesOf('Alien Zone') + ..add( + 'Alien Bumper A', + (context) => GameWidget( + game: AlienBumperAGame()..trace = context.boolProperty('Trace', true), + ), + codeLink: buildSourceLink('alien_zone/alien_bumper_a.dart'), + info: AlienBumperAGame.info, + ) + ..add( + 'Alien Bumper B', + (context) => GameWidget( + game: AlienBumperBGame()..trace = context.boolProperty('Trace', true), + ), + codeLink: buildSourceLink('alien_zone/alien_bumper_b.dart'), + info: AlienBumperAGame.info, + ); +} diff --git a/packages/pinball_components/sandbox/lib/stories/stories.dart b/packages/pinball_components/sandbox/lib/stories/stories.dart index 7dd02878..cdcf0825 100644 --- a/packages/pinball_components/sandbox/lib/stories/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/stories.dart @@ -1,3 +1,4 @@ +export 'alien_zone/stories.dart'; export 'ball/stories.dart'; export 'baseboard/stories.dart'; export 'boundaries/stories.dart'; diff --git a/packages/pinball_components/test/src/components/alien_bumper_test.dart b/packages/pinball_components/test/src/components/alien_bumper_test.dart new file mode 100644 index 00000000..cd55b62e --- /dev/null +++ b/packages/pinball_components/test/src/components/alien_bumper_test.dart @@ -0,0 +1,68 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame/components.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(TestGame.new); + + group('AlienBumper', () { + flameTester.test('"a" loads correctly', (game) async { + final bumper = AlienBumper.a(); + await game.ensureAdd(bumper); + + expect(game.contains(bumper), isTrue); + }); + + flameTester.test('"b" loads correctly', (game) async { + final bumper = AlienBumper.b(); + await game.ensureAdd(bumper); + expect(game.contains(bumper), isTrue); + }); + + flameTester.test('activate returns normally', (game) async { + final bumper = AlienBumper.a(); + await game.ensureAdd(bumper); + + expect(bumper.activate, returnsNormally); + }); + + flameTester.test('deactivate returns normally', (game) async { + final bumper = AlienBumper.a(); + await game.ensureAdd(bumper); + + expect(bumper.deactivate, returnsNormally); + }); + + flameTester.test('changes sprite', (game) async { + final bumper = AlienBumper.a(); + await game.ensureAdd(bumper); + + final spriteComponent = bumper.firstChild()!; + + final deactivatedSprite = spriteComponent.sprite; + bumper.activate(); + expect( + spriteComponent.sprite, + isNot(equals(deactivatedSprite)), + ); + + final activatedSprite = spriteComponent.sprite; + bumper.deactivate(); + expect( + spriteComponent.sprite, + isNot(equals(activatedSprite)), + ); + + expect( + activatedSprite, + isNot(equals(deactivatedSprite)), + ); + }); + }); +} diff --git a/test/game/components/alien_zone_test.dart b/test/game/components/alien_zone_test.dart new file mode 100644 index 00000000..68a2c2f1 --- /dev/null +++ b/test/game/components/alien_zone_test.dart @@ -0,0 +1,115 @@ +// ignore_for_file: cascade_invocations + +import 'dart:ui'; + +import 'package:bloc_test/bloc_test.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(EmptyPinballGameTest.new); + + group('AlienZone', () { + flameTester.test( + 'loads correctly', + (game) async { + await game.ready(); + final alienZone = AlienZone(); + await game.ensureAdd(alienZone); + + expect(game.contains(alienZone), isTrue); + }, + ); + + group('loads', () { + flameTester.test( + 'two AlienBumper', + (game) async { + await game.ready(); + final alienZone = AlienZone(); + await game.ensureAdd(alienZone); + + expect( + alienZone.descendants().whereType().length, + equals(2), + ); + }, + ); + }); + + group('bumpers', () { + late ControlledAlienBumper controlledAlienBumper; + late GameBloc gameBloc; + + setUp(() { + gameBloc = MockGameBloc(); + }); + + final flameBlocTester = FlameBlocTester( + gameBuilder: EmptyPinballGameTest.new, + blocBuilder: () => gameBloc, + ); + + flameTester.testGameWidget( + 'activate when deactivated bumper is hit', + setUp: (game, tester) async { + controlledAlienBumper = ControlledAlienBumper.a(); + await game.ensureAdd(controlledAlienBumper); + + controlledAlienBumper.controller.hit(); + }, + verify: (game, tester) async { + expect(controlledAlienBumper.controller.isActivated, isTrue); + }, + ); + + flameTester.testGameWidget( + 'deactivate when activated bumper is hit', + setUp: (game, tester) async { + controlledAlienBumper = ControlledAlienBumper.a(); + await game.ensureAdd(controlledAlienBumper); + + controlledAlienBumper.controller.hit(); + controlledAlienBumper.controller.hit(); + }, + verify: (game, tester) async { + expect(controlledAlienBumper.controller.isActivated, isFalse); + }, + ); + + flameBlocTester.testGameWidget( + 'add Scored event', + setUp: (game, tester) async { + final ball = Ball(baseColor: const Color(0xFF00FFFF)); + final alienZone = AlienZone(); + whenListen( + gameBloc, + const Stream.empty(), + initialState: const GameState.initial(), + ); + + await game.ensureAdd(alienZone); + await game.ensureAdd(ball); + game.addContactCallback(BallScorePointsCallback(game)); + + final bumpers = alienZone.descendants().whereType(); + + for (final bumper in bumpers) { + beginContact(game, bumper, ball); + verify( + () => gameBloc.add( + Scored(points: bumper.points), + ), + ).called(1); + } + }, + ); + }); + }); +} diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index 2dfd5d76..ef55b399 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -70,6 +70,14 @@ void main() { }, ); + flameTester.test( + 'one AlienZone', + (game) async { + await game.ready(); + expect(game.children.whereType().length, equals(1)); + }, + ); + group('controller', () { // TODO(alestiago): Write test to be controller agnostic. group('listenWhen', () {