From c73d36d22bfbff9da36ce47394b9cc397489442a Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Fri, 22 Apr 2022 01:52:51 +0100 Subject: [PATCH] feat: simplified Blueprint implementation (#212) * feat: adjusted Blueprint implementation * refactor: improved tests * refactor: renamed attach * feat: tested components setter * refactor: removed empty line Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * refactor: removed unecessary InitialPosition * fix: awaited test * refactor: moved Sensor to ControlledSparkyComputer * fix: coverage * refactor: renamed ContactCallback * docs: renamed template * docs: renamed template * refactor: renamed mock ans sensor * refactor: simplified spaceship_ramp * refactor: reduced redundancy code * feat: changes test query * test: pump on spaceship ramp golden tests * test: fixed golden tests for inactive spaceship ramp * refactor: golden tests Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> Co-authored-by: RuiAlonso --- .../controlled_sparky_computer.dart | 56 ++---- lib/game/components/launcher.dart | 26 +-- lib/game/pinball_game.dart | 2 +- .../lib/src/components/boundaries.dart | 19 +- .../lib/src/components/dino_walls.dart | 20 +- .../lib/src/components/launch_ramp.dart | 69 +++---- .../lib/src/components/slingshot.dart | 39 ++-- .../lib/src/components/spaceship.dart | 95 +++++---- .../lib/src/components/spaceship_rail.dart | 65 +++--- .../lib/src/components/spaceship_ramp.dart | 160 ++++++--------- .../lib/src/components/sparky_computer.dart | 86 ++++---- .../src/components/spaceship_rail_test.dart | 2 + .../src/components/spaceship_ramp_test.dart | 44 +++-- .../src/components/sparky_computer_test.dart | 2 + packages/pinball_flame/lib/src/blueprint.dart | 106 +++------- .../test/src/blueprint_test.dart | 186 ++++++------------ .../controlled_sparky_computer_test.dart | 21 +- test/helpers/mocks.dart | 3 +- 18 files changed, 397 insertions(+), 604 deletions(-) diff --git a/lib/game/components/controlled_sparky_computer.dart b/lib/game/components/controlled_sparky_computer.dart index 6a8e9e59..65190a1f 100644 --- a/lib/game/components/controlled_sparky_computer.dart +++ b/lib/game/components/controlled_sparky_computer.dart @@ -11,19 +11,16 @@ import 'package:pinball_flame/pinball_flame.dart'; /// [SparkyComputer] with a [SparkyComputerController] attached. /// {@endtemplate} class ControlledSparkyComputer extends SparkyComputer - with Controls, HasGameRef { + with Controls, HasGameRef { /// {@macro controlled_sparky_computer} - ControlledSparkyComputer() { + ControlledSparkyComputer() : super() { controller = SparkyComputerController(this); } @override - void build(Forge2DGame _) { - addContactCallback(SparkyTurboChargeSensorBallContactCallback()); - final sparkyTurboChargeSensor = SparkyTurboChargeSensor() - ..initialPosition = Vector2(-13, -49.8); - add(sparkyTurboChargeSensor); - super.build(_); + Future onLoad() async { + await super.onLoad(); + gameRef.addContactCallback(SparkyComputerSensorBallContactCallback()); } } @@ -39,46 +36,17 @@ class SparkyComputerController : super(controlledComputer); } -/// {@template sparky_turbo_charge_sensor} -/// Small sensor body used to detect when a ball has entered the -/// [SparkyComputer] with the [SparkyTurboChargeSensorBallContactCallback]. +/// {@template sparky_computer_sensor_ball_contact_callback} +/// Turbo charges the [Ball] when it enters the [SparkyComputer] /// {@endtemplate} @visibleForTesting -class SparkyTurboChargeSensor extends BodyComponent with InitialPosition { - /// {@macro sparky_turbo_charge_sensor} - SparkyTurboChargeSensor() { - renderBody = false; - } - - @override - Body createBody() { - final shape = CircleShape()..radius = 0.1; - - final fixtureDef = FixtureDef(shape)..isSensor = true; - - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this; - - return world.createBody(bodyDef)..createFixture(fixtureDef); - } -} - -/// {@template sparky_turbo_charge_sensor_ball_contact_callback} -/// Turbo charges the [Ball] on contact with [SparkyTurboChargeSensor]. -/// {@endtemplate} -@visibleForTesting -class SparkyTurboChargeSensorBallContactCallback - extends ContactCallback { - /// {@macro sparky_turbo_charge_sensor_ball_contact_callback} - SparkyTurboChargeSensorBallContactCallback(); +class SparkyComputerSensorBallContactCallback + extends ContactCallback { + /// {@macro sparky_computer_sensor_ball_contact_callback} + SparkyComputerSensorBallContactCallback(); @override - void begin( - SparkyTurboChargeSensor sparkyTurboChargeSensor, - ControlledBall ball, - _, - ) { + void begin(_, ControlledBall ball, __) { ball.controller.turboCharge(); } } diff --git a/lib/game/components/launcher.dart b/lib/game/components/launcher.dart index d2e69174..7aef09d2 100644 --- a/lib/game/components/launcher.dart +++ b/lib/game/components/launcher.dart @@ -7,21 +7,15 @@ import 'package:pinball_flame/pinball_flame.dart'; /// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and /// [LaunchRamp]. /// {@endtemplate} -class Launcher extends Forge2DBlueprint { +class Launcher extends Blueprint { /// {@macro launcher} - Launcher(); - - /// [Plunger] to launch the [Ball] onto the board. - late final Plunger plunger; - - @override - void build(Forge2DGame gameRef) { - plunger = ControlledPlunger(compressionDistance: 14) - ..initialPosition = Vector2(40.7, 38); - - final _rocket = RocketSpriteComponent()..position = Vector2(43, 62); - - addAll([_rocket, plunger]); - addBlueprint(LaunchRamp()); - } + Launcher() + : super( + components: [ + ControlledPlunger(compressionDistance: 14) + ..initialPosition = Vector2(40.7, 38), + RocketSpriteComponent()..position = Vector2(43, 62), + ], + blueprints: [LaunchRamp()], + ); } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 7034cfe5..4865e820 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -68,7 +68,7 @@ class PinballGame extends Forge2DGame ); unawaited(addFromBlueprint(SpaceshipRail())); - controller.attachTo(launcher.plunger); + controller.attachTo(launcher.components.whereType().first); await super.onLoad(); } diff --git a/packages/pinball_components/lib/src/components/boundaries.dart b/packages/pinball_components/lib/src/components/boundaries.dart index 8c59d598..f4f8c495 100644 --- a/packages/pinball_components/lib/src/components/boundaries.dart +++ b/packages/pinball_components/lib/src/components/boundaries.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_renaming_method_parameters - import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -8,14 +6,15 @@ import 'package:pinball_flame/pinball_flame.dart'; /// {@template boundaries} /// A [Blueprint] which creates the [_BottomBoundary] and [_OuterBoundary]. ///{@endtemplate boundaries} -class Boundaries extends Forge2DBlueprint { - @override - void build(_) { - final bottomBoundary = _BottomBoundary(); - final outerBoundary = _OuterBoundary(); - - addAll([outerBoundary, bottomBoundary]); - } +class Boundaries extends Blueprint { + /// {@macro boundaries} + Boundaries() + : super( + components: [ + _BottomBoundary(), + _OuterBoundary(), + ], + ); } /// {@template bottom_boundary} diff --git a/packages/pinball_components/lib/src/components/dino_walls.dart b/packages/pinball_components/lib/src/components/dino_walls.dart index 139e391b..9ce5a523 100644 --- a/packages/pinball_components/lib/src/components/dino_walls.dart +++ b/packages/pinball_components/lib/src/components/dino_walls.dart @@ -1,5 +1,3 @@ -// ignore_for_file: comment_references, avoid_renaming_method_parameters - import 'dart:async'; import 'package:flame/components.dart'; @@ -11,17 +9,15 @@ import 'package:pinball_flame/pinball_flame.dart'; /// {@template dinowalls} /// A [Blueprint] which creates walls for the [ChromeDino]. /// {@endtemplate} -class DinoWalls extends Forge2DBlueprint { +class DinoWalls extends Blueprint { /// {@macro dinowalls} - DinoWalls(); - - @override - void build(_) { - addAll([ - _DinoTopWall(), - _DinoBottomWall(), - ]); - } + DinoWalls() + : super( + components: [ + _DinoTopWall(), + _DinoBottomWall(), + ], + ); } /// {@template dino_top_wall} diff --git a/packages/pinball_components/lib/src/components/launch_ramp.dart b/packages/pinball_components/lib/src/components/launch_ramp.dart index 2ac9ee91..baa54744 100644 --- a/packages/pinball_components/lib/src/components/launch_ramp.dart +++ b/packages/pinball_components/lib/src/components/launch_ramp.dart @@ -11,36 +11,23 @@ import 'package:pinball_flame/pinball_flame.dart'; /// A [Blueprint] which creates the [_LaunchRampBase] and /// [_LaunchRampForegroundRailing]. /// {@endtemplate} -class LaunchRamp extends Forge2DBlueprint { - @override - void build(_) { - addAllContactCallback([ - LayerSensorBallContactCallback<_LaunchRampExit>(), - ]); - - final launchRampBase = _LaunchRampBase(); - - final launchRampForegroundRailing = _LaunchRampForegroundRailing(); - - final launchRampExit = _LaunchRampExit(rotation: math.pi / 2) - ..initialPosition = Vector2(0.6, -34); - - final launchRampCloseWall = _LaunchRampCloseWall() - ..initialPosition = Vector2(4, -69.5); - - addAll([ - launchRampBase, - launchRampForegroundRailing, - launchRampExit, - launchRampCloseWall, - ]); - } +class LaunchRamp extends Blueprint { + /// {@macro launch_ramp} + LaunchRamp() + : super( + components: [ + _LaunchRampBase(), + _LaunchRampForegroundRailing(), + _LaunchRampExit()..initialPosition = Vector2(0.6, -34), + _LaunchRampCloseWall()..initialPosition = Vector2(4, -69.5), + ], + ); } /// {@template launch_ramp_base} /// Ramp the [Ball] is launched from at the beginning of each ball life. /// {@endtemplate} -class _LaunchRampBase extends BodyComponent with InitialPosition, Layered { +class _LaunchRampBase extends BodyComponent with Layered { /// {@macro launch_ramp_base} _LaunchRampBase() : super( @@ -115,16 +102,18 @@ class _LaunchRampBase extends BodyComponent with InitialPosition, Layered { @override Body createBody() { - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); - - final body = world.createBody(bodyDef); + final body = world.createBody(BodyDef()); _createFixtureDefs().forEach(body.createFixture); return body; } + + @override + Future onLoad() async { + await super.onLoad(); + gameRef + .addContactCallback(LayerSensorBallContactCallback<_LaunchRampExit>()); + } } class _LaunchRampBaseSpriteComponent extends SpriteComponent with HasGameRef { @@ -162,7 +151,7 @@ class _LaunchRampBackgroundRailingSpriteComponent extends SpriteComponent /// Foreground railing for the [_LaunchRampBase] to render in front of the /// [Ball]. /// {@endtemplate} -class _LaunchRampForegroundRailing extends BodyComponent with InitialPosition { +class _LaunchRampForegroundRailing extends BodyComponent { /// {@macro launch_ramp_foreground_railing} _LaunchRampForegroundRailing() : super( @@ -205,11 +194,7 @@ class _LaunchRampForegroundRailing extends BodyComponent with InitialPosition { @override Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition; - - final body = world.createBody(bodyDef); + final body = world.createBody(BodyDef()); _createFixtureDefs().forEach(body.createFixture); return body; @@ -258,10 +243,8 @@ class _LaunchRampCloseWall extends BodyComponent with InitialPosition, Layered { /// {@endtemplate} class _LaunchRampExit extends LayerSensor { /// {@macro launch_ramp_exit} - _LaunchRampExit({ - required double rotation, - }) : _rotation = rotation, - super( + _LaunchRampExit() + : super( insideLayer: Layer.launcher, outsideLayer: Layer.board, orientation: LayerEntranceOrientation.down, @@ -272,8 +255,6 @@ class _LaunchRampExit extends LayerSensor { renderBody = false; } - final double _rotation; - static final Vector2 _size = Vector2(1.6, 0.1); @override @@ -282,6 +263,6 @@ class _LaunchRampExit extends LayerSensor { _size.x, _size.y, initialPosition, - _rotation, + math.pi / 2, ); } diff --git a/packages/pinball_components/lib/src/components/slingshot.dart b/packages/pinball_components/lib/src/components/slingshot.dart index 52cdb698..4d098c7a 100644 --- a/packages/pinball_components/lib/src/components/slingshot.dart +++ b/packages/pinball_components/lib/src/components/slingshot.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_renaming_method_parameters - import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -9,26 +7,23 @@ import 'package:pinball_flame/pinball_flame.dart'; /// A [Blueprint] which creates the pair of [Slingshot]s on the right side of /// the board. /// {@endtemplate} -class Slingshots extends Forge2DBlueprint { - @override - void build(_) { - final upperSlingshot = Slingshot( - length: 5.64, - angle: -0.017, - spritePath: Assets.images.slingshot.upper.keyName, - )..initialPosition = Vector2(22.3, -1.58); - - final lowerSlingshot = Slingshot( - length: 3.46, - angle: -0.468, - spritePath: Assets.images.slingshot.lower.keyName, - )..initialPosition = Vector2(24.7, 6.2); - - addAll([ - upperSlingshot, - lowerSlingshot, - ]); - } +class Slingshots extends Blueprint { + /// {@macro slingshots} + Slingshots() + : super( + components: [ + Slingshot( + length: 5.64, + angle: -0.017, + spritePath: Assets.images.slingshot.upper.keyName, + )..initialPosition = Vector2(22.3, -1.58), + Slingshot( + length: 3.46, + angle: -0.468, + spritePath: Assets.images.slingshot.lower.keyName, + )..initialPosition = Vector2(24.7, 6.2), + ], + ); } /// {@template slingshot} diff --git a/packages/pinball_components/lib/src/components/spaceship.dart b/packages/pinball_components/lib/src/components/spaceship.dart index b3d674dc..4ea4e05a 100644 --- a/packages/pinball_components/lib/src/components/spaceship.dart +++ b/packages/pinball_components/lib/src/components/spaceship.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_renaming_method_parameters - import 'dart:async'; import 'dart:math'; @@ -12,38 +10,28 @@ import 'package:pinball_flame/pinball_flame.dart'; /// {@template spaceship} /// A [Blueprint] which creates the spaceship feature. /// {@endtemplate} -class Spaceship extends Forge2DBlueprint { +class Spaceship extends Blueprint { /// {@macro spaceship} - Spaceship({required this.position}); + Spaceship({required Vector2 position}) + : super( + components: [ + SpaceshipSaucer()..initialPosition = position, + _SpaceshipEntrance()..initialPosition = position, + AndroidHead()..initialPosition = position, + _SpaceshipHole( + outsideLayer: Layer.spaceshipExitRail, + outsidePriority: RenderPriority.ballOnSpaceshipRail, + )..initialPosition = position - Vector2(5.2, -4.8), + _SpaceshipHole( + outsideLayer: Layer.board, + outsidePriority: RenderPriority.ballOnBoard, + )..initialPosition = position - Vector2(-7.2, -0.8), + SpaceshipWall()..initialPosition = position, + ], + ); /// Total size of the spaceship. static final size = Vector2(25, 19); - - /// The [position] where the elements will be created - final Vector2 position; - - @override - void build(_) { - addAllContactCallback([ - LayerSensorBallContactCallback<_SpaceshipEntrance>(), - LayerSensorBallContactCallback<_SpaceshipHole>(), - ]); - - addAll([ - SpaceshipSaucer()..initialPosition = position, - _SpaceshipEntrance()..initialPosition = position, - AndroidHead()..initialPosition = position, - _SpaceshipHole( - outsideLayer: Layer.spaceshipExitRail, - outsidePriority: RenderPriority.ballOnSpaceshipRail, - )..initialPosition = position - Vector2(5.2, -4.8), - _SpaceshipHole( - outsideLayer: Layer.board, - outsidePriority: RenderPriority.ballOnBoard, - )..initialPosition = position - Vector2(-7.2, -0.8), - SpaceshipWall()..initialPosition = position, - ]); - } } /// {@template spaceship_saucer} @@ -51,26 +39,28 @@ class Spaceship extends Forge2DBlueprint { /// {@endtemplate} class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { /// {@macro spaceship_saucer} - SpaceshipSaucer() : super(priority: RenderPriority.spaceshipSaucer) { + SpaceshipSaucer() + : super( + priority: RenderPriority.spaceshipSaucer, + children: [ + _SpaceshipSaucerSpriteComponent(), + ], + ) { layer = Layer.spaceship; + renderBody = false; } @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - Assets.images.spaceship.saucer.keyName, - ); - - await add( - SpriteComponent( - sprite: sprite, - size: Spaceship.size, - anchor: Anchor.center, - ), - ); - renderBody = false; + gameRef + ..addContactCallback( + LayerSensorBallContactCallback<_SpaceshipEntrance>(), + ) + ..addContactCallback( + LayerSensorBallContactCallback<_SpaceshipHole>(), + ); } @override @@ -89,6 +79,25 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { } } +class _SpaceshipSaucerSpriteComponent extends SpriteComponent with HasGameRef { + _SpaceshipSaucerSpriteComponent() + : super( + anchor: Anchor.center, + // TODO(alestiago): Refactor to use sprite orignial size instead. + size: Spaceship.size, + ); + + @override + Future onLoad() async { + await super.onLoad(); + + // TODO(alestiago): Use cached sprite. + sprite = await gameRef.loadSprite( + Assets.images.spaceship.saucer.keyName, + ); + } +} + /// {@template spaceship_bridge} /// A [BodyComponent] that provides both the collision and the rotation /// animation for the bridge. diff --git a/packages/pinball_components/lib/src/components/spaceship_rail.dart b/packages/pinball_components/lib/src/components/spaceship_rail.dart index 49fdd475..1175384b 100644 --- a/packages/pinball_components/lib/src/components/spaceship_rail.dart +++ b/packages/pinball_components/lib/src/components/spaceship_rail.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_renaming_method_parameters - import 'dart:math' as math; import 'package:flame/components.dart'; @@ -11,38 +9,30 @@ import 'package:pinball_flame/pinball_flame.dart'; /// {@template spaceship_rail} /// A [Blueprint] for the spaceship drop tube. /// {@endtemplate} -class SpaceshipRail extends Forge2DBlueprint { +class SpaceshipRail extends Blueprint { /// {@macro spaceship_rail} - SpaceshipRail(); - - @override - void build(_) { - addAllContactCallback([ - LayerSensorBallContactCallback<_SpaceshipRailExit>(), - ]); - - final railRamp = _SpaceshipRailRamp(); - final railEnd = _SpaceshipRailExit(); - final topBase = _SpaceshipRailBase(radius: 0.55) - ..initialPosition = Vector2(-26.15, -18.65); - final bottomBase = _SpaceshipRailBase(radius: 0.8) - ..initialPosition = Vector2(-25.5, 12.9); - final railForeground = _SpaceshipRailForeground(); - - addAll([ - railRamp, - railEnd, - topBase, - bottomBase, - railForeground, - ]); - } + SpaceshipRail() + : super( + components: [ + _SpaceshipRailRamp(), + _SpaceshipRailExit(), + _SpaceshipRailBase(radius: 0.55) + ..initialPosition = Vector2(-26.15, -18.65), + _SpaceshipRailBase(radius: 0.8) + ..initialPosition = Vector2(-25.5, 12.9), + _SpaceshipRailForeground() + ], + ); } -/// Represents the spaceship drop rail from the [Spaceship]. -class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { - _SpaceshipRailRamp() : super(priority: RenderPriority.spaceshipRail) { +class _SpaceshipRailRamp extends BodyComponent with Layered { + _SpaceshipRailRamp() + : super( + priority: RenderPriority.spaceshipRail, + children: [_SpaceshipRailRampSpriteComponent()], + ) { layer = Layer.spaceshipExitRail; + renderBody = false; } List _createFixtureDefs() { @@ -120,23 +110,17 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { @override Body createBody() { - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); - - final body = world.createBody(bodyDef); + final body = world.createBody(BodyDef()); _createFixtureDefs().forEach(body.createFixture); - return body; } @override Future onLoad() async { await super.onLoad(); - renderBody = false; - - await add(_SpaceshipRailRampSpriteComponent()); + gameRef.addContactCallback( + LayerSensorBallContactCallback<_SpaceshipRailExit>(), + ); } } @@ -188,7 +172,6 @@ class _SpaceshipRailBase extends BodyComponent with InitialPosition { final fixtureDef = FixtureDef(shape); final bodyDef = BodyDef( position: initialPosition, - userData: this, ); return world.createBody(bodyDef)..createFixture(fixtureDef); diff --git a/packages/pinball_components/lib/src/components/spaceship_ramp.dart b/packages/pinball_components/lib/src/components/spaceship_ramp.dart index e40c0e16..30211251 100644 --- a/packages/pinball_components/lib/src/components/spaceship_ramp.dart +++ b/packages/pinball_components/lib/src/components/spaceship_ramp.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_renaming_method_parameters - import 'dart:math' as math; import 'package:flame/components.dart'; @@ -12,62 +10,41 @@ import 'package:pinball_flame/pinball_flame.dart'; /// {@template spaceship_ramp} /// A [Blueprint] which creates the ramp leading into the [Spaceship]. /// {@endtemplate} -class SpaceshipRamp extends Forge2DBlueprint { +class SpaceshipRamp extends Blueprint { /// {@macro spaceship_ramp} - SpaceshipRamp(); - - /// [SpriteGroupComponent] representing the arrow that lights up. - @visibleForTesting - late final SpaceshipRampArrowSpriteComponent spaceshipRampArrow; + SpaceshipRamp() + : super( + components: [ + _SpaceshipRampOpening( + outsidePriority: RenderPriority.ballOnBoard, + rotation: math.pi, + ) + ..initialPosition = Vector2(1.7, -19.8) + ..layer = Layer.opening, + _SpaceshipRampOpening( + outsideLayer: Layer.spaceship, + outsidePriority: RenderPriority.ballOnSpaceship, + rotation: math.pi, + ) + ..initialPosition = Vector2(-13.7, -18.6) + ..layer = Layer.spaceshipEntranceRamp, + _SpaceshipRampBackground(), + _SpaceshipRampBoardOpeningSpriteComponent() + ..position = Vector2(3.4, -39.5), + _SpaceshipRampForegroundRailing(), + _SpaceshipRampBase()..initialPosition = Vector2(1.7, -20), + _SpaceshipRampBackgroundRailingSpriteComponent(), + _SpaceshipRampArrowSpriteComponent(), + ], + ); /// Forwards the sprite to the next [SpaceshipRampArrowSpriteState]. /// /// If the current state is the last one it cycles back to the initial state. - void progress() => spaceshipRampArrow.progress(); - - @override - void build(_) { - addAllContactCallback([ - LayerSensorBallContactCallback<_SpaceshipRampOpening>(), - ]); - - final rightOpening = _SpaceshipRampOpening( - outsidePriority: RenderPriority.ballOnBoard, - rotation: -5 * math.pi / 180, - ) - ..initialPosition = Vector2(1.7, -19.12) - ..layer = Layer.opening; - final leftOpening = _SpaceshipRampOpening( - outsideLayer: Layer.spaceship, - outsidePriority: RenderPriority.ballOnSpaceship, - rotation: -5 * math.pi / 180, - ) - ..initialPosition = Vector2(-13.7, -19) - ..layer = Layer.spaceshipEntranceRamp; - - final spaceshipRamp = _SpaceshipRampBackground(); - - spaceshipRampArrow = SpaceshipRampArrowSpriteComponent(); - - final spaceshipRampBoardOpeningSprite = - _SpaceshipRampBoardOpeningSpriteComponent() - ..position = Vector2(3.4, -39.5); - - final spaceshipRampForegroundRailing = _SpaceshipRampForegroundRailing(); - - final baseRight = _SpaceshipRampBase()..initialPosition = Vector2(1.7, -20); - - addAll([ - spaceshipRampBoardOpeningSprite, - rightOpening, - leftOpening, - baseRight, - _SpaceshipRampBackgroundRailingSpriteComponent(), - spaceshipRamp, - spaceshipRampArrow, - spaceshipRampForegroundRailing, - ]); - } + void progress() => components + .whereType<_SpaceshipRampArrowSpriteComponent>() + .first + .progress(); } /// Indicates the state of the arrow on the [SpaceshipRamp]. @@ -133,8 +110,6 @@ class _SpaceshipRampBackground extends BodyComponent static const width = 5.0; List _createFixtureDefs() { - final fixturesDef = []; - final outerLeftCurveShape = BezierCurveShape( controlPoints: [ Vector2(-30.75, -37.3), @@ -142,9 +117,6 @@ class _SpaceshipRampBackground extends BodyComponent Vector2(-14.2, -71.25), ], ); - final outerLeftCurveFixtureDef = FixtureDef(outerLeftCurveShape); - fixturesDef.add(outerLeftCurveFixtureDef); - final outerRightCurveShape = BezierCurveShape( controlPoints: [ outerLeftCurveShape.vertices.last, @@ -152,32 +124,35 @@ class _SpaceshipRampBackground extends BodyComponent Vector2(6.1, -44.9), ], ); - final outerRightCurveFixtureDef = FixtureDef(outerRightCurveShape); - fixturesDef.add(outerRightCurveFixtureDef); - final boardOpeningEdgeShape = EdgeShape() ..set( outerRightCurveShape.vertices.last, Vector2(7.3, -41.1), ); - final boardOpeningEdgeShapeFixtureDef = FixtureDef(boardOpeningEdgeShape); - fixturesDef.add(boardOpeningEdgeShapeFixtureDef); - return fixturesDef; + return [ + FixtureDef(outerRightCurveShape), + FixtureDef(outerLeftCurveShape), + FixtureDef(boardOpeningEdgeShape), + ]; } @override Body createBody() { - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); - + final bodyDef = BodyDef(position: initialPosition); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); return body; } + + @override + Future onLoad() async { + await super.onLoad(); + gameRef.addContactCallback( + LayerSensorBallContactCallback<_SpaceshipRampOpening>(), + ); + } } class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent @@ -188,6 +163,7 @@ class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent position: Vector2(-11.7, -54.3), priority: RenderPriority.spaceshipRampBackgroundRailing, ); + @override Future onLoad() async { await super.onLoad(); @@ -203,6 +179,12 @@ class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent class _SpaceshipRampBackgroundRampSpriteComponent extends SpriteComponent with HasGameRef { + _SpaceshipRampBackgroundRampSpriteComponent() + : super( + anchor: Anchor.center, + position: Vector2(-10.7, -53.6), + ); + @override Future onLoad() async { await super.onLoad(); @@ -213,21 +195,19 @@ class _SpaceshipRampBackgroundRampSpriteComponent extends SpriteComponent ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; - position = Vector2(-10.7, -53.6); } } /// {@template spaceship_ramp_arrow_sprite_component} /// An arrow inside [SpaceshipRamp]. /// -/// Lights up a each dash whenever a [Ball] gets into [SpaceshipRamp]. +/// Lights progressively whenever a [Ball] gets into [SpaceshipRamp]. /// {@endtemplate} -class SpaceshipRampArrowSpriteComponent +class _SpaceshipRampArrowSpriteComponent extends SpriteGroupComponent with HasGameRef { /// {@macro spaceship_ramp_arrow_sprite_component} - SpaceshipRampArrowSpriteComponent() + _SpaceshipRampArrowSpriteComponent() : super( anchor: Anchor.center, position: Vector2(-3.9, -56.5), @@ -255,6 +235,8 @@ class SpaceshipRampArrowSpriteComponent class _SpaceshipRampBoardOpeningSpriteComponent extends SpriteComponent with HasGameRef { + _SpaceshipRampBoardOpeningSpriteComponent() : super(anchor: Anchor.center); + @override Future onLoad() async { await super.onLoad(); @@ -265,7 +247,6 @@ class _SpaceshipRampBoardOpeningSpriteComponent extends SpriteComponent ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; } } @@ -281,8 +262,6 @@ class _SpaceshipRampForegroundRailing extends BodyComponent } List _createFixtureDefs() { - final fixturesDef = []; - final innerLeftCurveShape = BezierCurveShape( controlPoints: [ Vector2(-24.5, -38), @@ -290,10 +269,6 @@ class _SpaceshipRampForegroundRailing extends BodyComponent Vector2(-13.8, -64.5), ], ); - - final innerLeftCurveFixtureDef = FixtureDef(innerLeftCurveShape); - fixturesDef.add(innerLeftCurveFixtureDef); - final innerRightCurveShape = BezierCurveShape( controlPoints: [ innerLeftCurveShape.vertices.last, @@ -301,28 +276,22 @@ class _SpaceshipRampForegroundRailing extends BodyComponent Vector2(0, -44.5), ], ); - - final innerRightCurveFixtureDef = FixtureDef(innerRightCurveShape); - fixturesDef.add(innerRightCurveFixtureDef); - final boardOpeningEdgeShape = EdgeShape() ..set( innerRightCurveShape.vertices.last, Vector2(-0.85, -40.8), ); - final boardOpeningEdgeShapeFixtureDef = FixtureDef(boardOpeningEdgeShape); - fixturesDef.add(boardOpeningEdgeShapeFixtureDef); - return fixturesDef; + return [ + FixtureDef(innerLeftCurveShape), + FixtureDef(innerRightCurveShape), + FixtureDef(boardOpeningEdgeShape), + ]; } @override Body createBody() { - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); - + final bodyDef = BodyDef(position: initialPosition); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); @@ -371,10 +340,7 @@ class _SpaceshipRampBase extends BodyComponent with InitialPosition, Layered { ], ); final fixtureDef = FixtureDef(baseShape); - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); + final bodyDef = BodyDef(position: initialPosition); return world.createBody(bodyDef)..createFixture(fixtureDef); } diff --git a/packages/pinball_components/lib/src/components/sparky_computer.dart b/packages/pinball_components/lib/src/components/sparky_computer.dart index 427847ae..4cec4c59 100644 --- a/packages/pinball_components/lib/src/components/sparky_computer.dart +++ b/packages/pinball_components/lib/src/components/sparky_computer.dart @@ -6,75 +6,64 @@ import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; /// {@template sparky_computer} -/// A [Blueprint] which creates the [_ComputerBase] and -/// [_ComputerTopSpriteComponent]. +/// A computer owned by Sparky. +/// +/// Register a [ContactCallback] for [SparkyComputerSensor] to listen when +/// something enters the [SparkyComputer]. /// {@endtemplate} -class SparkyComputer extends Forge2DBlueprint { - @override - void build(_) { - final computerBase = _ComputerBase(); - final computerTop = _ComputerTopSpriteComponent(); - - addAll([ - computerBase, - computerTop, - ]); - } +class SparkyComputer extends Blueprint { + /// {@macro sparky_computer} + SparkyComputer() + : super( + components: [ + _ComputerBase(), + _ComputerTopSpriteComponent(), + SparkyComputerSensor(), + ], + ); } class _ComputerBase extends BodyComponent with InitialPosition { - _ComputerBase() : super(priority: RenderPriority.computerBase); + _ComputerBase() + : super( + priority: RenderPriority.computerBase, + children: [_ComputerBaseSpriteComponent()], + ) { + renderBody = false; + } List _createFixtureDefs() { - final fixturesDef = []; - final leftEdge = EdgeShape() ..set( Vector2(-14.9, -46), Vector2(-15.3, -49.6), ); - final leftEdgeFixtureDef = FixtureDef(leftEdge); - fixturesDef.add(leftEdgeFixtureDef); - final topEdge = EdgeShape() ..set( Vector2(-15.3, -49.6), Vector2(-10.7, -50.6), ); - final topEdgeFixtureDef = FixtureDef(topEdge); - fixturesDef.add(topEdgeFixtureDef); - final rightEdge = EdgeShape() ..set( Vector2(-10.7, -50.6), Vector2(-9, -47.2), ); - final rightEdgeFixtureDef = FixtureDef(rightEdge); - fixturesDef.add(rightEdgeFixtureDef); - return fixturesDef; + return [ + FixtureDef(leftEdge), + FixtureDef(topEdge), + FixtureDef(rightEdge), + ]; } @override Body createBody() { - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); - + final bodyDef = BodyDef(position: initialPosition); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); return body; } - - @override - Future onLoad() async { - await super.onLoad(); - renderBody = false; - - await add(_ComputerBaseSpriteComponent()); - } } class _ComputerBaseSpriteComponent extends SpriteComponent with HasGameRef { @@ -115,3 +104,24 @@ class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef { size = sprite.originalSize / 10; } } + +/// {@template sparky_computer_sensor} +/// Small sensor body used to detect when a ball has entered the +/// [SparkyComputer]. +/// {@endtemplate} +class SparkyComputerSensor extends BodyComponent with InitialPosition { + /// {@macro sparky_computer_sensor} + SparkyComputerSensor() { + renderBody = false; + } + + @override + Body createBody() { + final shape = CircleShape()..radius = 0.1; + final fixtureDef = FixtureDef(shape, isSensor: true); + final bodyDef = BodyDef() + ..position = initialPosition + ..userData = this; + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} diff --git a/packages/pinball_components/test/src/components/spaceship_rail_test.dart b/packages/pinball_components/test/src/components/spaceship_rail_test.dart index c73c8dee..d3242ff6 100644 --- a/packages/pinball_components/test/src/components/spaceship_rail_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_rail_test.dart @@ -17,6 +17,8 @@ void main() { 'renders correctly', setUp: (game, tester) async { await game.addFromBlueprint(SpaceshipRail()); + await game.ready(); + game.camera.followVector2(Vector2.zero()); game.camera.zoom = 8; }, diff --git a/packages/pinball_components/test/src/components/spaceship_ramp_test.dart b/packages/pinball_components/test/src/components/spaceship_ramp_test.dart index 4e5d149b..98a6e95f 100644 --- a/packages/pinball_components/test/src/components/spaceship_ramp_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_ramp_test.dart @@ -1,7 +1,6 @@ // ignore_for_file: cascade_invocations import 'package:flame/components.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -37,6 +36,7 @@ void main() { ); group('renders correctly', () { + const goldenFilePath = 'golden/spaceship_ramp/'; final centerForSpaceshipRamp = Vector2(-13, -55); flameTester.testGameWidget( @@ -49,7 +49,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.inactive, ); @@ -58,7 +61,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/inactive.png'), + matchesGoldenFile('${goldenFilePath}inactive.png'), ); }, ); @@ -74,7 +77,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.active1, ); @@ -83,7 +89,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/active1.png'), + matchesGoldenFile('${goldenFilePath}active1.png'), ); }, ); @@ -101,7 +107,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.active2, ); @@ -110,7 +119,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/active2.png'), + matchesGoldenFile('${goldenFilePath}active2.png'), ); }, ); @@ -129,7 +138,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.active3, ); @@ -138,7 +150,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/active3.png'), + matchesGoldenFile('${goldenFilePath}active3.png'), ); }, ); @@ -158,7 +170,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.active4, ); @@ -167,7 +182,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/active4.png'), + matchesGoldenFile('${goldenFilePath}active4.png'), ); }, ); @@ -188,7 +203,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.active5, ); @@ -197,7 +215,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/active5.png'), + matchesGoldenFile('${goldenFilePath}active5.png'), ); }, ); diff --git a/packages/pinball_components/test/src/components/sparky_computer_test.dart b/packages/pinball_components/test/src/components/sparky_computer_test.dart index 3d67106d..6b19481e 100644 --- a/packages/pinball_components/test/src/components/sparky_computer_test.dart +++ b/packages/pinball_components/test/src/components/sparky_computer_test.dart @@ -16,6 +16,8 @@ void main() { 'renders correctly', setUp: (game, tester) async { await game.addFromBlueprint(SparkyComputer()); + await game.ready(); + game.camera.followVector2(Vector2(-15, -50)); }, verify: (game, tester) async { diff --git a/packages/pinball_flame/lib/src/blueprint.dart b/packages/pinball_flame/lib/src/blueprint.dart index 5c2df683..cc1d31ae 100644 --- a/packages/pinball_flame/lib/src/blueprint.dart +++ b/packages/pinball_flame/lib/src/blueprint.dart @@ -1,101 +1,43 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/foundation.dart'; - -const _attachedErrorMessage = "Can't add to attached Blueprints"; // TODO(erickzanardo): Keeping this inside our code base // so we can experiment with the idea, but this is a // potential upstream change on Flame. -/// A [Blueprint] is a virtual way of grouping [Component]s -/// that are related, but they need to be added directly on -/// the [FlameGame] level. +/// {@template blueprint} +/// A [Blueprint] is a virtual way of grouping [Component]s that are related, +/// but they need to be added directly on the [FlameGame] level. +/// {@endtemplate blueprint} // TODO(alestiago): refactor with feat/make-blueprint-extend-component. -abstract class Blueprint extends Component { - final List _components = []; - final List _blueprints = []; - - bool _isAttached = false; - - /// Called before the the [Component]s managed - /// by this blueprint is added to the [FlameGame] - void build(T gameRef); - - /// Attach the [Component]s built on [build] to the [game] - /// instance - @mustCallSuper - Future attach(T game) async { - build(game); - await Future.wait([ - game.addAll(_components), - ..._blueprints.map(game.addFromBlueprint).toList(), - ]); - _isAttached = true; - } - - /// Adds a single [Component] to this blueprint. - @override - Future add(Component component) async { - assert(!_isAttached, _attachedErrorMessage); - _components.add(component); +class Blueprint extends Component { + /// {@macro blueprint} + Blueprint({ + Iterable? components, + Iterable? blueprints, + }) { + if (components != null) _components.addAll(components); + if (blueprints != null) { + for (final blueprint in blueprints) { + _components.addAll(blueprint.components); + } + } } - /// Adds a list of [Blueprint]s to this blueprint. - void addAllBlueprints(List blueprints) { - assert(!_isAttached, _attachedErrorMessage); - _blueprints.addAll(blueprints); - } + final List _components = []; - /// Adds a single [Blueprint] to this blueprint. - void addBlueprint(Blueprint blueprint) { - assert(!_isAttached, _attachedErrorMessage); - _blueprints.add(blueprint); + Future _addToParent(Component parent) async { + await parent.addAll(_components); } - /// Returns a copy of the components built by this blueprint + /// Returns a copy of the components built by this blueprint. List get components => List.unmodifiable(_components); - - /// Returns a copy of the children blueprints - List get blueprints => List.unmodifiable(_blueprints); -} - -/// A [Blueprint] that provides additional -/// structures specific to flame_forge2d -abstract class Forge2DBlueprint extends Blueprint { - final List _callbacks = []; - - /// Adds a single [ContactCallback] to this blueprint - void addContactCallback(ContactCallback callback) { - assert(!_isAttached, _attachedErrorMessage); - _callbacks.add(callback); - } - - /// Adds a collection of [ContactCallback]s to this blueprint - void addAllContactCallback(List callbacks) { - assert(!_isAttached, _attachedErrorMessage); - _callbacks.addAll(callbacks); - } - - @override - Future attach(Forge2DGame game) async { - await super.attach(game); - - for (final callback in _callbacks) { - game.addContactCallback(callback); - } - } - - /// Returns a copy of the callbacks built by this blueprint - List get callbacks => List.unmodifiable(_callbacks); } -/// Adds helper methods regardin [Blueprint]s to [FlameGame] -extension FlameGameBlueprint on FlameGame { - /// Shortcut to attach a [Blueprint] instance to this game - /// equivalent to `MyBluepinrt().attach(game)` +/// Adds helper methods regarding [Blueprint]s to [FlameGame]. +extension FlameGameBlueprint on Component { + /// Shortcut to add a [Blueprint]s components to its parent. Future addFromBlueprint(Blueprint blueprint) async { - await blueprint.attach(this); + await blueprint._addToParent(this); } } diff --git a/packages/pinball_flame/test/src/blueprint_test.dart b/packages/pinball_flame/test/src/blueprint_test.dart index f1c58dc9..7ab25125 100644 --- a/packages/pinball_flame/test/src/blueprint_test.dart +++ b/packages/pinball_flame/test/src/blueprint_test.dart @@ -1,138 +1,74 @@ +// ignore_for_file: cascade_invocations + import 'package:flame/components.dart'; -import 'package:flame_forge2d/contact_callbacks.dart'; +import 'package:flame/game.dart'; +import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; import 'package:pinball_flame/pinball_flame.dart'; -import '../helpers/helpers.dart'; - -class TestContactCallback extends ContactCallback {} - -class MyBlueprint extends Blueprint { - @override - void build(_) { - add(Component()); - addAll([Component(), Component()]); - } -} - -class MyOtherBlueprint extends Blueprint { - @override - void build(_) { - add(Component()); - } -} - -class YetMyOtherBlueprint extends Blueprint { - @override - void build(_) { - add(Component()); - } -} - -class MyComposedBlueprint extends Blueprint { - @override - void build(_) { - addBlueprint(MyBlueprint()); - addAllBlueprints([MyOtherBlueprint(), YetMyOtherBlueprint()]); - } -} - -class MyForge2dBlueprint extends Forge2DBlueprint { - @override - void build(_) { - addContactCallback(MockContactCallback()); - addAllContactCallback([MockContactCallback(), MockContactCallback()]); - } -} - void main() { - group('Blueprint', () { - setUpAll(() { - registerFallbackValue(MyBlueprint()); - registerFallbackValue(Component()); - }); - - test('components can be added to it', () { - final blueprint = MyBlueprint()..build(MockForge2DGame()); - - expect(blueprint.components.length, equals(3)); - }); + TestWidgetsFlutterBinding.ensureInitialized(); - test('blueprints can be added to it', () { - final blueprint = MyComposedBlueprint()..build(MockForge2DGame()); - - expect(blueprint.blueprints.length, equals(3)); - }); - - test('adds the components to a game on attach', () { - final mockGame = MockForge2DGame(); - when(() => mockGame.addAll(any())).thenAnswer((_) async {}); - MyBlueprint().attach(mockGame); - - verify(() => mockGame.addAll(any())).called(1); - }); - - test('adds components from a child Blueprint the to a game on attach', () { - final mockGame = MockForge2DGame(); - when(() => mockGame.addAll(any())).thenAnswer((_) async {}); - MyComposedBlueprint().attach(mockGame); - - verify(() => mockGame.addAll(any())).called(4); - }); - - test( - 'throws assertion error when adding to an already attached blueprint', - () async { - final mockGame = MockForge2DGame(); - when(() => mockGame.addAll(any())).thenAnswer((_) async {}); - final blueprint = MyBlueprint(); - await blueprint.attach(mockGame); - - expect(() => blueprint.add(Component()), throwsAssertionError); - expect(() => blueprint.addAll([Component()]), throwsAssertionError); - }, - ); - }); - - group('Forge2DBlueprint', () { - setUpAll(() { - registerFallbackValue(TestContactCallback()); + group('Blueprint', () { + final flameTester = FlameTester(FlameGame.new); + + test('correctly sets and gets components', () { + final component1 = Component(); + final component2 = Component(); + final blueprint = Blueprint( + components: [ + component1, + component2, + ], + ); + + expect(blueprint.components.length, 2); + expect(blueprint.components, contains(component1)); + expect(blueprint.components, contains(component2)); }); - test('callbacks can be added to it', () { - final blueprint = MyForge2dBlueprint()..build(MockForge2DGame()); - - expect(blueprint.callbacks.length, equals(3)); + flameTester.test('adds the components to parent on attach', (game) async { + final blueprint = Blueprint( + components: [ + Component(), + Component(), + ], + ); + await game.addFromBlueprint(blueprint); + await game.ready(); + + for (final component in blueprint.components) { + expect(game.children.contains(component), isTrue); + } }); - test('adds the callbacks to a game on attach', () async { - final mockGame = MockForge2DGame(); - when(() => mockGame.addAll(any())).thenAnswer((_) async {}); - when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {}); - await MyForge2dBlueprint().attach(mockGame); - - verify(() => mockGame.addContactCallback(any())).called(3); + flameTester.test('adds components from a child Blueprint', (game) async { + final childBlueprint = Blueprint( + components: [ + Component(), + Component(), + ], + ); + final parentBlueprint = Blueprint( + components: [ + Component(), + Component(), + ], + blueprints: [ + childBlueprint, + ], + ); + + await game.addFromBlueprint(parentBlueprint); + await game.ready(); + + for (final component in childBlueprint.components) { + expect(game.children, contains(component)); + expect(parentBlueprint.components, contains(component)); + } + for (final component in parentBlueprint.components) { + expect(game.children, contains(component)); + } }); - - test( - 'throws assertion error when adding to an already attached blueprint', - () async { - final mockGame = MockForge2DGame(); - when(() => mockGame.addAll(any())).thenAnswer((_) async {}); - when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {}); - final blueprint = MyForge2dBlueprint(); - await blueprint.attach(mockGame); - - expect( - () => blueprint.addContactCallback(MockContactCallback()), - throwsAssertionError, - ); - expect( - () => blueprint.addAllContactCallback([MockContactCallback()]), - throwsAssertionError, - ); - }, - ); }); } diff --git a/test/game/components/controlled_sparky_computer_test.dart b/test/game/components/controlled_sparky_computer_test.dart index 944afca3..6ba21a44 100644 --- a/test/game/components/controlled_sparky_computer_test.dart +++ b/test/game/components/controlled_sparky_computer_test.dart @@ -8,28 +8,21 @@ import 'package:pinball/game/game.dart'; import '../../helpers/helpers.dart'; void main() { - group('SparkyComputerController', () { + group('ControlledSparkyComputer', () { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(EmptyPinballTestGame.new); - late ControlledSparkyComputer controlledSparkyComputer; - - setUp(() { - controlledSparkyComputer = ControlledSparkyComputer(); - }); - - test('can be instantiated', () { - expect( - SparkyComputerController(controlledSparkyComputer), - isA(), - ); + flameTester.test('loads correctly', (game) async { + final sparkyComputer = ControlledSparkyComputer(); + await game.ensureAdd(sparkyComputer); + expect(game.children, contains(sparkyComputer)); }); flameTester.testGameWidget( 'SparkyTurboChargeSensorBallContactCallback turbo charges the ball', setUp: (game, tester) async { - final contackCallback = SparkyTurboChargeSensorBallContactCallback(); - final sparkyTurboChargeSensor = MockSparkyTurboChargeSensor(); + final contackCallback = SparkyComputerSensorBallContactCallback(); + final sparkyTurboChargeSensor = MockSparkyComputerSensor(); final ball = MockControlledBall(); final controller = MockBallController(); diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index e9af4408..9b0f67c9 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -69,8 +69,7 @@ class MockDashNestBumper extends Mock implements DashNestBumper {} class MockPinballAudio extends Mock implements PinballAudio {} -class MockSparkyTurboChargeSensor extends Mock - implements SparkyTurboChargeSensor {} +class MockSparkyComputerSensor extends Mock implements SparkyComputerSensor {} class MockAssetsManagerCubit extends Mock implements AssetsManagerCubit {}