diff --git a/.github/workflows/pinball_components.yaml b/.github/workflows/pinball_components.yaml index bf1907f8..e4154059 100644 --- a/.github/workflows/pinball_components.yaml +++ b/.github/workflows/pinball_components.yaml @@ -13,7 +13,7 @@ on: jobs: build: - uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@b075749771679a5baa4c90d36ad2e8580bbf273b with: working_directory: packages/pinball_components coverage_excludes: "lib/gen/*.dart" diff --git a/packages/pinball_components/lib/src/components/camera_zoom.dart b/packages/pinball_components/lib/src/components/camera_zoom.dart new file mode 100644 index 00000000..a3da382e --- /dev/null +++ b/packages/pinball_components/lib/src/components/camera_zoom.dart @@ -0,0 +1,56 @@ +import 'dart:async'; + +import 'package:flame/components.dart'; +import 'package:flame/effects.dart'; +import 'package:flutter/material.dart'; + +/// {@template camera_zoom} +/// Applies zoom to the camera of the game where this is added to +/// {@endtemplate} +class CameraZoom extends Effect with HasGameRef { + /// {@macro camera_zoom} + CameraZoom({ + required this.value, + }) : super( + EffectController( + duration: 0.4, + curve: Curves.easeOut, + ), + ); + + /// The total zoom value to be applied to the camera + final double value; + + late final Tween _tween; + + final Completer _completer = Completer(); + + @override + Future onLoad() async { + _tween = Tween( + begin: gameRef.camera.zoom, + end: value, + ); + } + + @override + void apply(double progress) { + gameRef.camera.zoom = _tween.transform(progress); + } + + /// Returns a [Future] that completes once the zoom is finished + Future get completed { + if (controller.completed) { + return Future.value(); + } + + return _completer.future; + } + + @override + void onRemove() { + _completer.complete(); + + super.onRemove(); + } +} diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index f9fd98ab..c8020fc4 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -3,6 +3,7 @@ export 'baseboard.dart'; export 'board_dimensions.dart'; export 'board_side.dart'; export 'boundaries.dart'; +export 'camera_zoom.dart'; export 'chrome_dino.dart'; export 'dash_nest_bumper.dart'; export 'dino_walls.dart'; diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index 1b99c18b..db0bd547 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -24,5 +24,6 @@ void main() { addPlungerStories(dashbook); addSlingshotStories(dashbook); addSparkyBumperStories(dashbook); + addZoomStories(dashbook); runApp(dashbook); } diff --git a/packages/pinball_components/sandbox/lib/stories/stories.dart b/packages/pinball_components/sandbox/lib/stories/stories.dart index 19dd0b0a..52be4327 100644 --- a/packages/pinball_components/sandbox/lib/stories/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/stories.dart @@ -9,3 +9,4 @@ export 'plunger/stories.dart'; export 'slingshot/stories.dart'; export 'spaceship/stories.dart'; export 'sparky_bumper/stories.dart'; +export 'zoom/stories.dart'; diff --git a/packages/pinball_components/sandbox/lib/stories/zoom/basic_zoom_game.dart b/packages/pinball_components/sandbox/lib/stories/zoom/basic_zoom_game.dart new file mode 100644 index 00000000..276dd39c --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/zoom/basic_zoom_game.dart @@ -0,0 +1,37 @@ +import 'package:flame/components.dart'; +import 'package:flame/input.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/common/common.dart'; + +class BasicCameraZoomGame extends BasicGame with TapDetector { + static const info = ''' + Simple game to demonstrate how the CameraZoom can be used. + Tap to zoom in/out + '''; + + bool zoomApplied = false; + + @override + Future onLoad() async { + final sprite = await loadSprite(Assets.images.flutterSignPost.keyName); + + await add( + SpriteComponent( + sprite: sprite, + size: Vector2(4, 8), + anchor: Anchor.center, + ), + ); + + camera.followVector2(Vector2.zero()); + } + + @override + void onTap() { + if (firstChild() == null) { + final zoom = CameraZoom(value: zoomApplied ? 30 : 10); + add(zoom); + zoomApplied = !zoomApplied; + } + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/zoom/stories.dart b/packages/pinball_components/sandbox/lib/stories/zoom/stories.dart new file mode 100644 index 00000000..653d5491 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/zoom/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/zoom/basic_zoom_game.dart'; + +void addZoomStories(Dashbook dashbook) { + dashbook.storiesOf('CameraZoom').add( + 'Basic', + (context) => GameWidget( + game: BasicCameraZoomGame(), + ), + codeLink: buildSourceLink('zoom/basic_zoom_game.dart'), + info: BasicCameraZoomGame.info, + ); +} diff --git a/packages/pinball_components/test/src/components/camera_zoom_test.dart b/packages/pinball_components/test/src/components/camera_zoom_test.dart new file mode 100644 index 00000000..00f43847 --- /dev/null +++ b/packages/pinball_components/test/src/components/camera_zoom_test.dart @@ -0,0 +1,85 @@ +// 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() { + group('CameraZoom', () { + final tester = FlameTester(TestGame.new); + + tester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + game.camera.followVector2(Vector2.zero()); + game.camera.zoom = 10; + final sprite = await game.loadSprite( + Assets.images.flutterSignPost.keyName, + ); + + await game.add( + SpriteComponent( + sprite: sprite, + size: Vector2(4, 8), + anchor: Anchor.center, + ), + ); + + await game.add(CameraZoom(value: 40)); + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/camera_zoom/no_zoom.png'), + ); + + game.update(0.2); + await tester.pump(); + await expectLater( + find.byGame(), + matchesGoldenFile('golden/camera_zoom/in_between.png'), + ); + + game.update(0.4); + await tester.pump(); + await expectLater( + find.byGame(), + matchesGoldenFile('golden/camera_zoom/finished.png'), + ); + game.update(0.1); + await tester.pump(); + + expect(game.firstChild(), isNull); + }, + ); + + tester.test( + 'completes when checked after it is finished', + (game) async { + await game.add(CameraZoom(value: 40)); + game.update(10); + final cameraZoom = game.firstChild(); + final future = cameraZoom!.completed; + + expect(future, completes); + }, + ); + + tester.test( + 'completes when checked before it is finished', + (game) async { + final zoom = CameraZoom(value: 40); + final future = zoom.completed; + + await game.add(zoom); + game.update(10); + game.update(0); + + expect(future, completes); + }, + ); + }); +} diff --git a/packages/pinball_components/test/src/components/golden/camera_zoom/finished.png b/packages/pinball_components/test/src/components/golden/camera_zoom/finished.png new file mode 100644 index 00000000..be784ada Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/camera_zoom/finished.png differ diff --git a/packages/pinball_components/test/src/components/golden/camera_zoom/in_between.png b/packages/pinball_components/test/src/components/golden/camera_zoom/in_between.png new file mode 100644 index 00000000..3809f0d0 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/camera_zoom/in_between.png differ diff --git a/packages/pinball_components/test/src/components/golden/camera_zoom/no_zoom.png b/packages/pinball_components/test/src/components/golden/camera_zoom/no_zoom.png new file mode 100644 index 00000000..a6215d65 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/camera_zoom/no_zoom.png differ