diff --git a/packages/pinball_components/fonts/PixeloidMono-1G8ae.ttf b/packages/pinball_components/fonts/PixeloidMono-1G8ae.ttf new file mode 100644 index 00000000..a797c1e1 Binary files /dev/null and b/packages/pinball_components/fonts/PixeloidMono-1G8ae.ttf differ diff --git a/packages/pinball_components/fonts/PixeloidSans-nR3g1.ttf b/packages/pinball_components/fonts/PixeloidSans-nR3g1.ttf new file mode 100644 index 00000000..2f9a03b4 Binary files /dev/null and b/packages/pinball_components/fonts/PixeloidSans-nR3g1.ttf differ diff --git a/packages/pinball_components/fonts/PixeloidSansBold-RpeJo.ttf b/packages/pinball_components/fonts/PixeloidSansBold-RpeJo.ttf new file mode 100644 index 00000000..81194f5d Binary files /dev/null and b/packages/pinball_components/fonts/PixeloidSansBold-RpeJo.ttf differ diff --git a/packages/pinball_components/lib/gen/fonts.gen.dart b/packages/pinball_components/lib/gen/fonts.gen.dart new file mode 100644 index 00000000..b15f2dd0 --- /dev/null +++ b/packages/pinball_components/lib/gen/fonts.gen.dart @@ -0,0 +1,11 @@ +/// GENERATED CODE - DO NOT MODIFY BY HAND +/// ***************************************************** +/// FlutterGen +/// ***************************************************** + +class FontFamily { + FontFamily._(); + + static const String pixeloidMono = 'PixeloidMono'; + static const String pixeloidSans = 'PixeloidSans'; +} diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index b71f7c13..2eb24508 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -17,6 +17,7 @@ export 'kicker.dart'; export 'launch_ramp.dart'; export 'layer.dart'; export 'ramp_opening.dart'; +export 'score_text_effect.dart'; export 'shapes/shapes.dart'; export 'slingshot.dart'; export 'spaceship.dart'; diff --git a/packages/pinball_components/lib/src/components/score_text_effect.dart b/packages/pinball_components/lib/src/components/score_text_effect.dart new file mode 100644 index 00000000..91b99c61 --- /dev/null +++ b/packages/pinball_components/lib/src/components/score_text_effect.dart @@ -0,0 +1,58 @@ +import 'dart:async'; + +import 'package:flame/components.dart'; +import 'package:flame/effects.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball_components/gen/fonts.gen.dart'; + +/// {@template score_text_effect} +/// A [TextComponent] that spawns at a given [position] +/// bundles a simples translate effect and is removed +/// once its animation is completed +/// {@endtemplate} +class ScoreTextEffect extends TextComponent { + + /// {@macro score_text_effect} + ScoreTextEffect({ + required String text, + required Vector2 position, + this.color = Colors.black, + }) : super( + text: text, + position: position, + anchor: Anchor.center, + priority: 100, + ); + late final Effect _effect; + /// The [text] [Color] + final Color color; + + @override + Future onLoad() async { + textRenderer = TextPaint( + style: TextStyle( + fontFamily: 'packages/pinball_components/${FontFamily.pixeloidMono}', + color: color, + fontSize: 4, + ), + ); + + unawaited( + add( + _effect = MoveEffect.by( + Vector2(0, -5), + EffectController(duration: 1), + ), + ), + ); + } + + @override + void update(double dt) { + super.update(dt); + + if (_effect.controller.completed) { + removeFromParent(); + } + } +} diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index b93f98a2..643caf01 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: flame_forge2d: ^0.10.0 flutter: sdk: flutter - geometry: + geometry: path: ../geometry @@ -24,6 +24,16 @@ dev_dependencies: flutter: generate: true + fonts: + - family: PixeloidSans + fonts: + - asset: fonts/PixeloidSans-nR3g1.ttf + - asset: fonts/PixeloidSansBold-RpeJo.ttf + weight: 700 + - family: PixeloidMono + fonts: + - asset: fonts/PixeloidMono-1G8ae.ttf + assets: - assets/images/ - assets/images/baseboard/ diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index b3b331a7..3c7d26e6 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -25,5 +25,6 @@ void main() { addSparkyBumperStories(dashbook); addZoomStories(dashbook); addBoundariesStories(dashbook); + addScoreTextEffectStories(dashbook); runApp(dashbook); } diff --git a/packages/pinball_components/sandbox/lib/stories/score_text_effect/basic.dart b/packages/pinball_components/sandbox/lib/stories/score_text_effect/basic.dart new file mode 100644 index 00000000..eba263fa --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/score_text_effect/basic.dart @@ -0,0 +1,31 @@ +import 'dart:math'; + +import 'package:flame/input.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/common/common.dart'; + +class ScoreTextEffectBasicGame extends BasicGame with TapDetector { + static const info = ''' + Simple game to show how score text effects works, + simply tap on the screen to spawn an effect on the given location. + '''; + + final rng = Random(); + + @override + Future onLoad() async { + camera.followVector2(Vector2.zero()); + } + + @override + void onTapUp(TapUpInfo info) { + add( + ScoreTextEffect( + text: rng.nextInt(100000).toString(), + color: Colors.white, + position: info.eventPosition.game..multiply(Vector2(1, -1)), + ), + ); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/score_text_effect/stories.dart b/packages/pinball_components/sandbox/lib/stories/score_text_effect/stories.dart new file mode 100644 index 00000000..cff161d5 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/score_text_effect/stories.dart @@ -0,0 +1,15 @@ +import 'package:dashbook/dashbook.dart'; +import 'package:flame/game.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/score_text_effect/basic.dart'; + +void addScoreTextEffectStories(Dashbook dashbook) { + dashbook.storiesOf('ScoreTextEffect').add( + 'Basic', + (context) => GameWidget( + game: ScoreTextEffectBasicGame(), + ), + codeLink: buildSourceLink('score_text_effect/basic.dart'), + info: ScoreTextEffectBasicGame.info, + ); +} diff --git a/packages/pinball_components/sandbox/lib/stories/stories.dart b/packages/pinball_components/sandbox/lib/stories/stories.dart index 009f53ac..69422d79 100644 --- a/packages/pinball_components/sandbox/lib/stories/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/stories.dart @@ -6,6 +6,7 @@ export 'effects/stories.dart'; export 'flipper/stories.dart'; export 'flutter_forest/stories.dart'; export 'layer/stories.dart'; +export 'score_text_effect/stories.dart'; export 'slingshot/stories.dart'; export 'spaceship/stories.dart'; export 'sparky_bumper/stories.dart'; diff --git a/packages/pinball_components/test/src/components/golden/score_text_effect/movement.png b/packages/pinball_components/test/src/components/golden/score_text_effect/movement.png new file mode 100644 index 00000000..32bae948 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/score_text_effect/movement.png differ diff --git a/packages/pinball_components/test/src/components/golden/score_text_effect/render.png b/packages/pinball_components/test/src/components/golden/score_text_effect/render.png new file mode 100644 index 00000000..db56ebba Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/score_text_effect/render.png differ diff --git a/packages/pinball_components/test/src/components/score_text_effects_test.dart b/packages/pinball_components/test/src/components/score_text_effects_test.dart new file mode 100644 index 00000000..169437d1 --- /dev/null +++ b/packages/pinball_components/test/src/components/score_text_effects_test.dart @@ -0,0 +1,78 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame/components.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('ScoreTextEffect', () { + final flameTester = FlameTester(TestGame.new); + + flameTester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + game.camera.followVector2(Vector2.zero()); + await game.ensureAdd( + ScoreTextEffect( + text: '123', + position: Vector2.zero(), + color: Colors.white, + ), + ); + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/score_text_effect/render.png'), + ); + }, + ); + + flameTester.testGameWidget( + 'has a movement effect', + setUp: (game, tester) async { + game.camera.followVector2(Vector2.zero()); + await game.ensureAdd( + ScoreTextEffect( + text: '123', + position: Vector2.zero(), + color: Colors.white, + ), + ); + + game.update(0.5); + await tester.pump(); + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/score_text_effect/movement.png'), + ); + }, + ); + + flameTester.testGameWidget( + 'is removed once finished', + setUp: (game, tester) async { + game.camera.followVector2(Vector2.zero()); + await game.ensureAdd( + ScoreTextEffect( + text: '123', + position: Vector2.zero(), + color: Colors.white, + ), + ); + + game.update(1); + game.update(0); // Ensure all component removals + }, + verify: (game, tester) async { + expect(game.children.length, equals(0)); + }, + ); + }); +}