diff --git a/lib/app/app.dart b/lib/app/app.dart index 2b135918..f23ab3c8 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -1,8 +1 @@ -// Copyright (c) 2021, Very Good Ventures -// https://verygood.ventures -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - export 'view/app.dart'; diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 528954a6..d778b55b 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -1,10 +1,3 @@ -// Copyright (c) 2021, Very Good Ventures -// https://verygood.ventures -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - // ignore_for_file: public_member_api_docs import 'package:authentication_repository/authentication_repository.dart'; diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index bbd87f0c..c5e42951 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -1,10 +1,3 @@ -// Copyright (c) 2021, Very Good Ventures -// https://verygood.ventures -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - // ignore_for_file: public_member_api_docs import 'dart:async'; diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index f9018ee5..bd29e4e8 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -5,7 +5,6 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flame/input.dart'; import 'package:flame_bloc/flame_bloc.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:pinball/game/game.dart'; @@ -14,7 +13,7 @@ import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart'; -class PinballGame extends Forge2DGame +class PinballGame extends PinballForge2DGame with FlameBloc, HasKeyboardHandlerComponents, diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index 548a81a6..0945f30f 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -1,10 +1,3 @@ -// Copyright (c) 2021, Very Good Ventures -// https://verygood.ventures -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - // ignore_for_file: public_member_api_docs import 'package:flutter/widgets.dart'; diff --git a/lib/main_development.dart b/lib/main_development.dart index 529c66e2..21166057 100644 --- a/lib/main_development.dart +++ b/lib/main_development.dart @@ -1,10 +1,3 @@ -// Copyright (c) 2021, Very Good Ventures -// https://verygood.ventures -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - import 'dart:async'; import 'package:authentication_repository/authentication_repository.dart'; diff --git a/lib/main_production.dart b/lib/main_production.dart index 529c66e2..21166057 100644 --- a/lib/main_production.dart +++ b/lib/main_production.dart @@ -1,10 +1,3 @@ -// Copyright (c) 2021, Very Good Ventures -// https://verygood.ventures -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - import 'dart:async'; import 'package:authentication_repository/authentication_repository.dart'; diff --git a/lib/main_staging.dart b/lib/main_staging.dart index 529c66e2..21166057 100644 --- a/lib/main_staging.dart +++ b/lib/main_staging.dart @@ -1,10 +1,3 @@ -// Copyright (c) 2021, Very Good Ventures -// https://verygood.ventures -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - import 'dart:async'; import 'package:authentication_repository/authentication_repository.dart'; diff --git a/packages/pinball_components/lib/src/components/ball.dart b/packages/pinball_components/lib/src/components/ball/ball.dart similarity index 73% rename from packages/pinball_components/lib/src/components/ball.dart rename to packages/pinball_components/lib/src/components/ball/ball.dart index 7469396a..12b1c877 100644 --- a/packages/pinball_components/lib/src/components/ball.dart +++ b/packages/pinball_components/lib/src/components/ball/ball.dart @@ -1,18 +1,18 @@ import 'dart:async'; import 'dart:math' as math; -import 'dart:ui'; import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/widgets.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_components/src/components/ball/behaviors/ball_gravitating_behavior.dart'; +import 'package:pinball_components/src/components/ball/behaviors/ball_scaling_behavior.dart'; import 'package:pinball_flame/pinball_flame.dart'; /// {@template ball} /// A solid, [BodyType.dynamic] sphere that rolls and bounces around. /// {@endtemplate} -class Ball extends BodyComponent - with Layered, InitialPosition, ZIndex { +class Ball extends BodyComponent with Layered, InitialPosition, ZIndex { /// {@macro ball} Ball({ required this.baseColor, @@ -20,6 +20,8 @@ class Ball extends BodyComponent renderBody: false, children: [ _BallSpriteComponent()..tint(baseColor.withOpacity(0.5)), + BallScalingBehavior(), + BallGravitatingBehavior(), ], ) { // TODO(ruimiguel): while developing Ball can be launched by clicking mouse, @@ -30,6 +32,15 @@ class Ball extends BodyComponent layer = Layer.board; } + /// Creates a [Ball] without any behaviors. + /// + /// This can be used for testing [Ball]'s behaviors in isolation. + @visibleForTesting + Ball.test({required this.baseColor}) + : super( + children: [_BallSpriteComponent()], + ); + /// The size of the [Ball]. static final Vector2 size = Vector2.all(4.13); @@ -76,48 +87,6 @@ class Ball extends BodyComponent body.linearVelocity = impulse; await add(_TurboChargeSpriteAnimationComponent()); } - - @override - void update(double dt) { - super.update(dt); - - _rescaleSize(); - _setPositionalGravity(); - } - - void _rescaleSize() { - final boardHeight = BoardDimensions.bounds.height; - const maxShrinkValue = BoardDimensions.perspectiveShrinkFactor; - - final standardizedYPosition = body.position.y + (boardHeight / 2); - - final scaleFactor = maxShrinkValue + - ((standardizedYPosition / boardHeight) * (1 - maxShrinkValue)); - - body.fixtures.first.shape.radius = (size.x / 2) * scaleFactor; - - // TODO(alestiago): Revisit and see if there's a better way to do this. - final spriteComponent = firstChild<_BallSpriteComponent>(); - spriteComponent?.scale = Vector2.all(scaleFactor); - } - - void _setPositionalGravity() { - final defaultGravity = gameRef.world.gravity.y; - final maxXDeviationFromCenter = BoardDimensions.bounds.width / 2; - const maxXGravityPercentage = - (1 - BoardDimensions.perspectiveShrinkFactor) / 2; - final xDeviationFromCenter = body.position.x; - - final positionalXForce = ((xDeviationFromCenter / maxXDeviationFromCenter) * - maxXGravityPercentage) * - defaultGravity; - - final positionalYForce = math.sqrt( - math.pow(defaultGravity, 2) - math.pow(positionalXForce, 2), - ); - - body.gravityOverride = Vector2(positionalXForce, positionalYForce); - } } class _BallSpriteComponent extends SpriteComponent with HasGameRef { diff --git a/packages/pinball_components/lib/src/components/ball/behaviors/ball_gravitating_behavior.dart b/packages/pinball_components/lib/src/components/ball/behaviors/ball_gravitating_behavior.dart new file mode 100644 index 00000000..bad129a6 --- /dev/null +++ b/packages/pinball_components/lib/src/components/ball/behaviors/ball_gravitating_behavior.dart @@ -0,0 +1,35 @@ +import 'dart:math' as math; + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +/// Scales the ball's gravity according to its position on the board. +class BallGravitatingBehavior extends Component + with ParentIsA, HasGameRef { + @override + void update(double dt) { + super.update(dt); + final defaultGravity = gameRef.world.gravity.y; + + final maxXDeviationFromCenter = BoardDimensions.bounds.width / 2; + const maxXGravityPercentage = + (1 - BoardDimensions.perspectiveShrinkFactor) / 2; + final xDeviationFromCenter = parent.body.position.x; + + final positionalXForce = ((xDeviationFromCenter / maxXDeviationFromCenter) * + maxXGravityPercentage) * + defaultGravity; + final positionalYForce = math.sqrt( + math.pow(defaultGravity, 2) - math.pow(positionalXForce, 2), + ); + + final gravityOverride = parent.body.gravityOverride; + if (gravityOverride != null) { + gravityOverride.setValues(positionalXForce, positionalYForce); + } else { + parent.body.gravityOverride = Vector2(positionalXForce, positionalYForce); + } + } +} diff --git a/packages/pinball_components/lib/src/components/ball/behaviors/ball_scaling_behavior.dart b/packages/pinball_components/lib/src/components/ball/behaviors/ball_scaling_behavior.dart new file mode 100644 index 00000000..7fc06fb1 --- /dev/null +++ b/packages/pinball_components/lib/src/components/ball/behaviors/ball_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 the ball's body and sprite according to its position on the board. +class BallScalingBehavior extends Component with ParentIsA { + @override + void update(double dt) { + super.update(dt); + final boardHeight = BoardDimensions.bounds.height; + const maxShrinkValue = BoardDimensions.perspectiveShrinkFactor; + + final standardizedYPosition = parent.body.position.y + (boardHeight / 2); + final scaleFactor = maxShrinkValue + + ((standardizedYPosition / boardHeight) * (1 - maxShrinkValue)); + + parent.body.fixtures.first.shape.radius = (Ball.size.x / 2) * scaleFactor; + + parent.firstChild()!.scale.setValues( + scaleFactor, + scaleFactor, + ); + } +} diff --git a/packages/pinball_components/lib/src/components/ball/behaviors/behaviors.dart b/packages/pinball_components/lib/src/components/ball/behaviors/behaviors.dart new file mode 100644 index 00000000..038b7833 --- /dev/null +++ b/packages/pinball_components/lib/src/components/ball/behaviors/behaviors.dart @@ -0,0 +1,2 @@ +export 'ball_gravitating_behavior.dart'; +export 'ball_scaling_behavior.dart'; diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index 82eadf0c..d3d4253b 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -2,7 +2,7 @@ export 'android_animatronic.dart'; export 'android_bumper/android_bumper.dart'; export 'android_spaceship/android_spaceship.dart'; export 'backboard/backboard.dart'; -export 'ball.dart'; +export 'ball/ball.dart'; export 'baseboard.dart'; export 'board_background_sprite_component.dart'; export 'board_dimensions.dart'; diff --git a/packages/pinball_components/lib/src/components/initial_position.dart b/packages/pinball_components/lib/src/components/initial_position.dart index d79f8d64..4265a3a7 100644 --- a/packages/pinball_components/lib/src/components/initial_position.dart +++ b/packages/pinball_components/lib/src/components/initial_position.dart @@ -5,7 +5,7 @@ import 'package:flame_forge2d/flame_forge2d.dart'; /// /// Note: If the [initialPosition] is set after the [BodyComponent] has been /// loaded it will have no effect; defaulting to [Vector2.zero]. -mixin InitialPosition on BodyComponent { +mixin InitialPosition on BodyComponent { final Vector2 _initialPosition = Vector2.zero(); set initialPosition(Vector2 value) { diff --git a/packages/pinball_components/lib/src/components/layer.dart b/packages/pinball_components/lib/src/components/layer.dart index a39ad837..8418fac1 100644 --- a/packages/pinball_components/lib/src/components/layer.dart +++ b/packages/pinball_components/lib/src/components/layer.dart @@ -9,7 +9,7 @@ import 'package:flutter/material.dart'; /// ignoring others. This compatibility depends on bit masking operation /// between layers. For more information read: https://en.wikipedia.org/wiki/Mask_(computing). /// {@endtemplate} -mixin Layered on BodyComponent { +mixin Layered on BodyComponent { Layer _layer = Layer.all; /// {@macro layered} diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index 9bce5632..cb268b41 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -1,9 +1,3 @@ -// Copyright (c) 2022, Very Good Ventures -// https://verygood.ventures -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. import 'package:dashbook/dashbook.dart'; import 'package:flutter/material.dart'; import 'package:sandbox/stories/stories.dart'; diff --git a/packages/pinball_components/test/src/components/ball_test.dart b/packages/pinball_components/test/src/components/ball/ball_test.dart similarity index 75% rename from packages/pinball_components/test/src/components/ball_test.dart rename to packages/pinball_components/test/src/components/ball/ball_test.dart index 26a03886..02175f16 100644 --- a/packages/pinball_components/test/src/components/ball_test.dart +++ b/packages/pinball_components/test/src/components/ball/ball_test.dart @@ -6,18 +6,29 @@ 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 'package:pinball_components/src/components/ball/behaviors/behaviors.dart'; -import '../../helpers/helpers.dart'; +import '../../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(TestGame.new); group('Ball', () { + const baseColor = Color(0xFFFFFFFF); + + test( + 'can be instantiated', + () { + expect(Ball(baseColor: baseColor), isA()); + expect(Ball.test(baseColor: baseColor), isA()); + }, + ); + flameTester.test( 'loads correctly', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ready(); await game.ensureAdd(ball); @@ -25,11 +36,31 @@ void main() { }, ); + group('adds', () { + flameTester.test('a BallScalingBehavior', (game) async { + final ball = Ball(baseColor: baseColor); + await game.ensureAdd(ball); + expect( + ball.descendants().whereType().length, + equals(1), + ); + }); + + flameTester.test('a BallGravitatingBehavior', (game) async { + final ball = Ball(baseColor: baseColor); + await game.ensureAdd(ball); + expect( + ball.descendants().whereType().length, + equals(1), + ); + }); + }); + group('body', () { flameTester.test( 'is dynamic', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); expect(ball.body.bodyType, equals(BodyType.dynamic)); @@ -38,7 +69,7 @@ void main() { group('can be moved', () { flameTester.test('by its weight', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); game.update(1); @@ -46,7 +77,7 @@ void main() { }); flameTester.test('by applying velocity', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); ball.body.gravityScale = Vector2.zero(); @@ -61,7 +92,7 @@ void main() { flameTester.test( 'exists', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); expect(ball.body.fixtures[0], isA()); @@ -71,7 +102,7 @@ void main() { flameTester.test( 'is dense', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); final fixture = ball.body.fixtures[0]; @@ -82,7 +113,7 @@ void main() { flameTester.test( 'shape is circular', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); final fixture = ball.body.fixtures[0]; @@ -94,7 +125,7 @@ void main() { flameTester.test( 'has Layer.all as default filter maskBits', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ready(); await game.ensureAdd(ball); await game.ready(); @@ -108,7 +139,7 @@ void main() { group('stop', () { group("can't be moved", () { flameTester.test('by its weight', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); ball.stop(); @@ -116,19 +147,6 @@ void main() { expect(ball.body.position, equals(ball.initialPosition)); }); }); - - // TODO(allisonryan0002): delete or retest this if/when solution is added - // to prevent forces on a ball while stopped. - - // flameTester.test('by applying velocity', (game) async { - // final ball = Ball(baseColor: Colors.blue); - // await game.ensureAdd(ball); - // ball.stop(); - - // ball.body.linearVelocity.setValues(10, 10); - // game.update(1); - // expect(ball.body.position, equals(ball.initialPosition)); - // }); }); group('resume', () { @@ -136,7 +154,7 @@ void main() { flameTester.test( 'by its weight when previously stopped', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); ball.stop(); ball.resume(); @@ -149,7 +167,7 @@ void main() { flameTester.test( 'by applying velocity when previously stopped', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); ball.stop(); ball.resume(); @@ -165,7 +183,7 @@ void main() { group('boost', () { flameTester.test('applies an impulse to the ball', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); expect(ball.body.linearVelocity, equals(Vector2.zero())); @@ -176,7 +194,7 @@ void main() { }); flameTester.test('adds TurboChargeSpriteAnimation', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); await ball.boost(Vector2.all(10)); @@ -190,7 +208,7 @@ void main() { flameTester.test('removes TurboChargeSpriteAnimation after it finishes', (game) async { - final ball = Ball(baseColor: Colors.blue); + final ball = Ball(baseColor: baseColor); await game.ensureAdd(ball); await ball.boost(Vector2.all(10)); diff --git a/packages/pinball_components/test/src/components/ball/behaviors/ball_gravitating_behavior_test.dart b/packages/pinball_components/test/src/components/ball/behaviors/ball_gravitating_behavior_test.dart new file mode 100644 index 00000000..de291f21 --- /dev/null +++ b/packages/pinball_components/test/src/components/ball/behaviors/ball_gravitating_behavior_test.dart @@ -0,0 +1,63 @@ +// 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:pinball_components/pinball_components.dart'; +import 'package:pinball_components/src/components/ball/behaviors/behaviors.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final asset = Assets.images.ball.ball.keyName; + final flameTester = FlameTester(() => TestGame([asset])); + + group('BallGravitatingBehavior', () { + const baseColor = Color(0xFFFFFFFF); + test('can be instantiated', () { + expect( + BallGravitatingBehavior(), + isA(), + ); + }); + + flameTester.test('can be loaded', (game) async { + final ball = Ball.test(baseColor: baseColor); + final behavior = BallGravitatingBehavior(); + await ball.add(behavior); + await game.ensureAdd(ball); + expect( + ball.firstChild(), + equals(behavior), + ); + }); + + flameTester.test( + "overrides the body's horizontal gravity symmetrically", + (game) async { + final ball1 = Ball.test(baseColor: baseColor) + ..initialPosition = Vector2(10, 0); + await ball1.add(BallGravitatingBehavior()); + + final ball2 = Ball.test(baseColor: baseColor) + ..initialPosition = Vector2(-10, 0); + await ball2.add(BallGravitatingBehavior()); + + await game.ensureAddAll([ball1, ball2]); + game.update(1); + + expect( + ball1.body.gravityOverride!.x, + equals(-ball2.body.gravityOverride!.x), + ); + expect( + ball1.body.gravityOverride!.y, + equals(ball2.body.gravityOverride!.y), + ); + }, + ); + }); +} 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 new file mode 100644 index 00000000..cd0a0486 --- /dev/null +++ b/packages/pinball_components/test/src/components/ball/behaviors/ball_scaling_behavior_test.dart @@ -0,0 +1,85 @@ +// 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:pinball_components/pinball_components.dart'; +import 'package:pinball_components/src/components/ball/behaviors/behaviors.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final asset = Assets.images.ball.ball.keyName; + final flameTester = FlameTester(() => TestGame([asset])); + + group('BallScalingBehavior', () { + const baseColor = Color(0xFFFFFFFF); + test('can be instantiated', () { + expect( + BallScalingBehavior(), + isA(), + ); + }); + + flameTester.test('can be loaded', (game) async { + final ball = Ball.test(baseColor: baseColor); + final behavior = BallScalingBehavior(); + await ball.add(behavior); + await game.ensureAdd(ball); + expect( + ball.firstChild(), + equals(behavior), + ); + }); + + flameTester.test('scales the shape radius', (game) async { + final ball1 = Ball.test(baseColor: baseColor) + ..initialPosition = Vector2(0, 10); + await ball1.add(BallScalingBehavior()); + + final ball2 = Ball.test(baseColor: baseColor) + ..initialPosition = Vector2(0, -10); + await ball2.add(BallScalingBehavior()); + + await game.ensureAddAll([ball1, ball2]); + game.update(1); + + final shape1 = ball1.body.fixtures.first.shape; + final shape2 = ball2.body.fixtures.first.shape; + expect( + shape1.radius, + greaterThan(shape2.radius), + ); + }); + + flameTester.test( + 'scales the sprite', + (game) async { + final ball1 = Ball.test(baseColor: baseColor) + ..initialPosition = Vector2(0, 10); + await ball1.add(BallScalingBehavior()); + + final ball2 = Ball.test(baseColor: baseColor) + ..initialPosition = Vector2(0, -10); + await ball2.add(BallScalingBehavior()); + + await game.ensureAddAll([ball1, ball2]); + game.update(1); + + final sprite1 = ball1.firstChild()!; + final sprite2 = ball2.firstChild()!; + expect( + sprite1.scale.x, + greaterThan(sprite2.scale.x), + ); + expect( + sprite1.scale.y, + greaterThan(sprite2.scale.y), + ); + }, + ); + }); +} diff --git a/packages/pinball_flame/lib/pinball_flame.dart b/packages/pinball_flame/lib/pinball_flame.dart index 66d34b14..8d458574 100644 --- a/packages/pinball_flame/lib/pinball_flame.dart +++ b/packages/pinball_flame/lib/pinball_flame.dart @@ -4,5 +4,6 @@ export 'src/component_controller.dart'; export 'src/contact_behavior.dart'; export 'src/keyboard_input_controller.dart'; export 'src/parent_is_a.dart'; +export 'src/pinball_forge2d_game.dart'; export 'src/sprite_animation.dart'; export 'src/z_canvas_component.dart'; diff --git a/packages/pinball_flame/lib/src/pinball_forge2d_game.dart b/packages/pinball_flame/lib/src/pinball_forge2d_game.dart new file mode 100644 index 00000000..118baad9 --- /dev/null +++ b/packages/pinball_flame/lib/src/pinball_forge2d_game.dart @@ -0,0 +1,44 @@ +import 'dart:math'; + +import 'package:flame/game.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_forge2d/world_contact_listener.dart'; + +// NOTE(wolfen): This should be removed when https://github.com/flame-engine/flame/pull/1597 is solved. +/// {@template pinball_forge2d_game} +/// A [Game] that uses the Forge2D physics engine. +/// {@endtemplate} +class PinballForge2DGame extends FlameGame implements Forge2DGame { + /// {@macro pinball_forge2d_game} + PinballForge2DGame({ + required Vector2 gravity, + }) : world = World(gravity), + super(camera: Camera()) { + camera.zoom = Forge2DGame.defaultZoom; + world.setContactListener(WorldContactListener()); + } + + @override + final World world; + + @override + void update(double dt) { + super.update(dt); + world.stepDt(min(dt, 1 / 60)); + } + + @override + Vector2 screenToFlameWorld(Vector2 position) { + throw UnimplementedError(); + } + + @override + Vector2 screenToWorld(Vector2 position) { + throw UnimplementedError(); + } + + @override + Vector2 worldToScreen(Vector2 position) { + throw UnimplementedError(); + } +} diff --git a/packages/pinball_flame/test/src/pinball_forge2d_game_test.dart b/packages/pinball_flame/test/src/pinball_forge2d_game_test.dart new file mode 100644 index 00000000..872f8b97 --- /dev/null +++ b/packages/pinball_flame/test/src/pinball_forge2d_game_test.dart @@ -0,0 +1,51 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame/extensions.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +void main() { + final flameTester = FlameTester( + () => PinballForge2DGame(gravity: Vector2.zero()), + ); + + group('PinballForge2DGame', () { + test('can instantiate', () { + expect( + () => PinballForge2DGame(gravity: Vector2.zero()), + returnsNormally, + ); + }); + + flameTester.test( + 'screenToFlameWorld throws UnimpelementedError', + (game) async { + expect( + () => game.screenToFlameWorld(Vector2.zero()), + throwsUnimplementedError, + ); + }, + ); + + flameTester.test( + 'screenToWorld throws UnimpelementedError', + (game) async { + expect( + () => game.screenToWorld(Vector2.zero()), + throwsUnimplementedError, + ); + }, + ); + + flameTester.test( + 'worldToScreen throws UnimpelementedError', + (game) async { + expect( + () => game.worldToScreen(Vector2.zero()), + throwsUnimplementedError, + ); + }, + ); + }); +} diff --git a/test/app/view/app_test.dart b/test/app/view/app_test.dart index 5ba5aca7..ca1cedff 100644 --- a/test/app/view/app_test.dart +++ b/test/app/view/app_test.dart @@ -1,10 +1,3 @@ -// Copyright (c) 2021, Very Good Ventures -// https://verygood.ventures -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - import 'package:authentication_repository/authentication_repository.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; diff --git a/test/game/components/controlled_ball_test.dart b/test/game/components/controlled_ball_test.dart index 17178e87..cfb3e157 100644 --- a/test/game/components/controlled_ball_test.dart +++ b/test/game/components/controlled_ball_test.dart @@ -2,7 +2,6 @@ import 'package:bloc_test/bloc_test.dart'; import 'package:flame/extensions.dart'; -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'; @@ -15,7 +14,7 @@ import '../../helpers/helpers.dart'; // TODO(allisonryan0002): remove once // https://github.com/flame-engine/flame/pull/1520 is merged class _WrappedBallController extends BallController { - _WrappedBallController(Ball ball, this._gameRef) : super(ball); + _WrappedBallController(Ball ball, this._gameRef) : super(ball); final PinballGame _gameRef; diff --git a/test/helpers/helpers.dart b/test/helpers/helpers.dart index 50bb9bc1..febf8d36 100644 --- a/test/helpers/helpers.dart +++ b/test/helpers/helpers.dart @@ -1,9 +1,3 @@ -// -// Copyright (c) 2021, Very Good Ventures -// Use of this source code is governed by an MIT-style -// https://opensource.org/licenses/MIT. -// https://verygood.ventures -// license that can be found in the LICENSE file or at export 'builders.dart'; export 'fakes.dart'; export 'forge2d.dart'; diff --git a/test/helpers/pump_app.dart b/test/helpers/pump_app.dart index 8c852f4e..a7d7ae67 100644 --- a/test/helpers/pump_app.dart +++ b/test/helpers/pump_app.dart @@ -1,10 +1,3 @@ -// Copyright (c) 2021, Very Good Ventures -// https://verygood.ventures -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - import 'package:bloc_test/bloc_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart';