From f7f8661d81b034fa425cfa118257af0f91daba30 Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Sat, 7 May 2022 20:46:19 +0100 Subject: [PATCH] feat: implemented `ScoreComponentScalingBehavior` (#391) * feat: implemented ScoreComponentScalingBehavior * fix: adjusted score ZIndex * refactor: removed unused class * refactor: adjusted goldens path --- .../lib/src/components/components.dart | 2 +- .../score_component/behaviors/behaviors.dart | 1 + .../score_component_scaling_behavior.dart | 24 ++++++ .../score_component.dart | 17 ++++ .../lib/src/components/z_indexes.dart | 2 +- .../behaviors/ball_scaling_behavior_test.dart | 5 +- ...score_component_scaling_behavior_test.dart | 74 ++++++++++++++++ .../score_component_test.dart | 84 +++++++++++++------ 8 files changed, 180 insertions(+), 29 deletions(-) create mode 100644 packages/pinball_components/lib/src/components/score_component/behaviors/behaviors.dart create mode 100644 packages/pinball_components/lib/src/components/score_component/behaviors/score_component_scaling_behavior.dart rename packages/pinball_components/lib/src/components/{ => score_component}/score_component.dart (77%) create mode 100644 packages/pinball_components/test/src/components/score_component/behaviors/score_component_scaling_behavior_test.dart rename packages/pinball_components/test/src/components/{ => score_component}/score_component_test.dart (72%) diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index 3e8d0db5..58319bac 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -26,7 +26,7 @@ export 'multiball/multiball.dart'; export 'multiplier/multiplier.dart'; export 'plunger.dart'; export 'rocket.dart'; -export 'score_component.dart'; +export 'score_component/score_component.dart'; export 'shapes/shapes.dart'; export 'signpost/signpost.dart'; export 'skill_shot/skill_shot.dart'; diff --git a/packages/pinball_components/lib/src/components/score_component/behaviors/behaviors.dart b/packages/pinball_components/lib/src/components/score_component/behaviors/behaviors.dart new file mode 100644 index 00000000..dba073f0 --- /dev/null +++ b/packages/pinball_components/lib/src/components/score_component/behaviors/behaviors.dart @@ -0,0 +1 @@ +export 'score_component_scaling_behavior.dart'; diff --git a/packages/pinball_components/lib/src/components/score_component/behaviors/score_component_scaling_behavior.dart b/packages/pinball_components/lib/src/components/score_component/behaviors/score_component_scaling_behavior.dart new file mode 100644 index 00000000..5e3d184b --- /dev/null +++ b/packages/pinball_components/lib/src/components/score_component/behaviors/score_component_scaling_behavior.dart @@ -0,0 +1,24 @@ +import 'package:flame/components.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +/// Scales a [ScoreComponent] according to its position on the board. +class ScoreComponentScalingBehavior extends Component + with ParentIsA { + @override + void update(double dt) { + super.update(dt); + final boardHeight = BoardDimensions.bounds.height; + const maxShrinkValue = 0.83; + + final augmentedPosition = parent.position.y * 3; + final standardizedYPosition = augmentedPosition + (boardHeight / 2); + final scaleFactor = maxShrinkValue + + ((standardizedYPosition / boardHeight) * (1 - maxShrinkValue)); + + parent.scale.setValues( + scaleFactor, + scaleFactor, + ); + } +} diff --git a/packages/pinball_components/lib/src/components/score_component.dart b/packages/pinball_components/lib/src/components/score_component/score_component.dart similarity index 77% rename from packages/pinball_components/lib/src/components/score_component.dart rename to packages/pinball_components/lib/src/components/score_component/score_component.dart index 0b9940aa..4908c337 100644 --- a/packages/pinball_components/lib/src/components/score_component.dart +++ b/packages/pinball_components/lib/src/components/score_component/score_component.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'package:flame/components.dart'; import 'package:flame/effects.dart'; +import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_components/src/components/score_component/behaviors/score_component_scaling_behavior.dart'; import 'package:pinball_flame/pinball_flame.dart'; enum Points { @@ -26,10 +28,25 @@ class ScoreComponent extends SpriteComponent with HasGameRef, ZIndex { super( position: position, anchor: Anchor.center, + children: [ScoreComponentScalingBehavior()], ) { zIndex = ZIndexes.score; } + /// Creates a [ScoreComponent] without any children. + /// + /// This can be used for testing [ScoreComponent]'s behaviors in isolation. + @visibleForTesting + ScoreComponent.test({ + required this.points, + required Vector2 position, + required EffectController effectController, + }) : _effectController = effectController, + super( + position: position, + anchor: Anchor.center, + ); + late Points points; late final Effect _effect; diff --git a/packages/pinball_components/lib/src/components/z_indexes.dart b/packages/pinball_components/lib/src/components/z_indexes.dart index 2fbcc3af..0d0fab98 100644 --- a/packages/pinball_components/lib/src/components/z_indexes.dart +++ b/packages/pinball_components/lib/src/components/z_indexes.dart @@ -106,7 +106,7 @@ abstract class ZIndexes { // Score - static const score = _above + spaceshipRampForegroundRailing; + static const score = _above + sparkyAnimatronic; // Debug information diff --git a/packages/pinball_components/test/src/components/ball/behaviors/ball_scaling_behavior_test.dart b/packages/pinball_components/test/src/components/ball/behaviors/ball_scaling_behavior_test.dart index c4c66d4a..1fe84ae4 100644 --- a/packages/pinball_components/test/src/components/ball/behaviors/ball_scaling_behavior_test.dart +++ b/packages/pinball_components/test/src/components/ball/behaviors/ball_scaling_behavior_test.dart @@ -10,8 +10,9 @@ import '../../../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final asset = theme.Assets.images.dash.ball.keyName; - final flameTester = FlameTester(() => TestGame([asset])); + final flameTester = FlameTester( + () => TestGame([theme.Assets.images.dash.ball.keyName]), + ); group('BallScalingBehavior', () { test('can be instantiated', () { diff --git a/packages/pinball_components/test/src/components/score_component/behaviors/score_component_scaling_behavior_test.dart b/packages/pinball_components/test/src/components/score_component/behaviors/score_component_scaling_behavior_test.dart new file mode 100644 index 00000000..9bf0db2f --- /dev/null +++ b/packages/pinball_components/test/src/components/score_component/behaviors/score_component_scaling_behavior_test.dart @@ -0,0 +1,74 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame/components.dart'; +import 'package:flame/effects.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_components/src/components/score_component/behaviors/behaviors.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + group('ScoreComponentScalingBehavior', () { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester( + () => TestGame([ + Assets.images.score.fiveThousand.keyName, + Assets.images.score.twentyThousand.keyName, + Assets.images.score.twoHundredThousand.keyName, + Assets.images.score.oneMillion.keyName, + ]), + ); + + test('can be instantiated', () { + expect( + ScoreComponentScalingBehavior(), + isA(), + ); + }); + + flameTester.test('can be loaded', (game) async { + final parent = ScoreComponent.test( + points: Points.fiveThousand, + position: Vector2.zero(), + effectController: EffectController(duration: 1), + ); + final behavior = ScoreComponentScalingBehavior(); + await game.ensureAdd(parent); + await parent.ensureAdd(behavior); + + expect(parent.children, contains(behavior)); + }); + + flameTester.test( + 'scales the sprite', + (game) async { + final parent1 = ScoreComponent.test( + points: Points.fiveThousand, + position: Vector2(0, 10), + effectController: EffectController(duration: 1), + ); + final parent2 = ScoreComponent.test( + points: Points.fiveThousand, + position: Vector2(0, -10), + effectController: EffectController(duration: 1), + ); + await game.ensureAddAll([parent1, parent2]); + + await parent1.ensureAdd(ScoreComponentScalingBehavior()); + await parent2.ensureAdd(ScoreComponentScalingBehavior()); + game.update(1); + + expect( + parent1.scale.x, + greaterThan(parent2.scale.x), + ); + expect( + parent1.scale.y, + greaterThan(parent2.scale.y), + ); + }, + ); + }); +} diff --git a/packages/pinball_components/test/src/components/score_component_test.dart b/packages/pinball_components/test/src/components/score_component/score_component_test.dart similarity index 72% rename from packages/pinball_components/test/src/components/score_component_test.dart rename to packages/pinball_components/test/src/components/score_component/score_component_test.dart index f2bd52e3..00c08d5c 100644 --- a/packages/pinball_components/test/src/components/score_component_test.dart +++ b/packages/pinball_components/test/src/components/score_component/score_component_test.dart @@ -5,24 +5,37 @@ import 'package:flame/effects.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_components/src/components/score_component/behaviors/behaviors.dart'; -import '../../helpers/helpers.dart'; +import '../../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final assets = [ - Assets.images.score.fiveThousand.keyName, - Assets.images.score.twentyThousand.keyName, - Assets.images.score.twoHundredThousand.keyName, - Assets.images.score.oneMillion.keyName, - ]; - final flameTester = FlameTester(() => TestGame(assets)); + final flameTester = FlameTester( + () => TestGame([ + Assets.images.score.fiveThousand.keyName, + Assets.images.score.twentyThousand.keyName, + Assets.images.score.twoHundredThousand.keyName, + Assets.images.score.oneMillion.keyName, + ]), + ); group('ScoreComponent', () { + test('can be instantiated', () { + expect( + ScoreComponent( + points: Points.fiveThousand, + position: Vector2.zero(), + effectController: EffectController(duration: 1), + ), + isA(), + ); + }); + flameTester.testGameWidget( 'loads correctly', setUp: (game, tester) async { - await game.images.loadAll(assets); + await game.onLoad(); game.camera.followVector2(Vector2.zero()); await game.ensureAdd( ScoreComponent( @@ -38,13 +51,32 @@ void main() { }, ); + flameTester.test( + 'adds a ScoreComponentScalingBehavior', + (game) async { + await game.onLoad(); + game.camera.followVector2(Vector2.zero()); + final component = ScoreComponent( + points: Points.oneMillion, + position: Vector2.zero(), + effectController: EffectController(duration: 1), + ); + await game.ensureAdd(component); + + expect( + component.children.whereType().length, + equals(1), + ); + }, + ); + flameTester.testGameWidget( 'has a movement effect', setUp: (game, tester) async { - await game.images.loadAll(assets); + await game.onLoad(); game.camera.followVector2(Vector2.zero()); await game.ensureAdd( - ScoreComponent( + ScoreComponent.test( points: Points.oneMillion, position: Vector2.zero(), effectController: EffectController(duration: 1), @@ -63,10 +95,10 @@ void main() { flameTester.testGameWidget( 'is removed once finished', setUp: (game, tester) async { - await game.images.loadAll(assets); + await game.onLoad(); game.camera.followVector2(Vector2.zero()); await game.ensureAdd( - ScoreComponent( + ScoreComponent.test( points: Points.oneMillion, position: Vector2.zero(), effectController: EffectController(duration: 1), @@ -83,12 +115,14 @@ void main() { ); group('renders correctly', () { + const goldensPath = '../golden/score/'; + flameTester.testGameWidget( '5000 points', setUp: (game, tester) async { - await game.images.loadAll(assets); + await game.onLoad(); await game.ensureAdd( - ScoreComponent( + ScoreComponent.test( points: Points.fiveThousand, position: Vector2.zero(), effectController: EffectController(duration: 1), @@ -104,7 +138,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/score/5k.png'), + matchesGoldenFile('${goldensPath}5k.png'), ); }, ); @@ -112,9 +146,9 @@ void main() { flameTester.testGameWidget( '20000 points', setUp: (game, tester) async { - await game.images.loadAll(assets); + await game.onLoad(); await game.ensureAdd( - ScoreComponent( + ScoreComponent.test( points: Points.twentyThousand, position: Vector2.zero(), effectController: EffectController(duration: 1), @@ -130,7 +164,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/score/20k.png'), + matchesGoldenFile('${goldensPath}20k.png'), ); }, ); @@ -138,9 +172,9 @@ void main() { flameTester.testGameWidget( '200000 points', setUp: (game, tester) async { - await game.images.loadAll(assets); + await game.onLoad(); await game.ensureAdd( - ScoreComponent( + ScoreComponent.test( points: Points.twoHundredThousand, position: Vector2.zero(), effectController: EffectController(duration: 1), @@ -156,7 +190,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/score/200k.png'), + matchesGoldenFile('${goldensPath}200k.png'), ); }, ); @@ -164,9 +198,9 @@ void main() { flameTester.testGameWidget( '1000000 points', setUp: (game, tester) async { - await game.images.loadAll(assets); + await game.onLoad(); await game.ensureAdd( - ScoreComponent( + ScoreComponent.test( points: Points.oneMillion, position: Vector2.zero(), effectController: EffectController(duration: 1), @@ -182,7 +216,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/score/1m.png'), + matchesGoldenFile('${goldensPath}1m.png'), ); }, );