From 8b866564120658c5bcdd6d003e91264f238d0a70 Mon Sep 17 00:00:00 2001 From: Rui Miguel Alonso Date: Fri, 1 Apr 2022 00:21:29 +0200 Subject: [PATCH 1/7] refactor: priority layer (#83) * refactor: removed findNested extensions (#77) * feat: added new Elevation for manage priority of BodyComponents * refactor: changed ball with Elevated * refactor: changed spaceship with Elevated * test: tests for Elevated * test: fix tests * chore: removed unused * test: fixed tests * refactor: changed Elevated mixin to Priority extension * test: fixed priority test changes * chore: ignore for file * test: fixed tests for PriorityX * chore: unused import * chore: removed unecessary ignore * refactor: removed unnecessary comma Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * refactor: fixed grammatical error in test named Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * refactor: moved priority set to constructors * chore: tests names * doc: doc priority * refactor: moved priority to flame dir * feat: added priority to rampopening * fix: fixed priority changes * test: fixed tests for spaceship priority changes * fix: fixed tests for priority * chore: renamed pathwayLayer and pathwayPriority to inside * Update packages/pinball_components/lib/src/components/ramp_opening.dart Co-authored-by: Alejandro Santiago Co-authored-by: Alejandro Santiago Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> --- lib/game/components/jetpack_ramp.dart | 2 +- lib/game/components/launcher_ramp.dart | 2 +- lib/game/components/spaceship_exit_rail.dart | 2 +- .../lib/src/components/ramp_opening.dart | 44 ++-- .../lib/src/components/spaceship.dart | 47 ++-- .../lib/src/flame/flame.dart | 1 + .../lib/src/flame/priority.dart | 39 ++++ .../test/helpers/mocks.dart | 3 + .../src/components/ramp_opening_test.dart | 34 +-- .../test/src/components/spaceship_test.dart | 35 +-- .../test/src/flame/priority_test.dart | 221 ++++++++++++++++++ test/helpers/mocks.dart | 4 - 12 files changed, 335 insertions(+), 99 deletions(-) create mode 100644 packages/pinball_components/lib/src/flame/priority.dart create mode 100644 packages/pinball_components/test/src/flame/priority_test.dart diff --git a/lib/game/components/jetpack_ramp.dart b/lib/game/components/jetpack_ramp.dart index 4c4c8be9..aa5a9dbd 100644 --- a/lib/game/components/jetpack_ramp.dart +++ b/lib/game/components/jetpack_ramp.dart @@ -107,7 +107,7 @@ class _JetpackRampOpening extends RampOpening { required double rotation, }) : _rotation = rotation, super( - pathwayLayer: Layer.jetpack, + insideLayer: Layer.jetpack, outsideLayer: outsideLayer, orientation: RampOrientation.down, ); diff --git a/lib/game/components/launcher_ramp.dart b/lib/game/components/launcher_ramp.dart index 79b7c831..c05f8aa2 100644 --- a/lib/game/components/launcher_ramp.dart +++ b/lib/game/components/launcher_ramp.dart @@ -122,7 +122,7 @@ class _LauncherRampOpening extends RampOpening { required double rotation, }) : _rotation = rotation, super( - pathwayLayer: Layer.launcher, + insideLayer: Layer.launcher, orientation: RampOrientation.down, ); diff --git a/lib/game/components/spaceship_exit_rail.dart b/lib/game/components/spaceship_exit_rail.dart index 0dc38322..4a6c44cd 100644 --- a/lib/game/components/spaceship_exit_rail.dart +++ b/lib/game/components/spaceship_exit_rail.dart @@ -169,7 +169,7 @@ class SpaceshipExitRailEnd extends RampOpening { /// {@macro spaceship_exit_rail_end} SpaceshipExitRailEnd() : super( - pathwayLayer: Layer.spaceshipExitRail, + insideLayer: Layer.spaceshipExitRail, orientation: RampOrientation.down, ) { layer = Layer.spaceshipExitRail; diff --git a/packages/pinball_components/lib/src/components/ramp_opening.dart b/packages/pinball_components/lib/src/components/ramp_opening.dart index 8f33e813..cb6066f2 100644 --- a/packages/pinball_components/lib/src/components/ramp_opening.dart +++ b/packages/pinball_components/lib/src/components/ramp_opening.dart @@ -20,28 +20,41 @@ enum RampOrientation { /// [RampOpeningBallContactCallback] detects when a [Ball] passes /// through this opening. /// -/// By default the base [layer] is set to [Layer.board]. +/// By default the base [layer] is set to [Layer.board] and the +/// [outsidePriority] is set to the lowest possible [Layer]. /// {@endtemplate} // TODO(ruialonso): Consider renaming the class. abstract class RampOpening extends BodyComponent with InitialPosition, Layered { /// {@macro ramp_opening} RampOpening({ - required Layer pathwayLayer, + required Layer insideLayer, Layer? outsideLayer, + int? insidePriority, + int? outsidePriority, required this.orientation, - }) : _pathwayLayer = pathwayLayer, - _outsideLayer = outsideLayer ?? Layer.board { - layer = Layer.board; + }) : _insideLayer = insideLayer, + _outsideLayer = outsideLayer ?? Layer.board, + _insidePriority = insidePriority ?? 0, + _outsidePriority = outsidePriority ?? 0 { + layer = Layer.opening; } - final Layer _pathwayLayer; + final Layer _insideLayer; final Layer _outsideLayer; + final int _insidePriority; + final int _outsidePriority; - /// Mask of category bits for collision inside pathway. - Layer get pathwayLayer => _pathwayLayer; + /// Mask of category bits for collision inside ramp. + Layer get insideLayer => _insideLayer; - /// Mask of category bits for collision outside pathway. + /// Mask of category bits for collision outside ramp. Layer get outsideLayer => _outsideLayer; + /// Priority for the [Ball] inside ramp. + int get insidePriority => _insidePriority; + + /// Priority for the [Ball] outside ramp. + int get outsidePriority => _outsidePriority; + /// The [Shape] of the [RampOpening]. Shape get shape; @@ -64,8 +77,7 @@ abstract class RampOpening extends BodyComponent with InitialPosition, Layered { } /// {@template ramp_opening_ball_contact_callback} -/// Detects when a [Ball] enters or exits a pathway ramp through a -/// [RampOpening]. +/// Detects when a [Ball] enters or exits a ramp through a [RampOpening]. /// /// Modifies [Ball]'s [Layer] accordingly depending on whether the [Ball] is /// outside or inside a ramp. @@ -80,9 +92,11 @@ class RampOpeningBallContactCallback Layer layer; if (!_ballsInside.contains(ball)) { - layer = opening.pathwayLayer; + layer = opening.insideLayer; _ballsInside.add(ball); - ball.layer = layer; + ball + ..sendTo(opening.insidePriority) + ..layer = layer; } else { _ballsInside.remove(ball); } @@ -103,7 +117,9 @@ class RampOpeningBallContactCallback ball.body.linearVelocity.y > 0); if (isBallOutsideOpening) { - ball.layer = opening.outsideLayer; + ball + ..sendTo(opening.outsidePriority) + ..layer = opening.outsideLayer; _ballsInside.remove(ball); } } diff --git a/packages/pinball_components/lib/src/components/spaceship.dart b/packages/pinball_components/lib/src/components/spaceship.dart index 7e9d097e..4d84eb68 100644 --- a/packages/pinball_components/lib/src/components/spaceship.dart +++ b/packages/pinball_components/lib/src/components/spaceship.dart @@ -21,6 +21,9 @@ class Spaceship extends Forge2DBlueprint { /// The [position] where the elements will be created final Vector2 position; + /// Base priority for wall while be on spaceship. + static const ballPriorityWhenOnSpaceship = 4; + @override void build(_) { addAllContactCallback([ @@ -33,8 +36,8 @@ class Spaceship extends Forge2DBlueprint { SpaceshipEntrance()..initialPosition = position, AndroidHead()..initialPosition = position, SpaceshipHole( - onExitLayer: Layer.spaceshipExitRail, - onExitElevation: 2, + outsideLayer: Layer.spaceshipExitRail, + outsidePriority: 2, )..initialPosition = position - Vector2(5.2, 4.8), SpaceshipHole()..initialPosition = position - Vector2(-7.2, 0.8), SpaceshipWall()..initialPosition = position, @@ -47,8 +50,8 @@ class Spaceship extends Forge2DBlueprint { /// {@endtemplate} class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { /// {@macro spaceship_saucer} - // TODO(ruimiguel): apply Elevated when PR merged. - SpaceshipSaucer() : super(priority: 3) { + SpaceshipSaucer() + : super(priority: Spaceship.ballPriorityWhenOnSpaceship - 1) { layer = Layer.spaceship; } @@ -92,7 +95,7 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { /// {@endtemplate} class AndroidHead extends BodyComponent with InitialPosition, Layered { /// {@macro spaceship_bridge} - AndroidHead() : super(priority: 4) { + AndroidHead() : super(priority: Spaceship.ballPriorityWhenOnSpaceship + 1) { layer = Layer.spaceship; } @@ -147,16 +150,13 @@ class SpaceshipEntrance extends RampOpening { /// {@macro spaceship_entrance} SpaceshipEntrance() : super( - pathwayLayer: Layer.spaceship, + insideLayer: Layer.spaceship, orientation: RampOrientation.up, + insidePriority: Spaceship.ballPriorityWhenOnSpaceship, ) { layer = Layer.spaceship; } - /// Priority order for [SpaceshipHole] on enter. - // TODO(ruimiguel): apply Elevated when PR merged. - final int onEnterElevation = 4; - @override Shape get shape { renderBody = false; @@ -181,19 +181,17 @@ class SpaceshipEntrance extends RampOpening { /// {@endtemplate} class SpaceshipHole extends RampOpening { /// {@macro spaceship_hole} - SpaceshipHole({Layer? onExitLayer, this.onExitElevation = 1}) + SpaceshipHole({Layer? outsideLayer, int? outsidePriority = 1}) : super( - pathwayLayer: Layer.spaceship, - outsideLayer: onExitLayer, + insideLayer: Layer.spaceship, + outsideLayer: outsideLayer, + outsidePriority: outsidePriority, orientation: RampOrientation.up, ) { + renderBody = false; layer = Layer.spaceship; } - /// Priority order for [SpaceshipHole] on exit. - // TODO(ruimiguel): apply Elevated when PR merged. - final int onExitElevation; - @override Shape get shape { return ArcShape( @@ -235,8 +233,7 @@ class _SpaceshipWallShape extends ChainShape { /// {@endtemplate} class SpaceshipWall extends BodyComponent with InitialPosition, Layered { /// {@macro spaceship_wall} - // TODO(ruimiguel): apply Elevated when PR merged - SpaceshipWall() : super(priority: 4) { + SpaceshipWall() : super(priority: Spaceship.ballPriorityWhenOnSpaceship + 1) { layer = Layer.spaceship; } @@ -269,9 +266,7 @@ class SpaceshipEntranceBallContactCallback @override void begin(SpaceshipEntrance entrance, Ball ball, _) { ball - // TODO(ruimiguel): apply Elevated when PR merged. - ..priority = entrance.onEnterElevation - ..gameRef.reorderChildren() + ..sendTo(entrance.insidePriority) ..layer = Layer.spaceship; } } @@ -279,16 +274,14 @@ class SpaceshipEntranceBallContactCallback /// [ContactCallback] that handles the contact between the [Ball] /// and a [SpaceshipHole]. /// -/// It sets the [Ball] priority and filter data so it will "be back" on the -/// board. +/// It sets the [Ball] priority and filter data so it will outside of the +/// [Spaceship]. class SpaceshipHoleBallContactCallback extends ContactCallback { @override void begin(SpaceshipHole hole, Ball ball, _) { ball - // TODO(ruimiguel): apply Elevated when PR merged. - ..priority = hole.onExitElevation - ..gameRef.reorderChildren() + ..sendTo(hole.outsidePriority) ..layer = hole.outsideLayer; } } diff --git a/packages/pinball_components/lib/src/flame/flame.dart b/packages/pinball_components/lib/src/flame/flame.dart index c46a6fd2..9af8dba6 100644 --- a/packages/pinball_components/lib/src/flame/flame.dart +++ b/packages/pinball_components/lib/src/flame/flame.dart @@ -1 +1,2 @@ export 'blueprint.dart'; +export 'priority.dart'; diff --git a/packages/pinball_components/lib/src/flame/priority.dart b/packages/pinball_components/lib/src/flame/priority.dart new file mode 100644 index 00000000..f4dccabf --- /dev/null +++ b/packages/pinball_components/lib/src/flame/priority.dart @@ -0,0 +1,39 @@ +import 'dart:math' as math; +import 'package:flame/components.dart'; + +/// Helper methods to change the [priority] of a [Component]. +extension ComponentPriorityX on Component { + static const _lowestPriority = 0; + + /// Changes the priority to a specific one. + void sendTo(int destinationPriority) { + if (priority != destinationPriority) { + priority = math.max(destinationPriority, _lowestPriority); + reorderChildren(); + } + } + + /// Changes the priority to the lowest possible. + void sendToBack() { + if (priority != _lowestPriority) { + priority = _lowestPriority; + reorderChildren(); + } + } + + /// Decreases the priority to be lower than another [Component]. + void showBehindOf(Component other) { + if (priority >= other.priority) { + priority = math.max(other.priority - 1, _lowestPriority); + reorderChildren(); + } + } + + /// Increases the priority to be higher than another [Component]. + void showInFrontOf(Component other) { + if (priority <= other.priority) { + priority = other.priority + 1; + reorderChildren(); + } + } +} diff --git a/packages/pinball_components/test/helpers/mocks.dart b/packages/pinball_components/test/helpers/mocks.dart index 8d6f45b3..c36afff2 100644 --- a/packages/pinball_components/test/helpers/mocks.dart +++ b/packages/pinball_components/test/helpers/mocks.dart @@ -1,5 +1,6 @@ import 'dart:ui'; +import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -24,3 +25,5 @@ class MockContact extends Mock implements Contact {} class MockContactCallback extends Mock implements ContactCallback {} + +class MockComponent extends Mock implements Component {} diff --git a/packages/pinball_components/test/src/components/ramp_opening_test.dart b/packages/pinball_components/test/src/components/ramp_opening_test.dart index c49e9164..cb42203a 100644 --- a/packages/pinball_components/test/src/components/ramp_opening_test.dart +++ b/packages/pinball_components/test/src/components/ramp_opening_test.dart @@ -12,7 +12,7 @@ class TestRampOpening extends RampOpening { required RampOrientation orientation, required Layer pathwayLayer, }) : super( - pathwayLayer: pathwayLayer, + insideLayer: pathwayLayer, orientation: orientation, ); @@ -129,14 +129,12 @@ void main() { final callback = TestRampOpeningBallContactCallback(); when(() => ball.body).thenReturn(body); + when(() => ball.priority).thenReturn(1); when(() => body.position).thenReturn(Vector2.zero()); when(() => ball.layer).thenReturn(Layer.board); - await game.ready(); - await game.ensureAdd(area); - callback.begin(ball, area, MockContact()); - verify(() => ball.layer = area.pathwayLayer).called(1); + verify(() => ball.layer = area.insideLayer).called(1); }); flameTester.test( @@ -152,14 +150,12 @@ void main() { final callback = TestRampOpeningBallContactCallback(); when(() => ball.body).thenReturn(body); + when(() => ball.priority).thenReturn(1); when(() => body.position).thenReturn(Vector2.zero()); when(() => ball.layer).thenReturn(Layer.board); - await game.ready(); - await game.ensureAdd(area); - callback.begin(ball, area, MockContact()); - verify(() => ball.layer = area.pathwayLayer).called(1); + verify(() => ball.layer = area.insideLayer).called(1); }); flameTester.test( @@ -174,15 +170,13 @@ void main() { final callback = TestRampOpeningBallContactCallback(); when(() => ball.body).thenReturn(body); + when(() => ball.priority).thenReturn(1); when(() => body.position).thenReturn(Vector2.zero()); when(() => body.linearVelocity).thenReturn(Vector2(0, -1)); when(() => ball.layer).thenReturn(Layer.board); - await game.ready(); - await game.ensureAdd(area); - callback.begin(ball, area, MockContact()); - verify(() => ball.layer = area.pathwayLayer).called(1); + verify(() => ball.layer = area.insideLayer).called(1); callback.end(ball, area, MockContact()); verify(() => ball.layer = Layer.board); @@ -200,15 +194,13 @@ void main() { final callback = TestRampOpeningBallContactCallback(); when(() => ball.body).thenReturn(body); + when(() => ball.priority).thenReturn(1); when(() => body.position).thenReturn(Vector2.zero()); when(() => body.linearVelocity).thenReturn(Vector2(0, 1)); when(() => ball.layer).thenReturn(Layer.board); - await game.ready(); - await game.ensureAdd(area); - callback.begin(ball, area, MockContact()); - verify(() => ball.layer = area.pathwayLayer).called(1); + verify(() => ball.layer = area.insideLayer).called(1); callback.end(ball, area, MockContact()); verify(() => ball.layer = Layer.board); @@ -226,21 +218,19 @@ void main() { final callback = TestRampOpeningBallContactCallback(); when(() => ball.body).thenReturn(body); + when(() => ball.priority).thenReturn(1); when(() => body.position).thenReturn(Vector2.zero()); when(() => body.linearVelocity).thenReturn(Vector2(0, 1)); when(() => ball.layer).thenReturn(Layer.board); - await game.ready(); - await game.ensureAdd(area); - callback.begin(ball, area, MockContact()); - verify(() => ball.layer = area.pathwayLayer).called(1); + verify(() => ball.layer = area.insideLayer).called(1); callback.end(ball, area, MockContact()); verifyNever(() => ball.layer = Layer.board); callback.begin(ball, area, MockContact()); - verifyNever(() => ball.layer = area.pathwayLayer); + verifyNever(() => ball.layer = area.insideLayer); callback.end(ball, area, MockContact()); verify(() => ball.layer = Layer.board); diff --git a/packages/pinball_components/test/src/components/spaceship_test.dart b/packages/pinball_components/test/src/components/spaceship_test.dart index f89408f7..4d980c69 100644 --- a/packages/pinball_components/test/src/components/spaceship_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_test.dart @@ -59,7 +59,8 @@ void main() { group('SpaceshipEntranceBallContactCallback', () { test('changes the ball priority on contact', () { - when(() => entrance.onEnterElevation).thenReturn(3); + when(() => ball.priority).thenReturn(2); + when(() => entrance.insidePriority).thenReturn(3); SpaceshipEntranceBallContactCallback().begin( entrance, @@ -67,39 +68,15 @@ void main() { MockContact(), ); - verify(() => ball.priority = entrance.onEnterElevation).called(1); - }); - - test('re order the game children', () { - when(() => entrance.onEnterElevation).thenReturn(3); - - SpaceshipEntranceBallContactCallback().begin( - entrance, - ball, - MockContact(), - ); - - verify(game.reorderChildren).called(1); + verify(() => ball.sendTo(entrance.insidePriority)).called(1); }); }); group('SpaceshipHoleBallContactCallback', () { test('changes the ball priority on contact', () { + when(() => ball.priority).thenReturn(2); when(() => hole.outsideLayer).thenReturn(Layer.board); - when(() => hole.onExitElevation).thenReturn(1); - - SpaceshipHoleBallContactCallback().begin( - hole, - ball, - MockContact(), - ); - - verify(() => ball.priority = hole.onExitElevation).called(1); - }); - - test('re order the game children', () { - when(() => hole.outsideLayer).thenReturn(Layer.board); - when(() => hole.onExitElevation).thenReturn(1); + when(() => hole.outsidePriority).thenReturn(1); SpaceshipHoleBallContactCallback().begin( hole, @@ -107,7 +84,7 @@ void main() { MockContact(), ); - verify(game.reorderChildren).called(1); + verify(() => ball.sendTo(hole.outsidePriority)).called(1); }); }); }); diff --git a/packages/pinball_components/test/src/flame/priority_test.dart b/packages/pinball_components/test/src/flame/priority_test.dart new file mode 100644 index 00000000..231c7744 --- /dev/null +++ b/packages/pinball_components/test/src/flame/priority_test.dart @@ -0,0 +1,221 @@ +// ignore_for_file: cascade_invocations +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball_components/src/flame/priority.dart'; + +import '../../helpers/helpers.dart'; + +class TestBodyComponent extends BodyComponent { + @override + Body createBody() { + final fixtureDef = FixtureDef(CircleShape()); + return world.createBody(BodyDef())..createFixture(fixtureDef); + } +} + +void main() { + final flameTester = FlameTester(Forge2DGame.new); + + group('ComponentPriorityX', () { + group('sendTo', () { + flameTester.test( + 'changes the priority correctly to other level', + (game) async { + const newPriority = 5; + final component = TestBodyComponent()..priority = 4; + + component.sendTo(newPriority); + + expect(component.priority, equals(newPriority)); + }, + ); + + flameTester.test( + 'calls reorderChildren if the new priority is different', + (game) async { + const newPriority = 5; + final component = MockComponent(); + when(() => component.priority).thenReturn(4); + + component.sendTo(newPriority); + + verify(component.reorderChildren).called(1); + }, + ); + + flameTester.test( + "doesn't call reorderChildren if the priority is the same", + (game) async { + const newPriority = 5; + final component = MockComponent(); + when(() => component.priority).thenReturn(newPriority); + + component.sendTo(newPriority); + + verifyNever(component.reorderChildren); + }, + ); + }); + + group('sendToBack', () { + flameTester.test( + 'changes the priority correctly to board level', + (game) async { + final component = TestBodyComponent()..priority = 4; + + component.sendToBack(); + + expect(component.priority, equals(0)); + }, + ); + + flameTester.test( + 'calls reorderChildren if the priority is greater than lowest level', + (game) async { + final component = MockComponent(); + when(() => component.priority).thenReturn(4); + + component.sendToBack(); + + verify(component.reorderChildren).called(1); + }, + ); + + flameTester.test( + "doesn't call reorderChildren if the priority is the lowest level", + (game) async { + final component = MockComponent(); + when(() => component.priority).thenReturn(0); + + component.sendToBack(); + + verifyNever(component.reorderChildren); + }, + ); + }); + + group('showBehindOf', () { + flameTester.test( + 'changes the priority if it is greater than other component', + (game) async { + const startPriority = 2; + final component = TestBodyComponent()..priority = startPriority; + final otherComponent = TestBodyComponent() + ..priority = startPriority - 1; + + component.showBehindOf(otherComponent); + + expect(component.priority, equals(otherComponent.priority - 1)); + }, + ); + + flameTester.test( + "doesn't change the priority if it is lower than other component", + (game) async { + const startPriority = 2; + final component = TestBodyComponent()..priority = startPriority; + final otherComponent = TestBodyComponent() + ..priority = startPriority + 1; + + component.showBehindOf(otherComponent); + + expect(component.priority, equals(startPriority)); + }, + ); + + flameTester.test( + 'calls reorderChildren if the priority is greater than other component', + (game) async { + const startPriority = 2; + final component = MockComponent(); + final otherComponent = MockComponent(); + when(() => component.priority).thenReturn(startPriority); + when(() => otherComponent.priority).thenReturn(startPriority - 1); + + component.showBehindOf(otherComponent); + + verify(component.reorderChildren).called(1); + }, + ); + + flameTester.test( + "doesn't call reorderChildren if the priority is lower than other " + 'component', + (game) async { + const startPriority = 2; + final component = MockComponent(); + final otherComponent = MockComponent(); + when(() => component.priority).thenReturn(startPriority); + when(() => otherComponent.priority).thenReturn(startPriority + 1); + + component.showBehindOf(otherComponent); + + verifyNever(component.reorderChildren); + }, + ); + }); + + group('showInFrontOf', () { + flameTester.test( + 'changes the priority if it is lower than other component', + (game) async { + const startPriority = 2; + final component = TestBodyComponent()..priority = startPriority; + final otherComponent = TestBodyComponent() + ..priority = startPriority + 1; + + component.showInFrontOf(otherComponent); + + expect(component.priority, equals(otherComponent.priority + 1)); + }, + ); + + flameTester.test( + "doesn't change the priority if it is greater than other component", + (game) async { + const startPriority = 2; + final component = TestBodyComponent()..priority = startPriority; + final otherComponent = TestBodyComponent() + ..priority = startPriority - 1; + + component.showInFrontOf(otherComponent); + + expect(component.priority, equals(startPriority)); + }, + ); + + flameTester.test( + 'calls reorderChildren if the priority is lower than other component', + (game) async { + const startPriority = 2; + final component = MockComponent(); + final otherComponent = MockComponent(); + when(() => component.priority).thenReturn(startPriority); + when(() => otherComponent.priority).thenReturn(startPriority + 1); + + component.showInFrontOf(otherComponent); + + verify(component.reorderChildren).called(1); + }, + ); + + flameTester.test( + "doesn't call reorderChildren if the priority is greater than other " + 'component', + (game) async { + const startPriority = 2; + final component = MockComponent(); + final otherComponent = MockComponent(); + when(() => component.priority).thenReturn(startPriority); + when(() => otherComponent.priority).thenReturn(startPriority - 1); + + component.showInFrontOf(otherComponent); + + verifyNever(component.reorderChildren); + }, + ); + }); + }); +} diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index 9453c93a..fbe8edfb 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -66,10 +66,6 @@ class MockFilter extends Mock implements Filter {} class MockFixture extends Mock implements Fixture {} -class MockSpaceshipEntrance extends Mock implements SpaceshipEntrance {} - -class MockSpaceshipHole extends Mock implements SpaceshipHole {} - class MockSpaceshipExitRailEnd extends Mock implements SpaceshipExitRailEnd {} class MockComponentSet extends Mock implements ComponentSet {} From 48f831264e860156af4dcd2c6cc92a7b359c6592 Mon Sep 17 00:00:00 2001 From: Rui Miguel Alonso Date: Fri, 1 Apr 2022 10:20:37 +0200 Subject: [PATCH 2/7] feat: add dino wall (#115) * feat: added dino walls * refactor: moved board * fix: undo board movement * feat: fixing shapes for wall with asset * fix: placed dino wall asset and shapes * fix: fixed top wall shape * feat: added dino bottom wall * fix: show background * fix: fixed geometry test * test: added golden tests for dino wall * test: removed unused import * refactor: moved dino walls to pinball_components * chore: analysis errors * refactor: more analysis errors * chore: analysis error * test: added tests for dino walls * chore: unused import * test: refactored golden test * refactor: refactored vertices shapes * test: make dino wall private and removed golden tests for them Co-authored-by: Alejandro Santiago --- lib/game/components/wall.dart | 4 +- lib/game/game_assets.dart | 2 + lib/game/pinball_game.dart | 7 + lib/gen/assets.gen.dart | 3 + packages/geometry/lib/src/geometry.dart | 2 +- packages/geometry/test/src/geometry_test.dart | 8 +- .../assets/images/dino/dino-land-bottom.png | Bin 0 -> 11297 bytes .../assets/images/dino/dino-land-top.png | Bin 0 -> 16559 bytes .../lib/gen/assets.gen.dart | 14 +- .../lib/src/components/components.dart | 1 + .../lib/src/components/dino_walls.dart | 225 ++++++++++++++++++ packages/pinball_components/pubspec.yaml | 1 + .../test/src/components/dino_walls_test.dart | 28 +++ .../components/golden/dino-bottom-wall.png | Bin 0 -> 39312 bytes .../src/components/golden/dino-top-wall.png | Bin 0 -> 45600 bytes 15 files changed, 287 insertions(+), 8 deletions(-) create mode 100644 packages/pinball_components/assets/images/dino/dino-land-bottom.png create mode 100644 packages/pinball_components/assets/images/dino/dino-land-top.png create mode 100644 packages/pinball_components/lib/src/components/dino_walls.dart create mode 100644 packages/pinball_components/test/src/components/dino_walls_test.dart create mode 100644 packages/pinball_components/test/src/components/golden/dino-bottom-wall.png create mode 100644 packages/pinball_components/test/src/components/golden/dino-top-wall.png diff --git a/lib/game/components/wall.dart b/lib/game/components/wall.dart index f5d03c80..030edc50 100644 --- a/lib/game/components/wall.dart +++ b/lib/game/components/wall.dart @@ -2,8 +2,8 @@ import 'package:flame/extensions.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:pinball/game/components/components.dart'; -import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart' hide Assets; /// {@template wall} /// A continuous generic and [BodyType.static] barrier that divides a game area. diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index ee7b7900..edfe7947 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -13,6 +13,8 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.flipper.right.keyName), images.load(components.Assets.images.baseboard.left.keyName), images.load(components.Assets.images.baseboard.right.keyName), + images.load(components.Assets.images.dino.dinoLandTop.keyName), + images.load(components.Assets.images.dino.dinoLandBottom.keyName), images.load(Assets.images.components.background.path), ]); } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index c2bbe8e0..b5162053 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -66,6 +66,13 @@ class PinballGame extends Forge2DGame Future _addGameBoundaries() async { await add(BottomWall()); createBoundaries(this).forEach(add); + unawaited( + addFromBlueprint( + DinoWalls( + position: Vector2(-2.4, 0), + ), + ), + ); } Future _addPlunger() async { diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 370d8fcf..97be7f3e 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -3,6 +3,8 @@ /// FlutterGen /// ***************************************************** +// ignore_for_file: directives_ordering,unnecessary_import + import 'package:flutter/widgets.dart'; class $AssetsImagesGen { @@ -15,6 +17,7 @@ class $AssetsImagesGen { class $AssetsImagesComponentsGen { const $AssetsImagesComponentsGen(); + /// File path: assets/images/components/background.png AssetGenImage get background => const AssetGenImage('assets/images/components/background.png'); } diff --git a/packages/geometry/lib/src/geometry.dart b/packages/geometry/lib/src/geometry.dart index 6975f8cb..edc176e7 100644 --- a/packages/geometry/lib/src/geometry.dart +++ b/packages/geometry/lib/src/geometry.dart @@ -82,7 +82,7 @@ List calculateEllipse({ /// For more information read: https://en.wikipedia.org/wiki/B%C3%A9zier_curve List calculateBezierCurve({ required List controlPoints, - double step = 0.001, + double step = 0.01, }) { assert( 0 <= step && step <= 1, diff --git a/packages/geometry/test/src/geometry_test.dart b/packages/geometry/test/src/geometry_test.dart index 7a49b2b2..e702f043 100644 --- a/packages/geometry/test/src/geometry_test.dart +++ b/packages/geometry/test/src/geometry_test.dart @@ -98,14 +98,14 @@ void main() { ); }); - test('returns by default 1000 points as indicated by step', () { + test('returns by default 100 points as indicated by step', () { final points = calculateBezierCurve( controlPoints: [ Vector2(0, 0), Vector2(10, 10), ], ); - expect(points.length, 1000); + expect(points.length, 100); }); test('returns as many points as indicated by step', () { @@ -114,9 +114,9 @@ void main() { Vector2(0, 0), Vector2(10, 10), ], - step: 0.01, + step: 0.001, ); - expect(points.length, 100); + expect(points.length, 1000); }); }); diff --git a/packages/pinball_components/assets/images/dino/dino-land-bottom.png b/packages/pinball_components/assets/images/dino/dino-land-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..1839dda3a852a83a3230ba24711fd0f043174320 GIT binary patch literal 11297 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
df&;(kjoJGDU!$~7>5tO_DAeHfsIxr>q~>o3rE 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 literal 0 HcmV?d00001 diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index 27a54591..8bd651ed 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -14,7 +14,7 @@ class $AssetsImagesGen { AssetGenImage get ball => const AssetGenImage('assets/images/ball.png'); $AssetsImagesBaseboardGen get baseboard => const $AssetsImagesBaseboardGen(); - + $AssetsImagesDinoGen get dino => const $AssetsImagesDinoGen(); $AssetsImagesFlipperGen get flipper => const $AssetsImagesFlipperGen(); /// File path: assets/images/flutter_sign_post.png @@ -42,6 +42,18 @@ class $AssetsImagesBaseboardGen { const AssetGenImage('assets/images/baseboard/right.png'); } +class $AssetsImagesDinoGen { + const $AssetsImagesDinoGen(); + + /// File path: assets/images/dino/dino-land-bottom.png + AssetGenImage get dinoLandBottom => + const AssetGenImage('assets/images/dino/dino-land-bottom.png'); + + /// File path: assets/images/dino/dino-land-top.png + AssetGenImage get dinoLandTop => + const AssetGenImage('assets/images/dino/dino-land-top.png'); +} + class $AssetsImagesFlipperGen { const $AssetsImagesFlipperGen(); diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index a475f91e..4e38c2c4 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -2,6 +2,7 @@ export 'ball.dart'; export 'baseboard.dart'; export 'board_dimensions.dart'; export 'board_side.dart'; +export 'dino_walls.dart'; export 'fire_effect.dart'; export 'flipper.dart'; export 'flutter_sign_post.dart'; diff --git a/packages/pinball_components/lib/src/components/dino_walls.dart b/packages/pinball_components/lib/src/components/dino_walls.dart new file mode 100644 index 00000000..13f56ff3 --- /dev/null +++ b/packages/pinball_components/lib/src/components/dino_walls.dart @@ -0,0 +1,225 @@ +// ignore_for_file: comment_references, avoid_renaming_method_parameters + +import 'dart:async'; + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/gen/assets.gen.dart'; +import 'package:pinball_components/pinball_components.dart' hide Assets; + +/// {@template dinowalls} +/// A [Blueprint] which creates walls for the [ChromeDino]. +/// {@endtemplate} +class DinoWalls extends Forge2DBlueprint { + /// {@macro dinowalls} + DinoWalls({required this.position}); + + /// The [position] where the elements will be created + final Vector2 position; + + @override + void build(_) { + addAll([ + _DinoTopWall()..initialPosition = position, + _DinoBottomWall()..initialPosition = position, + ]); + } +} + +/// {@template dino_top_wall} +/// Wall segment located above [ChromeDino]. +/// {@endtemplate} +class _DinoTopWall extends BodyComponent with InitialPosition { + ///{@macro dino_top_wall} + _DinoTopWall() : super(priority: 2); + + List _createFixtureDefs() { + final fixturesDef = []; + + final topStraightShape = EdgeShape() + ..set( + Vector2(29.5, 35.1), + Vector2(28.4, 35.1), + ); + final topStraightFixtureDef = FixtureDef(topStraightShape); + fixturesDef.add(topStraightFixtureDef); + + final topCurveShape = BezierCurveShape( + controlPoints: [ + topStraightShape.vertex1, + Vector2(17.4, 26.38), + Vector2(25.5, 20.7), + ], + ); + fixturesDef.add(FixtureDef(topCurveShape)); + + final middleCurveShape = BezierCurveShape( + controlPoints: [ + topCurveShape.vertices.last, + Vector2(27.8, 20.1), + Vector2(26.8, 19.5), + ], + ); + fixturesDef.add(FixtureDef(middleCurveShape)); + + final bottomCurveShape = BezierCurveShape( + controlPoints: [ + middleCurveShape.vertices.last, + Vector2(21.15, 16), + Vector2(25.6, 15.2), + ], + ); + fixturesDef.add(FixtureDef(bottomCurveShape)); + + final bottomStraightShape = EdgeShape() + ..set( + bottomCurveShape.vertices.last, + Vector2(31, 14.5), + ); + final bottomStraightFixtureDef = FixtureDef(bottomStraightShape); + fixturesDef.add(bottomStraightFixtureDef); + + return fixturesDef; + } + + @override + Body createBody() { + renderBody = false; + + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition + ..type = BodyType.static; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach( + (fixture) => body.createFixture( + fixture + ..restitution = 0.1 + ..friction = 0, + ), + ); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprite(); + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.dino.dinoLandTop.keyName, + ); + final spriteComponent = SpriteComponent( + sprite: sprite, + size: Vector2(10.6, 27.7), + anchor: Anchor.center, + position: Vector2(27, -28.2), + ); + + await add(spriteComponent); + } +} + +/// {@template dino_bottom_wall} +/// Wall segment located below [ChromeDino]. +/// {@endtemplate} +class _DinoBottomWall extends BodyComponent with InitialPosition { + ///{@macro dino_top_wall} + _DinoBottomWall() : super(priority: 2); + + List _createFixtureDefs() { + final fixturesDef = []; + + final topStraightControlPoints = [ + Vector2(32.4, 8.3), + Vector2(25, 7.7), + ]; + final topStraightShape = EdgeShape() + ..set( + topStraightControlPoints.first, + topStraightControlPoints.last, + ); + final topStraightFixtureDef = FixtureDef(topStraightShape); + fixturesDef.add(topStraightFixtureDef); + + final topLeftCurveControlPoints = [ + topStraightControlPoints.last, + Vector2(21.8, 7), + Vector2(29.5, -13.8), + ]; + final topLeftCurveShape = BezierCurveShape( + controlPoints: topLeftCurveControlPoints, + ); + fixturesDef.add(FixtureDef(topLeftCurveShape)); + + final bottomLeftStraightControlPoints = [ + topLeftCurveControlPoints.last, + Vector2(31.8, -44.1), + ]; + final bottomLeftStraightShape = EdgeShape() + ..set( + bottomLeftStraightControlPoints.first, + bottomLeftStraightControlPoints.last, + ); + final bottomLeftStraightFixtureDef = FixtureDef(bottomLeftStraightShape); + fixturesDef.add(bottomLeftStraightFixtureDef); + + final bottomStraightControlPoints = [ + bottomLeftStraightControlPoints.last, + Vector2(37.8, -44.1), + ]; + final bottomStraightShape = EdgeShape() + ..set( + bottomStraightControlPoints.first, + bottomStraightControlPoints.last, + ); + final bottomStraightFixtureDef = FixtureDef(bottomStraightShape); + fixturesDef.add(bottomStraightFixtureDef); + + return fixturesDef; + } + + @override + Body createBody() { + renderBody = false; + + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition + ..type = BodyType.static; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach( + (fixture) => body.createFixture( + fixture + ..restitution = 0.1 + ..friction = 0, + ), + ); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprite(); + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.dino.dinoLandBottom.keyName, + ); + final spriteComponent = SpriteComponent( + sprite: sprite, + size: Vector2(15.6, 54.8), + anchor: Anchor.center, + )..position = Vector2(31.7, 18); + + await add(spriteComponent); + } +} diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index c0eab284..d1f138d9 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -27,6 +27,7 @@ flutter: assets: - assets/images/ - assets/images/baseboard/ + - assets/images/dino/ - assets/images/flipper/ flutter_gen: diff --git a/packages/pinball_components/test/src/components/dino_walls_test.dart b/packages/pinball_components/test/src/components/dino_walls_test.dart new file mode 100644 index 00000000..af80444b --- /dev/null +++ b/packages/pinball_components/test/src/components/dino_walls_test.dart @@ -0,0 +1,28 @@ +// ignore_for_file: cascade_invocations + +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'; + +import '../../helpers/helpers.dart'; + +void main() { + group('DinoWalls', () { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(TestGame.new); + + flameTester.test( + 'loads correctly', + (game) async { + final dinoWalls = DinoWalls(position: Vector2.zero()); + await game.addFromBlueprint(dinoWalls); + await game.ready(); + + for (final wall in dinoWalls.components) { + expect(game.contains(wall), isTrue); + } + }, + ); + }); +} diff --git a/packages/pinball_components/test/src/components/golden/dino-bottom-wall.png b/packages/pinball_components/test/src/components/golden/dino-bottom-wall.png new file mode 100644 index 0000000000000000000000000000000000000000..14ae9c0d3daf286a1d288bd2e8e69fec7055a2e5 GIT binary patch literal 39312 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 literal 0 HcmV?d00001 diff --git a/packages/pinball_components/test/src/components/golden/dino-top-wall.png b/packages/pinball_components/test/src/components/golden/dino-top-wall.png new file mode 100644 index 0000000000000000000000000000000000000000..0d434d694322b84c746757eb3d2adda91a5ad6e6 GIT binary patch literal 45600 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 literal 0 HcmV?d00001 From e5c37089522d45dc25f43f2336ac0062bd7c75c9 Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Fri, 1 Apr 2022 09:35:03 +0100 Subject: [PATCH 3/7] feat: implemented DashBumper assets (#119) * chore: rebase * chore: rebase * chore: rebase * docs: removed extra paragraph * fix: removed checks * refactor: removed ephemeral state * refactor: uncommented code * feat: automatically sized Sprites * docs: included TODO comment * refactor: corrected typo * docs: updated doc comment * feat: adjusted tests * chore: rebase * feat: included mustCallSuper * feat: implemented FlutterForest controllers * feat: improved tests * fix: analyzer * refactor: removed unneccessary mock * feat: include DashNestBumper tests * docs: improved doc comment * refactor: fixed test name grammar * fix: fixed test --- lib/flame/component_controller.dart | 2 + lib/game/components/flutter_forest.dart | 195 ++++++------ lib/game/game_assets.dart | 6 + .../assets/images/dash_bumper/a/active.png | Bin 0 -> 10739 bytes .../assets/images/dash_bumper/a/inactive.png | Bin 0 -> 10452 bytes .../assets/images/dash_bumper/b/active.png | Bin 0 -> 10553 bytes .../assets/images/dash_bumper/b/inactive.png | Bin 0 -> 10233 bytes .../assets/images/dash_bumper/main/active.png | Bin 0 -> 8795 bytes .../images/dash_bumper/main/inactive.png | Bin 0 -> 8644 bytes .../lib/gen/assets.gen.dart | 47 +++ .../lib/src/components/components.dart | 1 + .../lib/src/components/dash_nest_bumper.dart | 142 +++++++++ packages/pinball_components/pubspec.yaml | 3 + .../src/components/dash_nest_bumper_test.dart | 116 +++++++ .../test/src/components/fire_effect_test.dart | 5 +- test/game/components/flutter_forest_test.dart | 285 ++++++++++++++---- 16 files changed, 648 insertions(+), 154 deletions(-) create mode 100644 packages/pinball_components/assets/images/dash_bumper/a/active.png create mode 100644 packages/pinball_components/assets/images/dash_bumper/a/inactive.png create mode 100644 packages/pinball_components/assets/images/dash_bumper/b/active.png create mode 100644 packages/pinball_components/assets/images/dash_bumper/b/inactive.png create mode 100644 packages/pinball_components/assets/images/dash_bumper/main/active.png create mode 100644 packages/pinball_components/assets/images/dash_bumper/main/inactive.png create mode 100644 packages/pinball_components/lib/src/components/dash_nest_bumper.dart create mode 100644 packages/pinball_components/test/src/components/dash_nest_bumper_test.dart diff --git a/lib/flame/component_controller.dart b/lib/flame/component_controller.dart index 2bbf5ca9..851028f0 100644 --- a/lib/flame/component_controller.dart +++ b/lib/flame/component_controller.dart @@ -1,5 +1,6 @@ import 'package:flame/components.dart'; import 'package:flame_bloc/flame_bloc.dart'; +import 'package:flutter/foundation.dart'; /// {@template component_controller} /// A [ComponentController] is a [Component] in charge of handling the logic @@ -30,6 +31,7 @@ mixin Controls on Component { late final T controller; @override + @mustCallSuper Future onLoad() async { await super.onLoad(); await add(controller); diff --git a/lib/game/components/flutter_forest.dart b/lib/game/components/flutter_forest.dart index 6eb3ce7d..2d7bdf33 100644 --- a/lib/game/components/flutter_forest.dart +++ b/lib/game/components/flutter_forest.dart @@ -1,11 +1,10 @@ // ignore_for_file: avoid_renaming_method_parameters -import 'dart:math' as math; - import 'package:flame/components.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; +import 'package:pinball/flame/flame.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -16,10 +15,47 @@ import 'package:pinball_components/pinball_components.dart'; /// When all [DashNestBumper]s are hit at least once, the [GameBonus.dashNest] /// is awarded, and the [BigDashNestBumper] releases a new [Ball]. /// {@endtemplate} -// TODO(alestiago): Make a [Blueprint] once nesting [Blueprint] is implemented. -class FlutterForest extends Component - with HasGameRef, BlocComponent { +// TODO(alestiago): Make a [Blueprint] once [Blueprint] inherits from +// [Component]. +class FlutterForest extends Component with Controls<_FlutterForestController> { /// {@macro flutter_forest} + FlutterForest() { + controller = _FlutterForestController(this); + } + + @override + Future onLoad() async { + await super.onLoad(); + final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, 58.3); + + final bigNest = _ControlledBigDashNestBumper( + id: 'big_nest_bumper', + )..initialPosition = Vector2(18.55, 59.35); + final smallLeftNest = _ControlledSmallDashNestBumper.a( + id: 'small_nest_bumper_a', + )..initialPosition = Vector2(8.95, 51.95); + final smallRightNest = _ControlledSmallDashNestBumper.b( + id: 'small_nest_bumper_b', + )..initialPosition = Vector2(23.3, 46.75); + + await addAll([ + signPost, + smallLeftNest, + smallRightNest, + bigNest, + ]); + } +} + +class _FlutterForestController extends ComponentController + with BlocComponent, HasGameRef { + _FlutterForestController(FlutterForest flutterForest) : super(flutterForest); + + @override + Future onLoad() async { + await super.onLoad(); + gameRef.addContactCallback(_ControlledDashNestBumperBallContactCallback()); + } @override bool listenWhen(GameState? previousState, GameState newState) { @@ -32,117 +68,90 @@ class FlutterForest extends Component void onNewState(GameState state) { super.onNewState(state); - add( - ControlledBall.bonus( - theme: gameRef.theme, - )..initialPosition = Vector2(17.2, 52.7), + component.add( + ControlledBall.bonus(theme: gameRef.theme) + ..initialPosition = Vector2(17.2, 52.7), ); } +} - @override - Future onLoad() async { - gameRef.addContactCallback(DashNestBumperBallContactCallback()); +class _ControlledBigDashNestBumper extends BigDashNestBumper + with Controls, ScorePoints { + _ControlledBigDashNestBumper({required String id}) : super() { + controller = DashNestBumperController(this, id: id); + } - final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, 58.3); + @override + int get points => 20; +} - // TODO(alestiago): adjust positioning once sprites are added. - final smallLeftNest = SmallDashNestBumper(id: 'small_left_nest') - ..initialPosition = Vector2(8.95, 51.95); - final smallRightNest = SmallDashNestBumper(id: 'small_right_nest') - ..initialPosition = Vector2(23.3, 46.75); - final bigNest = BigDashNestBumper(id: 'big_nest') - ..initialPosition = Vector2(18.55, 59.35); +class _ControlledSmallDashNestBumper extends SmallDashNestBumper + with Controls, ScorePoints { + _ControlledSmallDashNestBumper.a({required String id}) : super.a() { + controller = DashNestBumperController(this, id: id); + } - await addAll([ - signPost, - smallLeftNest, - smallRightNest, - bigNest, - ]); + _ControlledSmallDashNestBumper.b({required String id}) : super.b() { + controller = DashNestBumperController(this, id: id); } + + @override + int get points => 10; } -/// {@template dash_nest_bumper} -/// Bumper located in the [FlutterForest]. +/// {@template dash_nest_bumper_controller} +/// Controls a [DashNestBumper]. /// {@endtemplate} @visibleForTesting -abstract class DashNestBumper extends BodyComponent - with ScorePoints, InitialPosition { - /// {@macro dash_nest_bumper} - DashNestBumper({required this.id}) { - paint = Paint() - ..color = Colors.blue.withOpacity(0.5) - ..style = PaintingStyle.fill; - } - - /// Unique identifier for this [DashNestBumper]. +class DashNestBumperController extends ComponentController + with BlocComponent, HasGameRef { + /// {@macro dash_nest_bumper_controller} + DashNestBumperController( + DashNestBumper dashNestBumper, { + required this.id, + }) : super(dashNestBumper); + + /// Unique identifier for the controlled [DashNestBumper]. /// /// Used to identify [DashNestBumper]s in [GameState.activatedDashNests]. final String id; -} -/// Listens when a [Ball] bounces bounces against a [DashNestBumper]. -@visibleForTesting -class DashNestBumperBallContactCallback - extends ContactCallback { @override - void begin(DashNestBumper dashNestBumper, Ball ball, Contact _) { - dashNestBumper.gameRef.read().add( - DashNestActivated(dashNestBumper.id), - ); - } -} + bool listenWhen(GameState? previousState, GameState newState) { + final wasActive = previousState?.activatedDashNests.contains(id) ?? false; + final isActive = newState.activatedDashNests.contains(id); -/// {@macro dash_nest_bumper} -@visibleForTesting -class BigDashNestBumper extends DashNestBumper { - /// {@macro dash_nest_bumper} - BigDashNestBumper({required String id}) : super(id: id); + return wasActive != isActive; + } @override - int get points => 20; + void onNewState(GameState state) { + super.onNewState(state); - @override - Body createBody() { - final shape = EllipseShape( - center: Vector2.zero(), - majorRadius: 4.85, - minorRadius: 3.95, - )..rotate(math.pi / 2); - final fixtureDef = FixtureDef(shape); - - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this; - - return world.createBody(bodyDef)..createFixture(fixtureDef); + if (state.activatedDashNests.contains(id)) { + component.activate(); + } else { + component.deactivate(); + } } -} -/// {@macro dash_nest_bumper} -@visibleForTesting -class SmallDashNestBumper extends DashNestBumper { - /// {@macro dash_nest_bumper} - SmallDashNestBumper({required String id}) : super(id: id); - - @override - int get points => 10; + /// Registers when a [DashNestBumper] is hit by a [Ball]. + /// + /// Triggered by [_ControlledDashNestBumperBallContactCallback]. + void hit() { + gameRef.read().add(DashNestActivated(id)); + } +} +/// Listens when a [Ball] bounces bounces against a [DashNestBumper]. +class _ControlledDashNestBumperBallContactCallback + extends ContactCallback, Ball> { @override - Body createBody() { - final shape = EllipseShape( - center: Vector2.zero(), - majorRadius: 3, - minorRadius: 2.25, - )..rotate(math.pi / 2); - final fixtureDef = FixtureDef(shape) - ..friction = 0 - ..restitution = 4; - - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this; - - return world.createBody(bodyDef)..createFixture(fixtureDef); + void begin( + Controls controlledDashNestBumper, + Ball _, + Contact __, + ) { + controlledDashNestBumper.controller.hit(); } } diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index edfe7947..cc8aac9c 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -15,6 +15,12 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.baseboard.right.keyName), images.load(components.Assets.images.dino.dinoLandTop.keyName), images.load(components.Assets.images.dino.dinoLandBottom.keyName), + images.load(components.Assets.images.dashBumper.a.active.keyName), + images.load(components.Assets.images.dashBumper.a.inactive.keyName), + images.load(components.Assets.images.dashBumper.b.active.keyName), + images.load(components.Assets.images.dashBumper.b.inactive.keyName), + images.load(components.Assets.images.dashBumper.main.active.keyName), + images.load(components.Assets.images.dashBumper.main.inactive.keyName), images.load(Assets.images.components.background.path), ]); } diff --git a/packages/pinball_components/assets/images/dash_bumper/a/active.png b/packages/pinball_components/assets/images/dash_bumper/a/active.png new file mode 100644 index 0000000000000000000000000000000000000000..feeee11f4e87bd8e9e622fbbc8d9752ef23a3d5a GIT binary patch literal 10739 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 literal 0 HcmV?d00001 diff --git a/packages/pinball_components/assets/images/dash_bumper/a/inactive.png b/packages/pinball_components/assets/images/dash_bumper/a/inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..58ab8c56638c956ed04c25d2a7e343ec1871ed4f GIT binary patch literal 10452 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

-*0KAU4sD|H}ud*g&QycmS@M|*??-74aV|u zBV_b9!b%!a_ct^&S(0XPpGGm%dA_&(PDy|L{1eunqt0(yno;~ysFS@u@m8aK>5XC5 zOQQ}BlX?VSt@v|mYHmI)ZXH{ zV@T0&veLgyTv04{tzYTP);C&qGB-}l!JG@2F6V&q1njJ=JZKMNdOEQncEGHF z9*#{lxgGSNGW1z4*MCpy^1D`;Lva*v{7C!$;F9ereaP1h=~c%yR=J%WcRQmyN3H7~ z=@sACmHP^nW5Z33fk2aOs_=ChsSKoU&fK}dZ8S17|CHm~bHceE1iMCvT;ogJPKC?Gu%U zqkxywjv_#mh7%Gjq3N%v6U-_JlyPb5>9M`yASagzVUgBLL!1=yk@(oSov5|U9eCSh z`^-c$oljpD|Ca>vyv?Ql_u&01Y8iECM{`|tK%}6Netrs1Hr3S9vhu{MP7i3|kIV#d z+GHe_=`m~B%L|>6;D!|9#L@FQgo@msu9mM@#LZU?(`O51!IeM=HHgWKb3K2M}&A8`OR)kM#^``p=|MVwI$G9LZo68 z+dE+{>F;r!J?Sa6``+T@H{jGcUoZXg&|~;cg=N(>QGz3kL8;)*=PNuXZMPFp+g3h@KY!V3$SzrShOdLfyb>j z1GVWmGyDgLCDNWGB%a21&2R*g{5c|qJU7qB?j13wGO?#Jorxti>0f~pu}ig%`}v~_ z7(98?53YZbJDGw{wt)L&_H?%$Zp`{knGoG+v42-pRkgF&4~@H7W#NHs+Y>%%joxoP|4#^vP*`}*|KT`L-4l$HGeQ6l-o#QnX&&?#I=i6evW`{NevY7!yMC3^3#`0AiUuJbxlo8r+h6v!Ew5PBN1cO z)P6GokneHuubN;0Ts4f&W7pwGR)h;4wD<*lclqL+=3vas%vgXvhQEFlpZCWjI-%l$ zQkPz69|;eXsTNt%MfSs5-~kI=uz4iNrNNoVS~vt+0)!|6exOb1_Ro)$sL!1y_0F%| Y+i?ikEAS_SXCZMEWK^ZgB+bJ92f^rSfdBvi literal 0 HcmV?d00001 diff --git a/packages/pinball_components/assets/images/dash_bumper/b/inactive.png b/packages/pinball_components/assets/images/dash_bumper/b/inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..eddc7693c2467d3054396d2c869df7e8b818f21d GIT binary patch literal 10233 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 Qp5?=llU9-{lr#$XAA1b|^b#SuXwk_;?-6B` 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 B^g#ds literal 0 HcmV?d00001 diff --git a/packages/pinball_components/assets/images/dash_bumper/main/inactive.png b/packages/pinball_components/assets/images/dash_bumper/main/inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..e6f15b382f69bd70a8d4e4aef6dcecfa17c111da GIT binary patch literal 8644 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 const AssetGenImage('assets/images/ball.png'); $AssetsImagesBaseboardGen get baseboard => const $AssetsImagesBaseboardGen(); + $AssetsImagesDashBumperGen get dashBumper => + const $AssetsImagesDashBumperGen(); $AssetsImagesDinoGen get dino => const $AssetsImagesDinoGen(); $AssetsImagesFlipperGen get flipper => const $AssetsImagesFlipperGen(); @@ -42,6 +44,15 @@ class $AssetsImagesBaseboardGen { const AssetGenImage('assets/images/baseboard/right.png'); } +class $AssetsImagesDashBumperGen { + const $AssetsImagesDashBumperGen(); + + $AssetsImagesDashBumperAGen get a => const $AssetsImagesDashBumperAGen(); + $AssetsImagesDashBumperBGen get b => const $AssetsImagesDashBumperBGen(); + $AssetsImagesDashBumperMainGen get main => + const $AssetsImagesDashBumperMainGen(); +} + class $AssetsImagesDinoGen { const $AssetsImagesDinoGen(); @@ -66,6 +77,42 @@ class $AssetsImagesFlipperGen { const AssetGenImage('assets/images/flipper/right.png'); } +class $AssetsImagesDashBumperAGen { + const $AssetsImagesDashBumperAGen(); + + /// File path: assets/images/dash_bumper/a/active.png + AssetGenImage get active => + const AssetGenImage('assets/images/dash_bumper/a/active.png'); + + /// File path: assets/images/dash_bumper/a/inactive.png + AssetGenImage get inactive => + const AssetGenImage('assets/images/dash_bumper/a/inactive.png'); +} + +class $AssetsImagesDashBumperBGen { + const $AssetsImagesDashBumperBGen(); + + /// File path: assets/images/dash_bumper/b/active.png + AssetGenImage get active => + const AssetGenImage('assets/images/dash_bumper/b/active.png'); + + /// File path: assets/images/dash_bumper/b/inactive.png + AssetGenImage get inactive => + const AssetGenImage('assets/images/dash_bumper/b/inactive.png'); +} + +class $AssetsImagesDashBumperMainGen { + const $AssetsImagesDashBumperMainGen(); + + /// File path: assets/images/dash_bumper/main/active.png + AssetGenImage get active => + const AssetGenImage('assets/images/dash_bumper/main/active.png'); + + /// File path: assets/images/dash_bumper/main/inactive.png + AssetGenImage get inactive => + const AssetGenImage('assets/images/dash_bumper/main/inactive.png'); +} + class Assets { Assets._(); diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index 4e38c2c4..bbb2c29c 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -2,6 +2,7 @@ export 'ball.dart'; export 'baseboard.dart'; export 'board_dimensions.dart'; export 'board_side.dart'; +export 'dash_nest_bumper.dart'; export 'dino_walls.dart'; export 'fire_effect.dart'; export 'flipper.dart'; diff --git a/packages/pinball_components/lib/src/components/dash_nest_bumper.dart b/packages/pinball_components/lib/src/components/dash_nest_bumper.dart new file mode 100644 index 00000000..a2b9b982 --- /dev/null +++ b/packages/pinball_components/lib/src/components/dash_nest_bumper.dart @@ -0,0 +1,142 @@ +import 'dart:math' as math; + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// {@template dash_nest_bumper} +/// Bumper with a nest appearance. +/// {@endtemplate} +abstract class DashNestBumper extends BodyComponent with InitialPosition { + /// {@macro dash_nest_bumper} + DashNestBumper._({ + required String activeAssetPath, + required String inactiveAssetPath, + required SpriteComponent spriteComponent, + }) : _activeAssetPath = activeAssetPath, + _inactiveAssetPath = inactiveAssetPath, + _spriteComponent = spriteComponent; + + final String _activeAssetPath; + late final Sprite _activeSprite; + final String _inactiveAssetPath; + late final Sprite _inactiveSprite; + final SpriteComponent _spriteComponent; + + Future _loadSprites() async { + // TODO(alestiago): I think ideally we would like to do: + // Sprite(path).load so we don't require to store the activeAssetPath and + // the inactive assetPath. + _inactiveSprite = await gameRef.loadSprite(_inactiveAssetPath); + _activeSprite = await gameRef.loadSprite(_activeAssetPath); + } + + /// Activates the [DashNestBumper]. + void activate() { + _spriteComponent + ..sprite = _activeSprite + ..size = _activeSprite.originalSize / 10; + } + + /// Deactivates the [DashNestBumper]. + void deactivate() { + _spriteComponent + ..sprite = _inactiveSprite + ..size = _inactiveSprite.originalSize / 10; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprites(); + + // TODO(erickzanardo): Look into using onNewState instead. + // Currently doing: onNewState(gameRef.read()) will throw an + // `Exception: build context is not available yet` + deactivate(); + await add(_spriteComponent); + } +} + +/// {@macro dash_nest_bumper} +class BigDashNestBumper extends DashNestBumper { + /// {@macro dash_nest_bumper} + BigDashNestBumper() + : super._( + activeAssetPath: Assets.images.dashBumper.main.active.keyName, + inactiveAssetPath: Assets.images.dashBumper.main.inactive.keyName, + spriteComponent: SpriteComponent( + anchor: Anchor.center, + ), + ); + + @override + Body createBody() { + final shape = EllipseShape( + center: Vector2.zero(), + majorRadius: 4.85, + minorRadius: 3.95, + )..rotate(math.pi / 2); + final fixtureDef = FixtureDef(shape); + + final bodyDef = BodyDef() + ..position = initialPosition + ..userData = this; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} + +/// {@macro dash_nest_bumper} +class SmallDashNestBumper extends DashNestBumper { + /// {@macro dash_nest_bumper} + SmallDashNestBumper._({ + required String activeAssetPath, + required String inactiveAssetPath, + required SpriteComponent spriteComponent, + }) : super._( + activeAssetPath: activeAssetPath, + inactiveAssetPath: inactiveAssetPath, + spriteComponent: spriteComponent, + ); + + /// {@macro dash_nest_bumper} + SmallDashNestBumper.a() + : this._( + activeAssetPath: Assets.images.dashBumper.a.active.keyName, + inactiveAssetPath: Assets.images.dashBumper.a.inactive.keyName, + spriteComponent: SpriteComponent( + anchor: Anchor.center, + position: Vector2(0.35, -1.2), + ), + ); + + /// {@macro dash_nest_bumper} + SmallDashNestBumper.b() + : this._( + activeAssetPath: Assets.images.dashBumper.b.active.keyName, + inactiveAssetPath: Assets.images.dashBumper.b.inactive.keyName, + spriteComponent: SpriteComponent( + anchor: Anchor.center, + position: Vector2(0.35, -1.2), + ), + ); + + @override + Body createBody() { + final shape = EllipseShape( + center: Vector2.zero(), + majorRadius: 3, + minorRadius: 2.25, + )..rotate(math.pi / 2); + final fixtureDef = FixtureDef(shape) + ..friction = 0 + ..restitution = 4; + + final bodyDef = BodyDef() + ..position = initialPosition + ..userData = this; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index d1f138d9..8fc9c6f8 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -29,6 +29,9 @@ flutter: - assets/images/baseboard/ - assets/images/dino/ - assets/images/flipper/ + - assets/images/dash_bumper/a/ + - assets/images/dash_bumper/b/ + - assets/images/dash_bumper/main/ flutter_gen: line_length: 80 diff --git a/packages/pinball_components/test/src/components/dash_nest_bumper_test.dart b/packages/pinball_components/test/src/components/dash_nest_bumper_test.dart new file mode 100644 index 00000000..2c6bb00c --- /dev/null +++ b/packages/pinball_components/test/src/components/dash_nest_bumper_test.dart @@ -0,0 +1,116 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame/components.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(TestGame.new); + + group('BigDashNestBumper', () { + flameTester.test('loads correctly', (game) async { + final bumper = BigDashNestBumper(); + await game.ensureAdd(bumper); + expect(game.contains(bumper), isTrue); + }); + + flameTester.test('activate returns normally', (game) async { + final bumper = BigDashNestBumper(); + await game.ensureAdd(bumper); + + expect(bumper.activate, returnsNormally); + }); + + flameTester.test('deactivate returns normally', (game) async { + final bumper = BigDashNestBumper(); + await game.ensureAdd(bumper); + + expect(bumper.deactivate, returnsNormally); + }); + + flameTester.test('changes sprite', (game) async { + final bumper = BigDashNestBumper(); + await game.ensureAdd(bumper); + + final spriteComponent = bumper.firstChild()!; + + final deactivatedSprite = spriteComponent.sprite; + bumper.activate(); + expect( + spriteComponent.sprite, + isNot(equals(deactivatedSprite)), + ); + + final activatedSprite = spriteComponent.sprite; + bumper.deactivate(); + expect( + spriteComponent.sprite, + isNot(equals(activatedSprite)), + ); + + expect( + activatedSprite, + isNot(equals(deactivatedSprite)), + ); + }); + }); + + group('SmallDashNestBumper', () { + flameTester.test('"a" loads correctly', (game) async { + final bumper = SmallDashNestBumper.a(); + await game.ensureAdd(bumper); + + expect(game.contains(bumper), isTrue); + }); + + flameTester.test('"b" loads correctly', (game) async { + final bumper = SmallDashNestBumper.b(); + await game.ensureAdd(bumper); + expect(game.contains(bumper), isTrue); + }); + + flameTester.test('activate returns normally', (game) async { + final bumper = SmallDashNestBumper.a(); + await game.ensureAdd(bumper); + + expect(bumper.activate, returnsNormally); + }); + + flameTester.test('deactivate returns normally', (game) async { + final bumper = SmallDashNestBumper.a(); + await game.ensureAdd(bumper); + + expect(bumper.deactivate, returnsNormally); + }); + + flameTester.test('changes sprite', (game) async { + final bumper = SmallDashNestBumper.a(); + await game.ensureAdd(bumper); + + final spriteComponent = bumper.firstChild()!; + + final deactivatedSprite = spriteComponent.sprite; + bumper.activate(); + expect( + spriteComponent.sprite, + isNot(equals(deactivatedSprite)), + ); + + final activatedSprite = spriteComponent.sprite; + bumper.deactivate(); + expect( + spriteComponent.sprite, + isNot(equals(activatedSprite)), + ); + + expect( + activatedSprite, + isNot(equals(deactivatedSprite)), + ); + }); + }); +} diff --git a/packages/pinball_components/test/src/components/fire_effect_test.dart b/packages/pinball_components/test/src/components/fire_effect_test.dart index bc6baa4b..7bc62212 100644 --- a/packages/pinball_components/test/src/components/fire_effect_test.dart +++ b/packages/pinball_components/test/src/components/fire_effect_test.dart @@ -48,8 +48,9 @@ void main() { final canvas = MockCanvas(); effect.render(canvas); - verify(() => canvas.drawCircle(any(), any(), any())) - .called(greaterThan(0)); + verify(() => canvas.drawCircle(any(), any(), any())).called( + greaterThan(0), + ); }); }); } diff --git a/test/game/components/flutter_forest_test.dart b/test/game/components/flutter_forest_test.dart index 48586895..a0e1b81f 100644 --- a/test/game/components/flutter_forest_test.dart +++ b/test/game/components/flutter_forest_test.dart @@ -1,7 +1,9 @@ // ignore_for_file: cascade_invocations import 'package:bloc_test/bloc_test.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball/game/game.dart'; @@ -9,6 +11,18 @@ import 'package:pinball_components/pinball_components.dart'; import '../../helpers/helpers.dart'; +void beginContact(Forge2DGame game, BodyComponent bodyA, BodyComponent bodyB) { + assert( + bodyA.body.fixtures.isNotEmpty && bodyB.body.fixtures.isNotEmpty, + 'Bodies require fixtures to contact each other.', + ); + + final fixtureA = bodyA.body.fixtures.first; + final fixtureB = bodyB.body.fixtures.first; + final contact = Contact.init(fixtureA, 0, fixtureB, 0); + game.world.contactManager.contactListener?.beginContact(contact); +} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(PinballGameTest.create); @@ -30,13 +44,73 @@ void main() { 'a FlutterSignPost', (game) async { await game.ready(); + final flutterForest = FlutterForest(); + await game.ensureAdd(flutterForest); expect( - game.descendants().whereType().length, + flutterForest.descendants().whereType().length, equals(1), ); }, ); + + flameTester.test( + 'a BigDashNestBumper', + (game) async { + await game.ready(); + final flutterForest = FlutterForest(); + await game.ensureAdd(flutterForest); + + expect( + flutterForest.descendants().whereType().length, + equals(1), + ); + }, + ); + + flameTester.test( + 'two SmallDashNestBumper', + (game) async { + await game.ready(); + final flutterForest = FlutterForest(); + await game.ensureAdd(flutterForest); + + expect( + flutterForest.descendants().whereType().length, + equals(2), + ); + }, + ); + }); + + group('controller', () { + group('listenWhen', () { + final gameBloc = MockGameBloc(); + final tester = flameBlocTester( + game: TestGame.new, + gameBloc: () => gameBloc, + ); + + tester.testGameWidget( + 'listens when a Bonus.dashNest is added', + verify: (game, tester) async { + final flutterForest = FlutterForest(); + + const state = GameState( + score: 0, + balls: 3, + activatedBonusLetters: [], + activatedDashNests: {}, + bonusHistory: [GameBonus.dashNest], + ); + expect( + flutterForest.controller + .listenWhen(const GameState.initial(), state), + isTrue, + ); + }, + ); + }); }); flameTester.test( @@ -47,7 +121,7 @@ void main() { await game.ensureAdd(flutterForest); final previousBalls = game.descendants().whereType().length; - flutterForest.onNewState(MockGameState()); + flutterForest.controller.onNewState(MockGameState()); await game.ready(); expect( @@ -57,14 +131,13 @@ void main() { }, ); - group('listenWhen', () { - final gameBloc = MockGameBloc(); - final tester = flameBlocTester( - game: TestGame.new, - gameBloc: () => gameBloc, - ); + group('bumpers', () { + late Ball ball; + late GameBloc gameBloc; setUp(() { + ball = Ball(baseColor: const Color(0xFF00FFFF)); + gameBloc = MockGameBloc(); whenListen( gameBloc, const Stream.empty(), @@ -72,73 +145,167 @@ void main() { ); }); + final tester = flameBlocTester( + game: PinballGameTest.create, + gameBloc: () => gameBloc, + ); + + tester.testGameWidget( + 'add DashNestActivated event', + setUp: (game, tester) async { + await game.ready(); + final flutterForest = + game.descendants().whereType().first; + await game.ensureAdd(ball); + + final bumpers = + flutterForest.descendants().whereType(); + + for (final bumper in bumpers) { + beginContact(game, bumper, ball); + final controller = bumper.firstChild()!; + verify( + () => gameBloc.add(DashNestActivated(controller.id)), + ).called(1); + } + }, + ); + tester.testGameWidget( - 'listens when a Bonus.dashNest is added', - verify: (game, tester) async { + 'add Scored event', + setUp: (game, tester) async { final flutterForest = FlutterForest(); + await game.ensureAdd(flutterForest); + await game.ensureAdd(ball); - const state = GameState( - score: 0, - balls: 3, - activatedBonusLetters: [], - activatedDashNests: {}, - bonusHistory: [GameBonus.dashNest], - ); - expect( - flutterForest.listenWhen(const GameState.initial(), state), - isTrue, - ); + final bumpers = + flutterForest.descendants().whereType(); + + for (final bumper in bumpers) { + beginContact(game, bumper, ball); + final points = (bumper as ScorePoints).points; + verify( + () => gameBloc.add(Scored(points: points)), + ).called(1); + } }, ); }); }); - group('DashNestBumperBallContactCallback', () { - final gameBloc = MockGameBloc(); - final tester = flameBlocTester( - // TODO(alestiago): Use TestGame.new once a controller is implemented. - game: PinballGameTest.create, - gameBloc: () => gameBloc, - ); + group('DashNestBumperController', () { + late DashNestBumper dashNestBumper; setUp(() { - whenListen( - gameBloc, - const Stream.empty(), - initialState: const GameState.initial(), - ); + dashNestBumper = MockDashNestBumper(); }); - final dashNestBumper = MockDashNestBumper(); - tester.testGameWidget( - 'adds a DashNestActivated event with DashNestBumper.id', - setUp: (game, tester) async { - const id = '0'; - when(() => dashNestBumper.id).thenReturn(id); - when(() => dashNestBumper.gameRef).thenReturn(game); - }, - verify: (game, tester) async { - final contactCallback = DashNestBumperBallContactCallback(); - contactCallback.begin(dashNestBumper, MockBall(), MockContact()); + group( + 'listensWhen', + () { + late GameState previousState; + late GameState newState; - verify( - () => gameBloc.add(DashNestActivated(dashNestBumper.id)), - ).called(1); + setUp( + () { + previousState = MockGameState(); + newState = MockGameState(); + }, + ); + + test('listens when the id is added to activatedDashNests', () { + const id = ''; + final controller = DashNestBumperController( + dashNestBumper, + id: id, + ); + + when(() => previousState.activatedDashNests).thenReturn({}); + when(() => newState.activatedDashNests).thenReturn({id}); + + expect(controller.listenWhen(previousState, newState), isTrue); + }); + + test('listens when the id is removed from activatedDashNests', () { + const id = ''; + final controller = DashNestBumperController( + dashNestBumper, + id: id, + ); + + when(() => previousState.activatedDashNests).thenReturn({id}); + when(() => newState.activatedDashNests).thenReturn({}); + + expect(controller.listenWhen(previousState, newState), isTrue); + }); + + test("doesn't listen when the id is never in activatedDashNests", () { + final controller = DashNestBumperController( + dashNestBumper, + id: '', + ); + + when(() => previousState.activatedDashNests).thenReturn({}); + when(() => newState.activatedDashNests).thenReturn({}); + + expect(controller.listenWhen(previousState, newState), isFalse); + }); + + test("doesn't listen when the id still in activatedDashNests", () { + const id = ''; + final controller = DashNestBumperController( + dashNestBumper, + id: id, + ); + + when(() => previousState.activatedDashNests).thenReturn({id}); + when(() => newState.activatedDashNests).thenReturn({id}); + + expect(controller.listenWhen(previousState, newState), isFalse); + }); }, ); - }); - group('BigDashNestBumper', () { - test('has points', () { - final dashNestBumper = BigDashNestBumper(id: ''); - expect(dashNestBumper.points, greaterThan(0)); - }); - }); + group( + 'onNewState', + () { + late GameState state; - group('SmallDashNestBumper', () { - test('has points', () { - final dashNestBumper = SmallDashNestBumper(id: ''); - expect(dashNestBumper.points, greaterThan(0)); - }); + setUp(() { + state = MockGameState(); + }); + + test( + 'activates the bumper when id in activatedDashNests', + () { + const id = ''; + final controller = DashNestBumperController( + dashNestBumper, + id: id, + ); + + when(() => state.activatedDashNests).thenReturn({id}); + controller.onNewState(state); + + verify(() => dashNestBumper.activate()).called(1); + }, + ); + + test( + 'deactivates the bumper when id not in activatedDashNests', + () { + final controller = DashNestBumperController( + dashNestBumper, + id: '', + ); + + when(() => state.activatedDashNests).thenReturn({}); + controller.onNewState(state); + + verify(() => dashNestBumper.deactivate()).called(1); + }, + ); + }, + ); }); } From cbbf7b121e5fcb6bef8c44d84944afc94a6358f6 Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Fri, 1 Apr 2022 12:55:17 +0100 Subject: [PATCH 4/7] fix: flaky FireEffect test (#124) * refactor: migrated to ParticleSystemComponent * refactor: simplified tests * feat: forced min count to be 1 * feat: adjusted priorities --- .../lib/src/components/ball.dart | 5 +- .../lib/src/components/fire_effect.dart | 57 ++++++------------- .../lib/stories/effects/fire_effect.dart | 1 - .../test/helpers/mocks.dart | 4 -- .../test/src/components/fire_effect_test.dart | 48 +++------------- 5 files changed, 28 insertions(+), 87 deletions(-) diff --git a/packages/pinball_components/lib/src/components/ball.dart b/packages/pinball_components/lib/src/components/ball.dart index b62ceeba..892936f9 100644 --- a/packages/pinball_components/lib/src/components/ball.dart +++ b/packages/pinball_components/lib/src/components/ball.dart @@ -83,8 +83,9 @@ class Ball extends BodyComponent final direction = body.linearVelocity.normalized(); final effect = FireEffect( burstPower: _boostTimer, - direction: direction, - position: body.position, + direction: -direction, + position: Vector2(body.position.x, -body.position.y), + priority: priority - 1, ); unawaited(gameRef.add(effect)); diff --git a/packages/pinball_components/lib/src/components/fire_effect.dart b/packages/pinball_components/lib/src/components/fire_effect.dart index 0a7cef2b..cf8c3707 100644 --- a/packages/pinball_components/lib/src/components/fire_effect.dart +++ b/packages/pinball_components/lib/src/components/fire_effect.dart @@ -1,5 +1,6 @@ import 'dart:math' as math; +import 'package:flame/components.dart'; import 'package:flame/extensions.dart'; import 'package:flame/particles.dart'; import 'package:flame_forge2d/flame_forge2d.dart' hide Particle; @@ -19,33 +20,24 @@ const _particleRadius = 0.25; /// A [BodyComponent] which creates a fire trail effect using the given /// parameters /// {@endtemplate} -class FireEffect extends BodyComponent { +class FireEffect extends ParticleSystemComponent { /// {@macro fire_effect} FireEffect({ required this.burstPower, - required this.position, required this.direction, - }); + Vector2? position, + int? priority, + }) : super( + position: position, + priority: priority, + ); /// A [double] value that will define how "strong" the burst of particles - /// will be + /// will be. final double burstPower; - /// The position of the burst - final Vector2 position; - - /// Which direction the burst will aim + /// Which direction the burst will aim. final Vector2 direction; - late Particle _particle; - - @override - Body createBody() { - final bodyDef = BodyDef()..position = position; - - final fixtureDef = FixtureDef(CircleShape()..radius = 0)..isSensor = true; - - return world.createBody(bodyDef)..createFixture(fixtureDef); - } @override Future onLoad() async { @@ -71,15 +63,15 @@ class FireEffect extends BodyComponent { ); }), ]; - final rng = math.Random(); + final random = math.Random(); final spreadTween = Tween(begin: -0.2, end: 0.2); - _particle = Particle.generate( - count: (rng.nextDouble() * (burstPower * 10)).toInt(), + particle = Particle.generate( + count: math.max((random.nextDouble() * (burstPower * 10)).toInt(), 1), generator: (_) { final spread = Vector2( - spreadTween.transform(rng.nextDouble()), - spreadTween.transform(rng.nextDouble()), + spreadTween.transform(random.nextDouble()), + spreadTween.transform(random.nextDouble()), ); final finalDirection = Vector2(direction.x, -direction.y) + spread; final speed = finalDirection * (burstPower * 20); @@ -88,26 +80,9 @@ class FireEffect extends BodyComponent { lifespan: 5 / burstPower, position: Vector2.zero(), speed: speed, - child: children[rng.nextInt(children.length)], + child: children[random.nextInt(children.length)], ); }, ); } - - @override - void update(double dt) { - super.update(dt); - _particle.update(dt); - - if (_particle.shouldRemove) { - removeFromParent(); - } - } - - @override - void render(Canvas canvas) { - super.render(canvas); - - _particle.render(canvas); - } } diff --git a/packages/pinball_components/sandbox/lib/stories/effects/fire_effect.dart b/packages/pinball_components/sandbox/lib/stories/effects/fire_effect.dart index 9f066952..1262af11 100644 --- a/packages/pinball_components/sandbox/lib/stories/effects/fire_effect.dart +++ b/packages/pinball_components/sandbox/lib/stories/effects/fire_effect.dart @@ -34,7 +34,6 @@ class _EffectEmitter extends Component { add( FireEffect( burstPower: (_timer / _timerLimit) * _force, - position: Vector2.zero(), direction: _direction, ), ); diff --git a/packages/pinball_components/test/helpers/mocks.dart b/packages/pinball_components/test/helpers/mocks.dart index c36afff2..7771d1e1 100644 --- a/packages/pinball_components/test/helpers/mocks.dart +++ b/packages/pinball_components/test/helpers/mocks.dart @@ -1,12 +1,8 @@ -import 'dart:ui'; - import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/pinball_components.dart'; -class MockCanvas extends Mock implements Canvas {} - class MockFilter extends Mock implements Filter {} class MockFixture extends Mock implements Fixture {} diff --git a/packages/pinball_components/test/src/components/fire_effect_test.dart b/packages/pinball_components/test/src/components/fire_effect_test.dart index 7bc62212..2c404747 100644 --- a/packages/pinball_components/test/src/components/fire_effect_test.dart +++ b/packages/pinball_components/test/src/components/fire_effect_test.dart @@ -1,11 +1,8 @@ // ignore_for_file: cascade_invocations -import 'dart:ui'; - import 'package:flame/components.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/pinball_components.dart'; import '../../helpers/helpers.dart'; @@ -14,43 +11,16 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(TestGame.new); - setUpAll(() { - registerFallbackValue(Offset.zero); - registerFallbackValue(Paint()); - }); - - group('FireEffect', () { - flameTester.test('is removed once its particles are done', (game) async { - await game.ensureAdd( - FireEffect( - burstPower: 1, - position: Vector2.zero(), - direction: Vector2.all(2), - ), - ); - await game.ready(); - expect(game.children.whereType().length, equals(1)); - game.update(5); - - await game.ready(); - expect(game.children.whereType().length, equals(0)); - }); - - flameTester.test('render circles on the canvas', (game) async { - final effect = FireEffect( + flameTester.test( + 'loads correctly', + (game) async { + final fireEffect = FireEffect( burstPower: 1, - position: Vector2.zero(), - direction: Vector2.all(2), + direction: Vector2.zero(), ); - await game.ensureAdd(effect); - await game.ready(); - - final canvas = MockCanvas(); - effect.render(canvas); + await game.ensureAdd(fireEffect); - verify(() => canvas.drawCircle(any(), any(), any())).called( - greaterThan(0), - ); - }); - }); + expect(game.contains(fireEffect), isTrue); + }, + ); } From 40d0fd0995c89428d06adc01ede26bd2465e9b2b Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Fri, 1 Apr 2022 13:04:33 +0100 Subject: [PATCH 5/7] refactor: implemented FlameBlocTester (#122) * refactor: implemented FlameBlocTester * refactor: renamed parameter bloc to blocBuilder --- test/game/components/bonus_word_test.dart | 14 ++++---- .../game/components/controlled_ball_test.dart | 18 +++++----- test/game/components/flutter_forest_test.dart | 18 +++++----- test/helpers/builders.dart | 34 +++++++++---------- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/test/game/components/bonus_word_test.dart b/test/game/components/bonus_word_test.dart index f48d60ee..7d73b6bc 100644 --- a/test/game/components/bonus_word_test.dart +++ b/test/game/components/bonus_word_test.dart @@ -196,10 +196,10 @@ void main() { group('bonus letter activation', () { late GameBloc gameBloc; - final tester = flameBlocTester( + final flameBlocTester = FlameBlocTester( // TODO(alestiago): Use TestGame once BonusLetter has controller. - game: PinballGameTest.create, - gameBloc: () => gameBloc, + gameBuilder: PinballGameTest.create, + blocBuilder: () => gameBloc, ); setUp(() { @@ -211,7 +211,7 @@ void main() { ); }); - tester.testGameWidget( + flameBlocTester.testGameWidget( 'adds BonusLetterActivated to GameBloc when not activated', setUp: (game, tester) async { await game.ready(); @@ -225,7 +225,7 @@ void main() { }, ); - tester.testGameWidget( + flameBlocTester.testGameWidget( "doesn't add BonusLetterActivated to GameBloc when already activated", setUp: (game, tester) async { const state = GameState( @@ -253,7 +253,7 @@ void main() { }, ); - tester.testGameWidget( + flameBlocTester.testGameWidget( 'adds a ColorEffect', setUp: (game, tester) async { const state = GameState( @@ -284,7 +284,7 @@ void main() { }, ); - tester.testGameWidget( + flameBlocTester.testGameWidget( 'only listens when there is a change on the letter status', setUp: (game, tester) async { await game.ready(); diff --git a/test/game/components/controlled_ball_test.dart b/test/game/components/controlled_ball_test.dart index dcd075ca..8417aa25 100644 --- a/test/game/components/controlled_ball_test.dart +++ b/test/game/components/controlled_ball_test.dart @@ -66,12 +66,12 @@ void main() { ); }); - final tester = flameBlocTester( - game: PinballGameTest.create, - gameBloc: () => gameBloc, + final flameBlocTester = FlameBlocTester( + gameBuilder: PinballGameTest.create, + blocBuilder: () => gameBloc, ); - tester.testGameWidget( + flameBlocTester.testGameWidget( 'lost adds BallLost to GameBloc', setUp: (game, tester) async { final controller = LaunchedBallController(ball); @@ -86,7 +86,7 @@ void main() { ); group('listenWhen', () { - tester.testGameWidget( + flameBlocTester.testGameWidget( 'listens when a ball has been lost', setUp: (game, tester) async { final controller = LaunchedBallController(ball); @@ -107,7 +107,7 @@ void main() { }, ); - tester.testGameWidget( + flameBlocTester.testGameWidget( 'does not listen when a ball has not been lost', setUp: (game, tester) async { final controller = LaunchedBallController(ball); @@ -130,7 +130,7 @@ void main() { }); group('onNewState', () { - tester.testGameWidget( + flameBlocTester.testGameWidget( 'removes ball', setUp: (game, tester) async { final controller = LaunchedBallController(ball); @@ -147,7 +147,7 @@ void main() { }, ); - tester.testGameWidget( + flameBlocTester.testGameWidget( 'spawns a new ball when the ball is not the last one', setUp: (game, tester) async { final controller = LaunchedBallController(ball); @@ -168,7 +168,7 @@ void main() { }, ); - tester.testGameWidget( + flameBlocTester.testGameWidget( 'does not spawn a new ball is the last one', setUp: (game, tester) async { final controller = LaunchedBallController(ball); diff --git a/test/game/components/flutter_forest_test.dart b/test/game/components/flutter_forest_test.dart index a0e1b81f..33dbb991 100644 --- a/test/game/components/flutter_forest_test.dart +++ b/test/game/components/flutter_forest_test.dart @@ -86,12 +86,12 @@ void main() { group('controller', () { group('listenWhen', () { final gameBloc = MockGameBloc(); - final tester = flameBlocTester( - game: TestGame.new, - gameBloc: () => gameBloc, + final flameBlocTester = FlameBlocTester( + gameBuilder: TestGame.new, + blocBuilder: () => gameBloc, ); - tester.testGameWidget( + flameBlocTester.testGameWidget( 'listens when a Bonus.dashNest is added', verify: (game, tester) async { final flutterForest = FlutterForest(); @@ -145,12 +145,12 @@ void main() { ); }); - final tester = flameBlocTester( - game: PinballGameTest.create, - gameBloc: () => gameBloc, + final flameBlocTester = FlameBlocTester( + gameBuilder: PinballGameTest.create, + blocBuilder: () => gameBloc, ); - tester.testGameWidget( + flameBlocTester.testGameWidget( 'add DashNestActivated event', setUp: (game, tester) async { await game.ready(); @@ -171,7 +171,7 @@ void main() { }, ); - tester.testGameWidget( + flameBlocTester.testGameWidget( 'add Scored event', setUp: (game, tester) async { final flutterForest = FlutterForest(); diff --git a/test/helpers/builders.dart b/test/helpers/builders.dart index 970dd12b..f78aebe7 100644 --- a/test/helpers/builders.dart +++ b/test/helpers/builders.dart @@ -1,21 +1,21 @@ -import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame/src/game/flame_game.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:pinball/game/game.dart'; -FlameTester flameBlocTester({ - required T Function() game, - required GameBloc Function() gameBloc, -}) { - return FlameTester( - game, - pumpWidget: (gameWidget, tester) async { - await tester.pumpWidget( - BlocProvider.value( - value: gameBloc(), - child: gameWidget, - ), - ); - }, - ); +class FlameBlocTester> + extends FlameTester { + FlameBlocTester({ + required GameCreateFunction gameBuilder, + required B Function() blocBuilder, + }) : super( + gameBuilder, + pumpWidget: (gameWidget, tester) async { + await tester.pumpWidget( + BlocProvider.value( + value: blocBuilder(), + child: gameWidget, + ), + ); + }, + ); } From e6fd5f90fa084ffefd2a3936449b251e68b91a6f Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Fri, 1 Apr 2022 13:07:25 +0100 Subject: [PATCH 6/7] feat: disallowed adding components to `ComponentController` (#121) * feat: disallowed adding components to a ComponentController * docs: rephrased text * refactor: fixed variable typo * feat: included addAll test --- lib/flame/component_controller.dart | 5 ++++ test/flame/component_controller_test.dart | 30 +++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/flame/component_controller.dart b/lib/flame/component_controller.dart index 851028f0..1d6e0173 100644 --- a/lib/flame/component_controller.dart +++ b/lib/flame/component_controller.dart @@ -23,6 +23,11 @@ abstract class ComponentController extends Component { ); await super.addToParent(parent); } + + @override + Future add(Component component) { + throw Exception('ComponentController cannot add other components.'); + } } /// Mixin that attaches a single [ComponentController] to a [Component]. diff --git a/test/flame/component_controller_test.dart b/test/flame/component_controller_test.dart index 4e5da210..e1973274 100644 --- a/test/flame/component_controller_test.dart +++ b/test/flame/component_controller_test.dart @@ -31,6 +31,7 @@ void main() { ); }, ); + flameTester.test( 'throws AssertionError when not attached to controlled component', (game) async { @@ -44,6 +45,35 @@ void main() { ); }, ); + + flameTester.test( + 'throws Exception when adding a component', + (game) async { + final component = ControlledComponent(); + final controller = TestComponentController(component); + + await expectLater( + () async => controller.add(Component()), + throwsException, + ); + }, + ); + + flameTester.test( + 'throws Exception when adding multiple components', + (game) async { + final component = ControlledComponent(); + final controller = TestComponentController(component); + + await expectLater( + () async => controller.addAll([ + Component(), + Component(), + ]), + throwsException, + ); + }, + ); }); group('Controls', () { From c8bc7c194221f0ae300cd676db3d4fcf64281b9a Mon Sep 17 00:00:00 2001 From: Rui Miguel Alonso Date: Fri, 1 Apr 2022 14:36:16 +0200 Subject: [PATCH 7/7] feat: spaceship drop assets (#110) * feat: added assets for drop rail * feat: placed assets and base bodies * feat: assets and bases * refactor: hide spaceship hole * refactor: redimension curves * fix: lint * test: fix geometry tests * refactor: tube to fit spaceship opening * refactor: applied priority changes to drop tube * test: fixed tests for ball contact with exit rail * Update test/game/components/spaceship_exit_rail_test.dart Co-authored-by: Alejandro Santiago * chore: formatted test file Co-authored-by: Allison Ryan Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> Co-authored-by: Alejandro Santiago --- .../images/components/spaceship-drop-tube.png | Bin 0 -> 64940 bytes lib/game/components/spaceship_exit_rail.dart | 243 ++++++++++-------- lib/game/game_assets.dart | 1 + lib/game/pinball_game.dart | 4 +- lib/gen/assets.gen.dart | 4 + packages/geometry/test/src/geometry_test.dart | 4 +- .../lib/src/components/spaceship.dart | 4 +- .../components/spaceship_exit_rail_test.dart | 16 +- 8 files changed, 154 insertions(+), 122 deletions(-) create mode 100644 assets/images/components/spaceship-drop-tube.png diff --git a/assets/images/components/spaceship-drop-tube.png b/assets/images/components/spaceship-drop-tube.png new file mode 100644 index 0000000000000000000000000000000000000000..4b299c2cdf9726054d8e859fe4b6d101765fe63e GIT binary patch literal 64940 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%0A#hiNx}Wz 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

8C`?hs}Qxv8BOs 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 _createFixtureDefs() { - const entranceRotationAngle = 175 * math.pi / 180; - const curveRotationAngle = 275 * math.pi / 180; - const exitRotationAngle = 340 * math.pi / 180; - const width = 5.5; - final fixturesDefs = []; - final entranceWall = ArcShape( - center: Vector2(width / 2, 0), - arcRadius: width / 2, + final topArcShape = ArcShape( + center: Vector2(-35.5, 30.9), + arcRadius: 2.5, angle: math.pi, - rotation: entranceRotationAngle, + rotation: 2.9, ); - final entranceFixtureDef = FixtureDef(entranceWall); - fixturesDefs.add(entranceFixtureDef); - - final topLeftControlPoints = [ - Vector2(0, 0), - Vector2(10, .5), - Vector2(7, 4), - Vector2(15.5, 8.3), - ]; + final topArcFixtureDef = FixtureDef(topArcShape); + fixturesDefs.add(topArcFixtureDef); + final topLeftCurveShape = BezierCurveShape( - controlPoints: topLeftControlPoints, - )..rotate(curveRotationAngle); - final topLeftFixtureDef = FixtureDef(topLeftCurveShape); - fixturesDefs.add(topLeftFixtureDef); - - final topRightControlPoints = [ - Vector2(0, width), - Vector2(10, 6.5), - Vector2(7, 10), - Vector2(15.5, 13.2), - ]; - final topRightCurveShape = BezierCurveShape( - controlPoints: topRightControlPoints, - )..rotate(curveRotationAngle); - final topRightFixtureDef = FixtureDef(topRightCurveShape); - fixturesDefs.add(topRightFixtureDef); - - final mediumLeftControlPoints = [ - topLeftControlPoints.last, - Vector2(21, 12.9), - Vector2(30, 7.1), - Vector2(32, 4.8), - ]; - final mediumLeftCurveShape = BezierCurveShape( - controlPoints: mediumLeftControlPoints, - )..rotate(curveRotationAngle); - final mediumLeftFixtureDef = FixtureDef(mediumLeftCurveShape); - fixturesDefs.add(mediumLeftFixtureDef); - - final mediumRightControlPoints = [ - topRightControlPoints.last, - Vector2(21, 17.2), - Vector2(30, 12.1), - Vector2(32, 10.2), - ]; - final mediumRightCurveShape = BezierCurveShape( - controlPoints: mediumRightControlPoints, - )..rotate(curveRotationAngle); - final mediumRightFixtureDef = FixtureDef(mediumRightCurveShape); - fixturesDefs.add(mediumRightFixtureDef); - - final bottomLeftControlPoints = [ - mediumLeftControlPoints.last, - Vector2(40, -1), - Vector2(48, 1.9), - Vector2(50.5, 2.5), - ]; + controlPoints: [ + Vector2(-37.9, 30.4), + Vector2(-38, 23.9), + Vector2(-30.93, 18.2), + ], + ); + final topLeftCurveFixtureDef = FixtureDef(topLeftCurveShape); + fixturesDefs.add(topLeftCurveFixtureDef); + + final middleLeftCurveShape = BezierCurveShape( + controlPoints: [ + Vector2(-30.93, 18.2), + Vector2(-22.6, 10.3), + Vector2(-30, 0.2), + ], + ); + final middleLeftCurveFixtureDef = FixtureDef(middleLeftCurveShape); + fixturesDefs.add(middleLeftCurveFixtureDef); + final bottomLeftCurveShape = BezierCurveShape( - controlPoints: bottomLeftControlPoints, - )..rotate(curveRotationAngle); - final bottomLeftFixtureDef = FixtureDef(bottomLeftCurveShape); - fixturesDefs.add(bottomLeftFixtureDef); - - final bottomRightControlPoints = [ - mediumRightControlPoints.last, - Vector2(40, 4), - Vector2(46, 6.5), - Vector2(48.8, 7.6), - ]; + controlPoints: [ + Vector2(-30, 0.2), + Vector2(-36, -8.6), + Vector2(-32.04, -18.3), + ], + ); + final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurveShape); + fixturesDefs.add(bottomLeftCurveFixtureDef); + + final topRightStraightShape = EdgeShape() + ..set( + Vector2(-33, 31.3), + Vector2(-27.2, 21.3), + ); + final topRightStraightFixtureDef = FixtureDef(topRightStraightShape); + fixturesDefs.add(topRightStraightFixtureDef); + + final middleRightCurveShape = BezierCurveShape( + controlPoints: [ + Vector2(-27.2, 21.3), + Vector2(-16.5, 11.4), + Vector2(-25.29, -1.7), + ], + ); + final middleRightCurveFixtureDef = FixtureDef(middleRightCurveShape); + fixturesDefs.add(middleRightCurveFixtureDef); + final bottomRightCurveShape = BezierCurveShape( - controlPoints: bottomRightControlPoints, - )..rotate(curveRotationAngle); - final bottomRightFixtureDef = FixtureDef(bottomRightCurveShape); - fixturesDefs.add(bottomRightFixtureDef); - - final exitWall = ArcShape( - center: exitPoint, - arcRadius: width / 2, - angle: math.pi, - rotation: exitRotationAngle, + controlPoints: [ + Vector2(-25.29, -1.7), + Vector2(-29.91, -8.5), + Vector2(-26.8, -15.7), + ], ); - final exitFixtureDef = FixtureDef(exitWall); - fixturesDefs.add(exitFixtureDef); + final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurveShape); + fixturesDefs.add(bottomRightCurveFixtureDef); return fixturesDefs; } @@ -159,6 +133,52 @@ class _SpaceshipExitRailRamp extends BodyComponent return body; } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprite(); + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.components.spaceshipDropTube.path, + ); + final spriteComponent = SpriteComponent( + sprite: sprite, + size: Vector2(17.5, 55.7), + anchor: Anchor.center, + position: Vector2(-29.4, -5.7), + ); + + await add(spriteComponent); + } +} + +class _SpaceshipExitRailBase extends BodyComponent + with InitialPosition, Layered { + _SpaceshipExitRailBase({required this.radius}) + : super( + priority: SpaceshipExitRail.ballPriorityWhenOnSpaceshipExitRail + 1, + ) { + renderBody = false; + layer = Layer.board; + } + + final double radius; + + @override + Body createBody() { + final shape = CircleShape()..radius = radius; + + final fixtureDef = FixtureDef(shape); + + final bodyDef = BodyDef() + ..position = initialPosition + ..userData = this; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } } /// {@template spaceship_exit_rail_end} @@ -172,12 +192,18 @@ class SpaceshipExitRailEnd extends RampOpening { insideLayer: Layer.spaceshipExitRail, orientation: RampOrientation.down, ) { + renderBody = false; layer = Layer.spaceshipExitRail; } @override Shape get shape { - return CircleShape()..radius = 1; + return ArcShape( + center: Vector2(-29, -17.8), + arcRadius: 2.5, + angle: math.pi * 0.8, + rotation: -0.16, + ); } } @@ -191,8 +217,7 @@ class SpaceshipExitRailEndBallContactCallback @override void begin(SpaceshipExitRailEnd exitRail, Ball ball, _) { ball - ..priority = 1 - ..gameRef.reorderChildren() + ..sendTo(exitRail.outsidePriority) ..layer = exitRail.outsideLayer; } } diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index cc8aac9c..e0d25592 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -22,6 +22,7 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.dashBumper.main.active.keyName), images.load(components.Assets.images.dashBumper.main.inactive.keyName), images.load(Assets.images.components.background.path), + images.load(Assets.images.components.spaceshipDropTube.path), ]); } } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index b5162053..0711e677 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -45,9 +45,7 @@ class PinballGame extends Forge2DGame ); unawaited( addFromBlueprint( - SpaceshipExitRail( - position: Vector2(-34.3, 23.8), - ), + SpaceshipExitRail(), ), ); diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 97be7f3e..2fe23925 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -20,6 +20,10 @@ class $AssetsImagesComponentsGen { /// File path: assets/images/components/background.png AssetGenImage get background => const AssetGenImage('assets/images/components/background.png'); + + /// File path: assets/images/components/spaceship-drop-tube.png + AssetGenImage get spaceshipDropTube => + const AssetGenImage('assets/images/components/spaceship-drop-tube.png'); } class Assets { diff --git a/packages/geometry/test/src/geometry_test.dart b/packages/geometry/test/src/geometry_test.dart index e702f043..eb5e8d74 100644 --- a/packages/geometry/test/src/geometry_test.dart +++ b/packages/geometry/test/src/geometry_test.dart @@ -114,9 +114,9 @@ void main() { Vector2(0, 0), Vector2(10, 10), ], - step: 0.001, + step: 0.02, ); - expect(points.length, 1000); + expect(points.length, 50); }); }); diff --git a/packages/pinball_components/lib/src/components/spaceship.dart b/packages/pinball_components/lib/src/components/spaceship.dart index 4d84eb68..59f1037a 100644 --- a/packages/pinball_components/lib/src/components/spaceship.dart +++ b/packages/pinball_components/lib/src/components/spaceship.dart @@ -195,8 +195,8 @@ class SpaceshipHole extends RampOpening { @override Shape get shape { return ArcShape( - center: Vector2(0, 4.2), - arcRadius: 6, + center: Vector2(0, 3.2), + arcRadius: 5, angle: 1, rotation: 60 * pi / 180, ); diff --git a/test/game/components/spaceship_exit_rail_test.dart b/test/game/components/spaceship_exit_rail_test.dart index 99afc808..edd81aab 100644 --- a/test/game/components/spaceship_exit_rail_test.dart +++ b/test/game/components/spaceship_exit_rail_test.dart @@ -31,29 +31,33 @@ void main() { when(() => fixture.filterData).thenReturn(filterData); }); + // TODO(alestiago): Make ContactCallback private and use `beginContact` + // instead. group('SpaceshipExitHoleBallContactCallback', () { - test('changes the ball priority on contact', () { + setUp(() { + when(() => ball.priority).thenReturn(1); when(() => exitRailEnd.outsideLayer).thenReturn(Layer.board); + when(() => exitRailEnd.outsidePriority).thenReturn(0); + }); + test('changes the ball priority on contact', () { SpaceshipExitRailEndBallContactCallback().begin( exitRailEnd, ball, MockContact(), ); - verify(() => ball.priority = 1).called(1); + verify(() => ball.sendTo(exitRailEnd.outsidePriority)).called(1); }); - test('reorders the game children', () { - when(() => exitRailEnd.outsideLayer).thenReturn(Layer.board); - + test('changes the ball layer on contact', () { SpaceshipExitRailEndBallContactCallback().begin( exitRailEnd, ball, MockContact(), ); - verify(game.reorderChildren).called(1); + verify(() => ball.layer = exitRailEnd.outsideLayer).called(1); }); }); });