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/components/wall.dart b/lib/game/components/wall.dart index ba8af5e7..aaae1d23 100644 --- a/lib/game/components/wall.dart +++ b/lib/game/components/wall.dart @@ -39,22 +39,6 @@ class Wall extends BodyComponent { } } -/// Create top, left, and right [Wall]s for the game board. -List createBoundaries(Forge2DGame game) { - final topLeft = BoardDimensions.bounds.topLeft.toVector2() + Vector2(18.6, 0); - final bottomRight = BoardDimensions.bounds.bottomRight.toVector2(); - - final topRight = - BoardDimensions.bounds.topRight.toVector2() - Vector2(18.6, 0); - final bottomLeft = BoardDimensions.bounds.bottomLeft.toVector2(); - - return [ - Wall(start: topLeft, end: topRight), - Wall(start: topRight, end: bottomRight), - Wall(start: topLeft, end: bottomLeft), - ]; -} - /// {@template bottom_wall} /// [Wall] located at the bottom of the board. /// diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 7034cfe5..d73fb33c 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -46,7 +46,8 @@ class PinballGame extends Forge2DGame unawaited(add(CameraController(this))); unawaited(add(Backboard.waiting(position: Vector2(0, -88)))); - await _addGameBoundaries(); + // TODO(allisonryan0002): banish Wall and Board classes in later PR. + await add(BottomWall()); unawaited(addFromBlueprint(Boundaries())); unawaited(addFromBlueprint(ControlledSparkyComputer())); @@ -68,7 +69,7 @@ class PinballGame extends Forge2DGame ); unawaited(addFromBlueprint(SpaceshipRail())); - controller.attachTo(launcher.plunger); + controller.attachTo(launcher.components.whereType().first); await super.onLoad(); } @@ -77,11 +78,6 @@ class PinballGame extends Forge2DGame addContactCallback(BottomWallBallContactCallback()); } - Future _addGameBoundaries() async { - await add(BottomWall()); - createBoundaries(this).forEach(add); - } - Future _addBonusWord() async { await add( GoogleWord( diff --git a/packages/pinball_components/assets/images/baseboard/left.png b/packages/pinball_components/assets/images/baseboard/left.png index 17253554..d13b4e31 100644 Binary files a/packages/pinball_components/assets/images/baseboard/left.png and b/packages/pinball_components/assets/images/baseboard/left.png differ diff --git a/packages/pinball_components/assets/images/baseboard/right.png b/packages/pinball_components/assets/images/baseboard/right.png index 081a1782..8ad93045 100644 Binary files a/packages/pinball_components/assets/images/baseboard/right.png and b/packages/pinball_components/assets/images/baseboard/right.png differ diff --git a/packages/pinball_components/assets/images/boundary/outer.png b/packages/pinball_components/assets/images/boundary/outer.png index c0b81e96..3c06cb6c 100644 Binary files a/packages/pinball_components/assets/images/boundary/outer.png and b/packages/pinball_components/assets/images/boundary/outer.png differ diff --git a/packages/pinball_components/assets/images/dash/animatronic.png b/packages/pinball_components/assets/images/dash/animatronic.png index e67cafef..13f7b794 100644 Binary files a/packages/pinball_components/assets/images/dash/animatronic.png and b/packages/pinball_components/assets/images/dash/animatronic.png differ diff --git a/packages/pinball_components/assets/images/flipper/left.png b/packages/pinball_components/assets/images/flipper/left.png index 42798f28..3aefa225 100644 Binary files a/packages/pinball_components/assets/images/flipper/left.png and b/packages/pinball_components/assets/images/flipper/left.png differ diff --git a/packages/pinball_components/assets/images/flipper/right.png b/packages/pinball_components/assets/images/flipper/right.png index 86fbc81d..3627c86c 100644 Binary files a/packages/pinball_components/assets/images/flipper/right.png and b/packages/pinball_components/assets/images/flipper/right.png differ diff --git a/packages/pinball_components/assets/images/slingshot/lower.png b/packages/pinball_components/assets/images/slingshot/lower.png index 71a6a277..61e01ba1 100644 Binary files a/packages/pinball_components/assets/images/slingshot/lower.png and b/packages/pinball_components/assets/images/slingshot/lower.png differ diff --git a/packages/pinball_components/assets/images/slingshot/upper.png b/packages/pinball_components/assets/images/slingshot/upper.png index e6b42ded..d86bd925 100644 Binary files a/packages/pinball_components/assets/images/slingshot/upper.png and b/packages/pinball_components/assets/images/slingshot/upper.png differ diff --git a/packages/pinball_components/lib/src/components/baseboard.dart b/packages/pinball_components/lib/src/components/baseboard.dart index 29d7285b..07f39070 100644 --- a/packages/pinball_components/lib/src/components/baseboard.dart +++ b/packages/pinball_components/lib/src/components/baseboard.dart @@ -22,7 +22,6 @@ class Baseboard extends BodyComponent with InitialPosition { final BoardSide _side; List _createFixtureDefs() { - final fixturesDef = []; final direction = _side.direction; const arcsAngle = 1.11; final arcsRotation = (_side.isLeft) ? -2.7 : -1.6; @@ -30,12 +29,10 @@ class Baseboard extends BodyComponent with InitialPosition { final pegBumperShape = CircleShape()..radius = 0.7; pegBumperShape.position.setValues(11.11 * direction, -7.15); final pegBumperFixtureDef = FixtureDef(pegBumperShape); - fixturesDef.add(pegBumperFixtureDef); final topCircleShape = CircleShape()..radius = 0.7; topCircleShape.position.setValues(9.71 * direction, -4.95); final topCircleFixtureDef = FixtureDef(topCircleShape); - fixturesDef.add(topCircleFixtureDef); final innerEdgeShape = EdgeShape() ..set( @@ -43,7 +40,6 @@ class Baseboard extends BodyComponent with InitialPosition { Vector2(5.29 * direction, 0.95), ); final innerEdgeShapeFixtureDef = FixtureDef(innerEdgeShape); - fixturesDef.add(innerEdgeShapeFixtureDef); final outerEdgeShape = EdgeShape() ..set( @@ -51,7 +47,6 @@ class Baseboard extends BodyComponent with InitialPosition { Vector2(3.79 * direction, 5.95), ); final outerEdgeShapeFixtureDef = FixtureDef(outerEdgeShape); - fixturesDef.add(outerEdgeShapeFixtureDef); final upperArcShape = ArcShape( center: Vector2(0.09 * direction, -2.15), @@ -60,7 +55,6 @@ class Baseboard extends BodyComponent with InitialPosition { rotation: arcsRotation, ); final upperArcFixtureDef = FixtureDef(upperArcShape); - fixturesDef.add(upperArcFixtureDef); final lowerArcShape = ArcShape( center: Vector2(0.09 * direction, 3.35), @@ -69,7 +63,6 @@ class Baseboard extends BodyComponent with InitialPosition { rotation: arcsRotation, ); final lowerArcFixtureDef = FixtureDef(lowerArcShape); - fixturesDef.add(lowerArcFixtureDef); final bottomRectangle = PolygonShape() ..setAsBox( @@ -79,9 +72,16 @@ class Baseboard extends BodyComponent with InitialPosition { 0, ); final bottomRectangleFixtureDef = FixtureDef(bottomRectangle); - fixturesDef.add(bottomRectangleFixtureDef); - return fixturesDef; + return [ + pegBumperFixtureDef, + topCircleFixtureDef, + innerEdgeShapeFixtureDef, + outerEdgeShapeFixtureDef, + upperArcFixtureDef, + lowerArcFixtureDef, + bottomRectangleFixtureDef, + ]; } @override @@ -100,21 +100,26 @@ class Baseboard extends BodyComponent with InitialPosition { } class _BaseboardSpriteComponent extends SpriteComponent with HasGameRef { - _BaseboardSpriteComponent({required BoardSide side}) : _side = side; + _BaseboardSpriteComponent({required BoardSide side}) + : _side = side, + super( + anchor: Anchor.center, + position: Vector2(0.4 * -side.direction, 0), + ); final BoardSide _side; @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - (_side.isLeft) - ? Assets.images.baseboard.left.keyName - : Assets.images.baseboard.right.keyName, + final sprite = Sprite( + gameRef.images.fromCache( + (_side.isLeft) + ? Assets.images.baseboard.left.keyName + : Assets.images.baseboard.right.keyName, + ), ); this.sprite = sprite; size = sprite.originalSize / 10; - position = Vector2(0.4 * -_side.direction, 0); - anchor = Anchor.center; } } diff --git a/packages/pinball_components/lib/src/components/boundaries.dart b/packages/pinball_components/lib/src/components/boundaries.dart index 8c59d598..254cbfcf 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} @@ -33,8 +32,6 @@ class _BottomBoundary extends BodyComponent with InitialPosition { } List _createFixtureDefs() { - final fixturesDefs = []; - final bottomLeftCurve = BezierCurveShape( controlPoints: [ Vector2(-43.9, 41.8), @@ -43,7 +40,6 @@ class _BottomBoundary extends BodyComponent with InitialPosition { ], ); final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurve); - fixturesDefs.add(bottomLeftCurveFixtureDef); final bottomRightCurve = BezierCurveShape( controlPoints: [ @@ -53,9 +49,8 @@ class _BottomBoundary extends BodyComponent with InitialPosition { ], ); final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurve); - fixturesDefs.add(bottomRightCurveFixtureDef); - return fixturesDefs; + return [bottomLeftCurveFixtureDef, bottomRightCurveFixtureDef]; } @override @@ -69,16 +64,22 @@ class _BottomBoundary extends BodyComponent with InitialPosition { } class _BottomBoundarySpriteComponent extends SpriteComponent with HasGameRef { + _BottomBoundarySpriteComponent() + : super( + anchor: Anchor.center, + position: Vector2(-5.4, 55.6), + ); + @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - Assets.images.boundary.bottom.keyName, + final sprite = Sprite( + gameRef.images.fromCache( + Assets.images.boundary.bottom.keyName, + ), ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; - position = Vector2(-5.4, 55.6); } } @@ -97,15 +98,12 @@ class _OuterBoundary extends BodyComponent with InitialPosition { } List _createFixtureDefs() { - final fixturesDefs = []; - final topWall = EdgeShape() ..set( Vector2(3.6, -70.2), Vector2(-14.1, -70.2), ); final topWallFixtureDef = FixtureDef(topWall); - fixturesDefs.add(topWallFixtureDef); final topLeftCurve = BezierCurveShape( controlPoints: [ @@ -115,7 +113,6 @@ class _OuterBoundary extends BodyComponent with InitialPosition { ], ); final topLeftCurveFixtureDef = FixtureDef(topLeftCurve); - fixturesDefs.add(topLeftCurveFixtureDef); final leftWall = EdgeShape() ..set( @@ -123,9 +120,12 @@ class _OuterBoundary extends BodyComponent with InitialPosition { Vector2(-43.9, 41.8), ); final leftWallFixtureDef = FixtureDef(leftWall); - fixturesDefs.add(leftWallFixtureDef); - return fixturesDefs; + return [ + topWallFixtureDef, + topLeftCurveFixtureDef, + leftWallFixtureDef, + ]; } @override @@ -139,15 +139,21 @@ class _OuterBoundary extends BodyComponent with InitialPosition { } class _OuterBoundarySpriteComponent extends SpriteComponent with HasGameRef { + _OuterBoundarySpriteComponent() + : super( + anchor: Anchor.center, + position: Vector2(0, -7.8), + ); + @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - Assets.images.boundary.outer.keyName, + final sprite = Sprite( + gameRef.images.fromCache( + Assets.images.boundary.outer.keyName, + ), ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; - position = Vector2(-0.2, -1.4); } } diff --git a/packages/pinball_components/lib/src/components/dash_animatronic.dart b/packages/pinball_components/lib/src/components/dash_animatronic.dart index 634c5574..f1c5b145 100644 --- a/packages/pinball_components/lib/src/components/dash_animatronic.dart +++ b/packages/pinball_components/lib/src/components/dash_animatronic.dart @@ -17,12 +17,12 @@ class DashAnimatronic extends SpriteAnimationComponent with HasGameRef { Future onLoad() async { await super.onLoad(); - final spriteSheet = await gameRef.images.load( + final spriteSheet = gameRef.images.fromCache( Assets.images.dash.animatronic.keyName, ); - const amountPerRow = 12; - const amountPerColumn = 8; + const amountPerRow = 13; + const amountPerColumn = 6; final textureSize = Vector2( spriteSheet.width / amountPerRow, spriteSheet.height / amountPerColumn, 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/flipper.dart b/packages/pinball_components/lib/src/components/flipper.dart index 1d0a3c5d..bd826668 100644 --- a/packages/pinball_components/lib/src/components/flipper.dart +++ b/packages/pinball_components/lib/src/components/flipper.dart @@ -60,7 +60,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { } List _createFixtureDefs() { - final fixturesDef = []; final direction = side.direction; final assetShadow = Flipper.size.x * 0.012 * -direction; @@ -77,7 +76,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { 0, ); final bigCircleFixtureDef = FixtureDef(bigCircleShape); - fixturesDef.add(bigCircleFixtureDef); final smallCircleShape = CircleShape()..radius = size.y * 0.23; smallCircleShape.position.setValues( @@ -87,7 +85,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { 0, ); final smallCircleFixtureDef = FixtureDef(smallCircleShape); - fixturesDef.add(smallCircleFixtureDef); final trapeziumVertices = side.isLeft ? [ @@ -108,9 +105,12 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { density: 50, // TODO(alestiago): Use a proper density. friction: .1, // TODO(alestiago): Use a proper friction. ); - fixturesDef.add(trapeziumFixtureDef); - return fixturesDef; + return [ + bigCircleFixtureDef, + smallCircleFixtureDef, + trapeziumFixtureDef, + ]; } @override @@ -136,21 +136,24 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { } class _FlipperSpriteComponent extends SpriteComponent with HasGameRef { - _FlipperSpriteComponent({required BoardSide side}) : _side = side; + _FlipperSpriteComponent({required BoardSide side}) + : _side = side, + super(anchor: Anchor.center); final BoardSide _side; @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - (_side.isLeft) - ? Assets.images.flipper.left.keyName - : Assets.images.flipper.right.keyName, + final sprite = Sprite( + gameRef.images.fromCache( + (_side.isLeft) + ? Assets.images.flipper.left.keyName + : Assets.images.flipper.right.keyName, + ), ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; } } 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/render_priority.dart b/packages/pinball_components/lib/src/components/render_priority.dart index 1b02326f..b179cce9 100644 --- a/packages/pinball_components/lib/src/components/render_priority.dart +++ b/packages/pinball_components/lib/src/components/render_priority.dart @@ -49,7 +49,7 @@ abstract class RenderPriority { static const int launchRamp = _above + outerBoudary; - static const int launchRampForegroundRailing = _above + ballOnLaunchRamp; + static const int launchRampForegroundRailing = _below + ballOnBoard; static const int plunger = _above + launchRamp; @@ -63,7 +63,7 @@ abstract class RenderPriority { static const int dinoBottomWall = _above + dino; - static const int slingshot = _above + ballOnBoard; + static const int slingshot = _above + dinoBottomWall; // Flutter Forest @@ -71,7 +71,7 @@ abstract class RenderPriority { static const int dashBumper = _above + ballOnBoard; - static const int dashAnimatronic = _above + launchRampForegroundRailing; + static const int dashAnimatronic = 2 * _above + launchRamp; // Sparky Fire Zone diff --git a/packages/pinball_components/lib/src/components/slingshot.dart b/packages/pinball_components/lib/src/components/slingshot.dart index 52cdb698..35346584 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} @@ -54,18 +49,15 @@ class Slingshot extends BodyComponent with InitialPosition { final double _angle; List _createFixtureDefs() { - final fixturesDef = []; const circleRadius = 1.55; final topCircleShape = CircleShape()..radius = circleRadius; topCircleShape.position.setValues(0, -_length / 2); final topCircleFixtureDef = FixtureDef(topCircleShape); - fixturesDef.add(topCircleFixtureDef); final bottomCircleShape = CircleShape()..radius = circleRadius; bottomCircleShape.position.setValues(0, _length / 2); final bottomCircleFixtureDef = FixtureDef(bottomCircleShape); - fixturesDef.add(bottomCircleFixtureDef); final leftEdgeShape = EdgeShape() ..set( @@ -77,8 +69,6 @@ class Slingshot extends BodyComponent with InitialPosition { restitution: 5, ); - fixturesDef.add(leftEdgeShapeFixtureDef); - final rightEdgeShape = EdgeShape() ..set( Vector2(-circleRadius, _length / 2), @@ -88,9 +78,13 @@ class Slingshot extends BodyComponent with InitialPosition { rightEdgeShape, restitution: 5, ); - fixturesDef.add(rightEdgeShapeFixtureDef); - return fixturesDef; + return [ + topCircleFixtureDef, + bottomCircleFixtureDef, + leftEdgeShapeFixtureDef, + rightEdgeShapeFixtureDef, + ]; } @override @@ -123,7 +117,7 @@ class _SlinghsotSpriteComponent extends SpriteComponent with HasGameRef { @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite(_path); + final sprite = Sprite(gameRef.images.fromCache(_path)); this.sprite = sprite; size = sprite.originalSize / 10; } 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/sandbox/lib/stories/baseboard/baseboard_game.dart b/packages/pinball_components/sandbox/lib/stories/baseboard/baseboard_game.dart new file mode 100644 index 00000000..3dab5c6f --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/baseboard/baseboard_game.dart @@ -0,0 +1,38 @@ +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; + +class BaseboardGame extends BasicBallGame with Traceable { + static const info = ''' + Shows how the Baseboards are rendered. + + - Activate the "trace" parameter to overlay the body. + - Tap anywhere on the screen to spawn a ball into the game. +'''; + + @override + Future onLoad() async { + await super.onLoad(); + + await images.loadAll([ + Assets.images.baseboard.left.keyName, + Assets.images.baseboard.right.keyName, + ]); + + final center = screenToWorld(camera.viewport.canvasSize! / 2); + final leftBaseboard = Baseboard(side: BoardSide.left) + ..initialPosition = center - Vector2(25, 0) + ..priority = 1; + final rightBaseboard = Baseboard(side: BoardSide.right) + ..initialPosition = center + Vector2(25, 0) + ..priority = 1; + + await addAll([ + leftBaseboard, + rightBaseboard, + ]); + + await traceAllBodies(); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/baseboard/basic_baseboard_game.dart b/packages/pinball_components/sandbox/lib/stories/baseboard/basic_baseboard_game.dart deleted file mode 100644 index 0650fa13..00000000 --- a/packages/pinball_components/sandbox/lib/stories/baseboard/basic_baseboard_game.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:pinball_components/pinball_components.dart'; -import 'package:sandbox/common/common.dart'; - -class BasicBaseboardGame extends BasicGame { - static const info = 'Shows how a Baseboard works.'; - - @override - Future onLoad() async { - await super.onLoad(); - - final center = screenToWorld(camera.viewport.canvasSize! / 2); - - final leftBaseboard = Baseboard(side: BoardSide.left) - ..initialPosition = center - Vector2(25, 0); - final rightBaseboard = Baseboard(side: BoardSide.right) - ..initialPosition = center + Vector2(25, 0); - - await addAll([ - leftBaseboard, - rightBaseboard, - ]); - } -} diff --git a/packages/pinball_components/sandbox/lib/stories/baseboard/stories.dart b/packages/pinball_components/sandbox/lib/stories/baseboard/stories.dart index b3982af4..2e4d3090 100644 --- a/packages/pinball_components/sandbox/lib/stories/baseboard/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/baseboard/stories.dart @@ -1,15 +1,15 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import 'package:sandbox/common/common.dart'; -import 'package:sandbox/stories/baseboard/basic_baseboard_game.dart'; +import 'package:sandbox/stories/baseboard/baseboard_game.dart'; void addBaseboardStories(Dashbook dashbook) { dashbook.storiesOf('Baseboard').add( 'Basic', (context) => GameWidget( - game: BasicBaseboardGame(), + game: BaseboardGame()..trace = context.boolProperty('Trace', true), ), - codeLink: buildSourceLink('baseboard/basic.dart'), - info: BasicBaseboardGame.info, + codeLink: buildSourceLink('baseboard_game/basic.dart'), + info: BaseboardGame.info, ); } diff --git a/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart b/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart index ab984045..ca7c8cc8 100644 --- a/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart @@ -16,6 +16,11 @@ class BoundariesGame extends BasicBallGame with Traceable { Future onLoad() async { await super.onLoad(); + await images.loadAll([ + Assets.images.boundary.outer.keyName, + Assets.images.boundary.bottom.keyName, + ]); + await addFromBlueprint(Boundaries()); await ready(); diff --git a/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart b/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart index c0dfee24..40a638b9 100644 --- a/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart @@ -33,6 +33,11 @@ class FlipperGame extends BasicBallGame with KeyboardEvents, Traceable { Future onLoad() async { await super.onLoad(); + await images.loadAll([ + Assets.images.flipper.left.keyName, + Assets.images.flipper.right.keyName, + ]); + final center = screenToWorld(camera.viewport.canvasSize! / 2); leftFlipper = Flipper(side: BoardSide.left) diff --git a/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart b/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart index 7d941099..a0f54ea4 100644 --- a/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart @@ -15,6 +15,12 @@ class SlingshotGame extends BasicBallGame with Traceable { @override Future onLoad() async { await super.onLoad(); + + await images.loadAll([ + Assets.images.slingshot.upper.keyName, + Assets.images.slingshot.lower.keyName, + ]); + await addFromBlueprint(Slingshots()); camera.followVector2(Vector2.zero()); await traceAllBodies(); diff --git a/packages/pinball_components/test/src/components/baseboard_test.dart b/packages/pinball_components/test/src/components/baseboard_test.dart index 82a6475e..101e3e21 100644 --- a/packages/pinball_components/test/src/components/baseboard_test.dart +++ b/packages/pinball_components/test/src/components/baseboard_test.dart @@ -10,11 +10,16 @@ import '../../helpers/helpers.dart'; void main() { group('Baseboard', () { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(TestGame.new); + final assets = [ + Assets.images.baseboard.left.keyName, + Assets.images.baseboard.right.keyName, + ]; + final flameTester = FlameTester(() => TestGame(assets)); flameTester.testGameWidget( 'renders correctly', setUp: (game, tester) async { + await game.images.loadAll(assets); final leftBaseboard = Baseboard( side: BoardSide.left, )..initialPosition = Vector2(-20, 0); @@ -24,6 +29,7 @@ void main() { await game.ensureAddAll([leftBaseboard, rightBaseboard]); game.camera.followVector2(Vector2.zero()); + await tester.pump(); }, verify: (game, tester) async { await expectLater( diff --git a/packages/pinball_components/test/src/components/boundaries_test.dart b/packages/pinball_components/test/src/components/boundaries_test.dart index e62d63e3..9affe0b7 100644 --- a/packages/pinball_components/test/src/components/boundaries_test.dart +++ b/packages/pinball_components/test/src/components/boundaries_test.dart @@ -10,15 +10,20 @@ import '../../helpers/helpers.dart'; void main() { group('Boundaries', () { + final assets = [ + Assets.images.boundary.outer.keyName, + Assets.images.boundary.bottom.keyName, + ]; final tester = FlameTester(TestGame.new); tester.testGameWidget( 'render correctly', setUp: (game, tester) async { + await game.images.loadAll(assets); await game.addFromBlueprint(Boundaries()); game.camera.followVector2(Vector2.zero()); - game.camera.zoom = 3.9; - await game.ready(); + game.camera.zoom = 3.2; + await tester.pump(); }, verify: (game, tester) async { await expectLater( diff --git a/packages/pinball_components/test/src/components/dash_animatronic_test.dart b/packages/pinball_components/test/src/components/dash_animatronic_test.dart index e4b31ca6..8b5d0e30 100644 --- a/packages/pinball_components/test/src/components/dash_animatronic_test.dart +++ b/packages/pinball_components/test/src/components/dash_animatronic_test.dart @@ -9,29 +9,33 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(TestGame.new); + final asset = Assets.images.dash.animatronic.keyName; + final flameTester = FlameTester(() => TestGame([asset])); group('DashAnimatronic', () { flameTester.testGameWidget( 'renders correctly', setUp: (game, tester) async { + await game.images.load(asset); await game.ensureAdd(DashAnimatronic()..playing = true); game.camera.followVector2(Vector2.zero()); + await tester.pump(); }, verify: (game, tester) async { + const animationDuration = 3.25; await expectLater( find.byGame(), matchesGoldenFile('golden/dash_animatronic/start.png'), ); - game.update(1); + game.update(animationDuration * 0.25); await tester.pump(); await expectLater( find.byGame(), matchesGoldenFile('golden/dash_animatronic/middle.png'), ); - game.update(4); + game.update(animationDuration * 0.75); await tester.pump(); await expectLater( find.byGame(), diff --git a/packages/pinball_components/test/src/components/flipper_test.dart b/packages/pinball_components/test/src/components/flipper_test.dart index f413fade..c34d0d1c 100644 --- a/packages/pinball_components/test/src/components/flipper_test.dart +++ b/packages/pinball_components/test/src/components/flipper_test.dart @@ -10,7 +10,11 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(TestGame.new); + final assets = [ + Assets.images.flipper.left.keyName, + Assets.images.flipper.right.keyName, + ]; + final flameTester = FlameTester(() => TestGame(assets)); group('Flipper', () { // TODO(alestiago): Consider testing always both left and right Flipper. @@ -18,6 +22,7 @@ void main() { flameTester.testGameWidget( 'renders correctly', setUp: (game, tester) async { + await game.images.loadAll(assets); final leftFlipper = Flipper( side: BoardSide.left, )..initialPosition = Vector2(-10, 0); @@ -27,6 +32,7 @@ void main() { await game.ensureAddAll([leftFlipper, rightFlipper]); game.camera.followVector2(Vector2.zero()); + await tester.pump(); }, verify: (game, tester) async { await expectLater( diff --git a/packages/pinball_components/test/src/components/golden/baseboard.png b/packages/pinball_components/test/src/components/golden/baseboard.png index df83fa4c..01141551 100644 Binary files a/packages/pinball_components/test/src/components/golden/baseboard.png and b/packages/pinball_components/test/src/components/golden/baseboard.png differ diff --git a/packages/pinball_components/test/src/components/golden/boundaries.png b/packages/pinball_components/test/src/components/golden/boundaries.png index 4ac8f5eb..ecb396c4 100644 Binary files a/packages/pinball_components/test/src/components/golden/boundaries.png and b/packages/pinball_components/test/src/components/golden/boundaries.png differ diff --git a/packages/pinball_components/test/src/components/golden/dash_animatronic/end.png b/packages/pinball_components/test/src/components/golden/dash_animatronic/end.png index 8c99f674..3e5e91f5 100644 Binary files a/packages/pinball_components/test/src/components/golden/dash_animatronic/end.png and b/packages/pinball_components/test/src/components/golden/dash_animatronic/end.png differ diff --git a/packages/pinball_components/test/src/components/golden/dash_animatronic/middle.png b/packages/pinball_components/test/src/components/golden/dash_animatronic/middle.png index b2bf16a3..9e79695a 100644 Binary files a/packages/pinball_components/test/src/components/golden/dash_animatronic/middle.png and b/packages/pinball_components/test/src/components/golden/dash_animatronic/middle.png differ diff --git a/packages/pinball_components/test/src/components/golden/dash_animatronic/start.png b/packages/pinball_components/test/src/components/golden/dash_animatronic/start.png index 8c99f674..3e5e91f5 100644 Binary files a/packages/pinball_components/test/src/components/golden/dash_animatronic/start.png and b/packages/pinball_components/test/src/components/golden/dash_animatronic/start.png differ diff --git a/packages/pinball_components/test/src/components/golden/flipper.png b/packages/pinball_components/test/src/components/golden/flipper.png index 79b7ca5b..07fe81ed 100644 Binary files a/packages/pinball_components/test/src/components/golden/flipper.png and b/packages/pinball_components/test/src/components/golden/flipper.png differ diff --git a/packages/pinball_components/test/src/components/golden/slingshots.png b/packages/pinball_components/test/src/components/golden/slingshots.png index 2cbb1f5c..d0b01f76 100644 Binary files a/packages/pinball_components/test/src/components/golden/slingshots.png and b/packages/pinball_components/test/src/components/golden/slingshots.png differ diff --git a/packages/pinball_components/test/src/components/slingshot_test.dart b/packages/pinball_components/test/src/components/slingshot_test.dart index a5535750..69296f78 100644 --- a/packages/pinball_components/test/src/components/slingshot_test.dart +++ b/packages/pinball_components/test/src/components/slingshot_test.dart @@ -10,17 +10,22 @@ import '../../helpers/helpers.dart'; void main() { group('Slingshot', () { - final flameTester = FlameTester(TestGame.new); + final assets = [ + Assets.images.slingshot.upper.keyName, + Assets.images.slingshot.lower.keyName, + ]; + final flameTester = FlameTester(() => TestGame(assets)); const length = 2.0; const angle = 0.0; - final spritePath = Assets.images.slingshot.upper.keyName; flameTester.testGameWidget( 'renders correctly', setUp: (game, tester) async { + await game.images.loadAll(assets); await game.addFromBlueprint(Slingshots()); game.camera.followVector2(Vector2.zero()); await game.ready(); + await tester.pump(); }, verify: (game, tester) async { await expectLater( @@ -36,7 +41,7 @@ void main() { final slingshot = Slingshot( length: length, angle: angle, - spritePath: spritePath, + spritePath: assets.first, ); await game.ensureAdd(slingshot); @@ -50,7 +55,7 @@ void main() { final slingshot = Slingshot( length: length, angle: angle, - spritePath: spritePath, + spritePath: assets.first, ); await game.ensureAdd(slingshot); @@ -64,7 +69,7 @@ void main() { final slingshot = Slingshot( length: length, angle: angle, - spritePath: spritePath, + spritePath: assets.first, ); await game.ensureAdd(slingshot); @@ -82,7 +87,7 @@ void main() { final slingshot = Slingshot( length: length, angle: angle, - spritePath: spritePath, + spritePath: assets.first, ); await game.ensureAdd(slingshot); 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..de9c8b2c 100644 --- a/packages/pinball_flame/lib/src/blueprint.dart +++ b/packages/pinball_flame/lib/src/blueprint.dart @@ -1,101 +1,49 @@ 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; +class Blueprint extends Component { + /// {@macro blueprint} + Blueprint({ + Iterable? components, + Iterable? blueprints, + }) { + if (components != null) _components.addAll(components); + if (blueprints != null) { + _blueprints.addAll(blueprints); + for (final blueprint in blueprints) { + _components.addAll(blueprint.components); + } + } } - /// Adds a single [Component] to this blueprint. - @override - Future add(Component component) async { - assert(!_isAttached, _attachedErrorMessage); - _components.add(component); - } + final List _components = []; - /// Adds a list of [Blueprint]s to this blueprint. - void addAllBlueprints(List blueprints) { - assert(!_isAttached, _attachedErrorMessage); - _blueprints.addAll(blueprints); - } + final List _blueprints = []; - /// 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); + /// Returns a copy of the blueprints built by this blueprint. + List get blueprints => List.unmodifiable(_blueprints); } -/// 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..402d5059 100644 --- a/packages/pinball_flame/test/src/blueprint_test.dart +++ b/packages/pinball_flame/test/src/blueprint_test.dart @@ -1,138 +1,86 @@ +// 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()); + TestWidgetsFlutterBinding.ensureInitialized(); - expect(blueprint.components.length, equals(3)); - }); - - 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); + 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( - '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); - }, - ); - }); + test('correctly sets and gets blueprints', () { + final blueprint2 = Blueprint( + components: [Component()], + ); + final blueprint1 = Blueprint( + components: [Component()], + blueprints: [blueprint2], + ); - group('Forge2DBlueprint', () { - setUpAll(() { - registerFallbackValue(TestContactCallback()); + expect(blueprint1.blueprints, contains(blueprint2)); }); - 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/pubspec.lock b/pubspec.lock index 1a502f37..31aed6af 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "31.0.0" + version: "39.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "2.8.0" + version: "4.0.0" args: dependency: transitive description: @@ -71,13 +71,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" - cli_util: - dependency: transitive - description: - name: cli_util - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.5" clock: dependency: transitive description: @@ -321,7 +314,7 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.4" json_annotation: dependency: transitive description: @@ -356,7 +349,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4" meta: dependency: transitive description: @@ -419,7 +412,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" path_provider: dependency: transitive description: @@ -592,7 +585,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" stack_trace: dependency: transitive description: @@ -634,21 +627,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.19.5" + version: "1.21.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" + version: "0.4.9" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.13" typed_data: dependency: transitive description: @@ -669,7 +662,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" very_good_analysis: dependency: "direct dev" description: diff --git a/test/game/components/board_test.dart b/test/game/components/board_test.dart index 3ecbe6e7..bc1c5c39 100644 --- a/test/game/components/board_test.dart +++ b/test/game/components/board_test.dart @@ -16,10 +16,15 @@ void main() { Assets.images.dash.bumper.a.inactive.keyName, Assets.images.dash.bumper.b.active.keyName, Assets.images.dash.bumper.b.inactive.keyName, + Assets.images.dash.animatronic.keyName, Assets.images.signpost.inactive.keyName, Assets.images.signpost.active1.keyName, Assets.images.signpost.active2.keyName, Assets.images.signpost.active3.keyName, + Assets.images.baseboard.left.keyName, + Assets.images.baseboard.right.keyName, + Assets.images.flipper.left.keyName, + Assets.images.flipper.right.keyName, ]; final flameTester = FlameTester(() => EmptyPinballTestGame(assets)); diff --git a/test/game/components/controlled_flipper_test.dart b/test/game/components/controlled_flipper_test.dart index 01982129..1b2a7e43 100644 --- a/test/game/components/controlled_flipper_test.dart +++ b/test/game/components/controlled_flipper_test.dart @@ -11,7 +11,11 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballTestGame.new); + final assets = [ + Assets.images.flipper.left.keyName, + Assets.images.flipper.right.keyName, + ]; + final flameTester = FlameTester(() => EmptyPinballTestGame(assets)); final flameBlocTester = FlameBlocTester( gameBuilder: EmptyPinballTestGame.new, @@ -25,6 +29,7 @@ void main() { whenListen(bloc, Stream.value(state), initialState: state); return bloc; }, + assets: assets, ); group('FlipperController', () { 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/game/components/flutter_forest_test.dart b/test/game/components/flutter_forest_test.dart index 73259afd..388410d5 100644 --- a/test/game/components/flutter_forest_test.dart +++ b/test/game/components/flutter_forest_test.dart @@ -19,6 +19,7 @@ void main() { Assets.images.dash.bumper.a.inactive.keyName, Assets.images.dash.bumper.b.active.keyName, Assets.images.dash.bumper.b.inactive.keyName, + Assets.images.dash.animatronic.keyName, Assets.images.signpost.inactive.keyName, Assets.images.signpost.active1.keyName, Assets.images.signpost.active2.keyName, diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index 1775a42f..4c3dbbfb 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -19,6 +19,7 @@ void main() { Assets.images.dash.bumper.a.inactive.keyName, Assets.images.dash.bumper.b.active.keyName, Assets.images.dash.bumper.b.inactive.keyName, + Assets.images.dash.animatronic.keyName, Assets.images.signpost.inactive.keyName, Assets.images.signpost.active1.keyName, Assets.images.signpost.active2.keyName, @@ -43,6 +44,14 @@ void main() { Assets.images.spaceship.ramp.arrow.active3.keyName, Assets.images.spaceship.ramp.arrow.active4.keyName, Assets.images.spaceship.ramp.arrow.active5.keyName, + Assets.images.baseboard.left.keyName, + Assets.images.baseboard.right.keyName, + Assets.images.flipper.left.keyName, + Assets.images.flipper.right.keyName, + Assets.images.boundary.outer.keyName, + Assets.images.boundary.bottom.keyName, + Assets.images.slingshot.upper.keyName, + Assets.images.slingshot.lower.keyName, ]; final flameTester = FlameTester(() => PinballTestGame(assets)); final debugModeFlameTester = FlameTester(() => DebugPinballTestGame(assets)); @@ -52,17 +61,6 @@ void main() { // [BallScorePointsCallback] once the following issue is resolved: // https://github.com/flame-engine/flame/issues/1416 group('components', () { - flameTester.test( - 'has three Walls', - (game) async { - await game.ready(); - final walls = game.children.where( - (component) => component is Wall && component is! BottomWall, - ); - expect(walls.length, 3); - }, - ); - flameTester.test( 'has only one BottomWall', (game) async { 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 {}