feat: implement blinking `Kicker` assets (#283)

* refactor: simplified Fixtures creation

* feat: add blinking assets

* test: blinking and asset updates

* docs: clean kicker docs

* refactor: adjusted Kicker constructor

* refactor: moved Mock

Co-authored-by: alestiago <dev@alestiago.com>
pull/287/head
Allison Ryan 3 years ago committed by GitHub
parent 95fe7a62aa
commit 92b83dc892
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -38,7 +38,7 @@ class _BottomGroupSide extends Component {
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
final direction = _side.direction; final direction = _side.direction;
final centerXAdjustment = _side.isLeft ? 0 : -6.5; final centerXAdjustment = _side.isLeft ? 0 : -6.66;
final flipper = ControlledFlipper( final flipper = ControlledFlipper(
side: _side, side: _side,
@ -46,16 +46,16 @@ class _BottomGroupSide extends Component {
final baseboard = Baseboard(side: _side) final baseboard = Baseboard(side: _side)
..initialPosition = Vector2( ..initialPosition = Vector2(
(25.58 * direction) + centerXAdjustment, (25.58 * direction) + centerXAdjustment,
28.69, 28.71,
); );
final kicker = Kicker( final kicker = Kicker(
side: _side, side: _side,
children: [ children: [
ScoringBehavior(points: 5000), ScoringBehavior(points: 5000)..applyTo(['bouncy_edge']),
], ],
)..initialPosition = Vector2( )..initialPosition = Vector2(
(22.4 * direction) + centerXAdjustment, (22.64 * direction) + centerXAdjustment,
25, 25.1,
); );
await addAll([flipper, baseboard, kicker]); await addAll([flipper, baseboard, kicker]);

@ -24,8 +24,10 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.flipper.right.keyName), images.load(components.Assets.images.flipper.right.keyName),
images.load(components.Assets.images.baseboard.left.keyName), images.load(components.Assets.images.baseboard.left.keyName),
images.load(components.Assets.images.baseboard.right.keyName), images.load(components.Assets.images.baseboard.right.keyName),
images.load(components.Assets.images.kicker.left.keyName), images.load(components.Assets.images.kicker.left.lit.keyName),
images.load(components.Assets.images.kicker.right.keyName), images.load(components.Assets.images.kicker.left.dimmed.keyName),
images.load(components.Assets.images.kicker.right.lit.keyName),
images.load(components.Assets.images.kicker.right.dimmed.keyName),
images.load(components.Assets.images.slingshot.upper.keyName), images.load(components.Assets.images.slingshot.upper.keyName),
images.load(components.Assets.images.slingshot.lower.keyName), images.load(components.Assets.images.slingshot.lower.keyName),
images.load(components.Assets.images.launchRamp.ramp.keyName), images.load(components.Assets.images.launchRamp.ramp.keyName),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@ -169,13 +169,8 @@ class $AssetsImagesGoogleWordGen {
class $AssetsImagesKickerGen { class $AssetsImagesKickerGen {
const $AssetsImagesKickerGen(); const $AssetsImagesKickerGen();
/// File path: assets/images/kicker/left.png $AssetsImagesKickerLeftGen get left => const $AssetsImagesKickerLeftGen();
AssetGenImage get left => $AssetsImagesKickerRightGen get right => const $AssetsImagesKickerRightGen();
const AssetGenImage('assets/images/kicker/left.png');
/// File path: assets/images/kicker/right.png
AssetGenImage get right =>
const AssetGenImage('assets/images/kicker/right.png');
} }
class $AssetsImagesLaunchRampGen { class $AssetsImagesLaunchRampGen {
@ -344,6 +339,30 @@ class $AssetsImagesDinoAnimatronicGen {
const AssetGenImage('assets/images/dino/animatronic/mouth.png'); const AssetGenImage('assets/images/dino/animatronic/mouth.png');
} }
class $AssetsImagesKickerLeftGen {
const $AssetsImagesKickerLeftGen();
/// File path: assets/images/kicker/left/dimmed.png
AssetGenImage get dimmed =>
const AssetGenImage('assets/images/kicker/left/dimmed.png');
/// File path: assets/images/kicker/left/lit.png
AssetGenImage get lit =>
const AssetGenImage('assets/images/kicker/left/lit.png');
}
class $AssetsImagesKickerRightGen {
const $AssetsImagesKickerRightGen();
/// File path: assets/images/kicker/right/dimmed.png
AssetGenImage get dimmed =>
const AssetGenImage('assets/images/kicker/right/dimmed.png');
/// File path: assets/images/kicker/right/lit.png
AssetGenImage get lit =>
const AssetGenImage('assets/images/kicker/right/lit.png');
}
class $AssetsImagesMultiplierX2Gen { class $AssetsImagesMultiplierX2Gen {
const $AssetsImagesMultiplierX2Gen(); const $AssetsImagesMultiplierX2Gen();

@ -17,7 +17,7 @@ export 'flipper.dart';
export 'google_letter/google_letter.dart'; export 'google_letter/google_letter.dart';
export 'initial_position.dart'; export 'initial_position.dart';
export 'joint_anchor.dart'; export 'joint_anchor.dart';
export 'kicker.dart'; export 'kicker/kicker.dart';
export 'launch_ramp.dart'; export 'launch_ramp.dart';
export 'layer.dart'; export 'layer.dart';
export 'layer_sensor.dart'; export 'layer_sensor.dart';

@ -0,0 +1,2 @@
export 'kicker_ball_contact_behavior.dart';
export 'kicker_blinking_behavior.dart';

@ -0,0 +1,14 @@
// ignore_for_file: public_member_api_docs
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
class KickerBallContactBehavior extends ContactBehavior<Kicker> {
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is! Ball) return;
parent.bloc.onBallContacted();
}
}

@ -0,0 +1,37 @@
import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template kicker_blinking_behavior}
/// Makes a [Kicker] blink back to [KickerState.lit] when [KickerState.dimmed].
/// {@endtemplate}
class KickerBlinkingBehavior extends TimerComponent with ParentIsA<Kicker> {
/// {@macro kicker_blinking_behavior}
KickerBlinkingBehavior() : super(period: 0.05);
void _onNewState(KickerState state) {
switch (state) {
case KickerState.lit:
break;
case KickerState.dimmed:
timer
..reset()
..start();
break;
}
}
@override
Future<void> onLoad() async {
await super.onLoad();
timer.stop();
parent.bloc.stream.listen(_onNewState);
}
@override
void onTick() {
super.onTick();
timer.stop();
parent.bloc.onBlinked();
}
}

@ -0,0 +1,17 @@
// ignore_for_file: public_member_api_docs
import 'package:bloc/bloc.dart';
part 'kicker_state.dart';
class KickerCubit extends Cubit<KickerState> {
KickerCubit() : super(KickerState.lit);
void onBallContacted() {
emit(KickerState.dimmed);
}
void onBlinked() {
emit(KickerState.lit);
}
}

@ -0,0 +1,8 @@
// ignore_for_file: public_member_api_docs
part of 'kicker_cubit.dart';
enum KickerState {
lit,
dimmed,
}

@ -2,9 +2,15 @@ import 'dart:math' as math;
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:geometry/geometry.dart' as geometry show centroid; import 'package:geometry/geometry.dart' as geometry show centroid;
import 'package:pinball_components/gen/assets.gen.dart'; import 'package:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets; import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_components/src/components/bumping_behavior.dart';
import 'package:pinball_components/src/components/kicker/behaviors/behaviors.dart';
import 'package:pinball_flame/pinball_flame.dart';
export 'cubit/kicker_cubit.dart';
/// {@template kicker} /// {@template kicker}
/// Triangular [BodyType.static] body that propels the [Ball] towards the /// Triangular [BodyType.static] body that propels the [Ball] towards the
@ -17,42 +23,69 @@ class Kicker extends BodyComponent with InitialPosition {
Kicker({ Kicker({
required BoardSide side, required BoardSide side,
Iterable<Component>? children, Iterable<Component>? children,
}) : this._(
side: side,
bloc: KickerCubit(),
children: children,
);
Kicker._({
required BoardSide side,
required this.bloc,
Iterable<Component>? children,
}) : _side = side, }) : _side = side,
super( super(
children: [ children: [
_KickerSpriteComponent(side: side), BumpingBehavior(strength: 15)..applyTo(['bouncy_edge']),
KickerBallContactBehavior()..applyTo(['bouncy_edge']),
KickerBlinkingBehavior(),
_KickerSpriteGroupComponent(
side: side,
state: bloc.state,
),
...?children, ...?children,
], ],
renderBody: false, renderBody: false,
); );
/// The size of the [Kicker] body. /// Creates a [Kicker] without any children.
static final Vector2 size = Vector2(4.4, 15); ///
/// This can be used for testing [Kicker]'s behaviors in isolation.
// TODO(alestiago): Refactor injecting bloc once the following is merged:
// https://github.com/flame-engine/flame/pull/1538
@visibleForTesting
Kicker.test({
required this.bloc,
required BoardSide side,
}) : _side = side;
// TODO(alestiago): Consider refactoring once the following is merged:
// https://github.com/flame-engine/flame/pull/1538
// ignore: public_member_api_docs
final KickerCubit bloc;
@override
void onRemove() {
bloc.close();
super.onRemove();
}
/// Whether the [Kicker] is on the left or right side of the board. /// Whether the [Kicker] is on the left or right side of the board.
///
/// A [Kicker] with [BoardSide.left] propels the [Ball] to the right,
/// whereas a [Kicker] with [BoardSide.right] propels the [Ball] to the
/// left.
final BoardSide _side; final BoardSide _side;
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final fixturesDefs = <FixtureDef>[];
final direction = _side.direction; final direction = _side.direction;
const quarterPi = math.pi / 4; const quarterPi = math.pi / 4;
final size = Vector2(4.4, 15);
final upperCircle = CircleShape()..radius = 1.6; final upperCircle = CircleShape()..radius = 1.6;
upperCircle.position.setValues(0, upperCircle.radius / 2); upperCircle.position.setValues(0, upperCircle.radius / 2);
final upperCircleFixtureDef = FixtureDef(upperCircle);
fixturesDefs.add(upperCircleFixtureDef);
final lowerCircle = CircleShape()..radius = 1.6; final lowerCircle = CircleShape()..radius = 1.6;
lowerCircle.position.setValues( lowerCircle.position.setValues(
size.x * -direction, size.x * -direction,
size.y + 0.8, size.y + 0.8,
); );
final lowerCircleFixtureDef = FixtureDef(lowerCircle);
fixturesDefs.add(lowerCircleFixtureDef);
final wallFacingEdge = EdgeShape() final wallFacingEdge = EdgeShape()
..set( ..set(
@ -63,8 +96,6 @@ class Kicker extends BodyComponent with InitialPosition {
), ),
Vector2(2.5 * direction, size.y - 2), Vector2(2.5 * direction, size.y - 2),
); );
final wallFacingLineFixtureDef = FixtureDef(wallFacingEdge);
fixturesDefs.add(wallFacingLineFixtureDef);
final bottomEdge = EdgeShape() final bottomEdge = EdgeShape()
..set( ..set(
@ -75,8 +106,6 @@ class Kicker extends BodyComponent with InitialPosition {
lowerCircle.radius * math.sin(quarterPi), lowerCircle.radius * math.sin(quarterPi),
), ),
); );
final bottomLineFixtureDef = FixtureDef(bottomEdge);
fixturesDefs.add(bottomLineFixtureDef);
final bouncyEdge = EdgeShape() final bouncyEdge = EdgeShape()
..set( ..set(
@ -92,12 +121,13 @@ class Kicker extends BodyComponent with InitialPosition {
), ),
); );
final bouncyFixtureDef = FixtureDef( final fixturesDefs = [
bouncyEdge, FixtureDef(upperCircle),
// TODO(alestiago): Play with restitution value once game is bundled. FixtureDef(lowerCircle),
restitution: 10, FixtureDef(wallFacingEdge),
); FixtureDef(bottomEdge),
fixturesDefs.add(bouncyFixtureDef); FixtureDef(bouncyEdge, userData: 'bouncy_edge'),
];
// TODO(alestiago): Evaluate if there is value on centering the fixtures. // TODO(alestiago): Evaluate if there is value on centering the fixtures.
final centroid = geometry.centroid( final centroid = geometry.centroid(
@ -130,25 +160,46 @@ class Kicker extends BodyComponent with InitialPosition {
} }
} }
class _KickerSpriteComponent extends SpriteComponent with HasGameRef { class _KickerSpriteGroupComponent extends SpriteGroupComponent<KickerState>
_KickerSpriteComponent({required BoardSide side}) : _side = side; with HasGameRef, ParentIsA<Kicker> {
_KickerSpriteGroupComponent({
required BoardSide side,
required KickerState state,
}) : _side = side,
super(
anchor: Anchor.center,
position: Vector2(0.7 * -side.direction, -2.2),
current: state,
);
final BoardSide _side; final BoardSide _side;
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
// TODO(alestiago): Consider refactoring once the following is merged:
// TODO(alestiago): Used cached asset. // https://github.com/flame-engine/flame/pull/1538
final sprite = await gameRef.loadSprite( // ignore: public_member_api_docs
(_side.isLeft) parent.bloc.stream.listen((state) => current = state);
? Assets.images.kicker.left.keyName
: Assets.images.kicker.right.keyName, final sprites = {
); KickerState.lit: Sprite(
this.sprite = sprite; gameRef.images.fromCache(
size = sprite.originalSize / 10; (_side.isLeft)
anchor = Anchor.center; ? Assets.images.kicker.left.lit.keyName
position = Vector2(0.7 * -_side.direction, -2.2); : Assets.images.kicker.right.lit.keyName,
),
),
KickerState.dimmed: Sprite(
gameRef.images.fromCache(
(_side.isLeft)
? Assets.images.kicker.left.dimmed.keyName
: Assets.images.kicker.right.dimmed.keyName,
),
),
};
this.sprites = sprites;
size = sprites[current]!.originalSize / 10;
} }
} }

@ -64,7 +64,8 @@ flutter:
- assets/images/android/bumper/a/ - assets/images/android/bumper/a/
- assets/images/android/bumper/b/ - assets/images/android/bumper/b/
- assets/images/android/bumper/cow/ - assets/images/android/bumper/cow/
- assets/images/kicker/ - assets/images/kicker/left/
- assets/images/kicker/right/
- assets/images/plunger/ - assets/images/plunger/
- assets/images/slingshot/ - assets/images/slingshot/
- assets/images/sparky/ - assets/images/sparky/

@ -3,6 +3,16 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class KickerGame extends BallGame { class KickerGame extends BallGame {
KickerGame()
: super(
imagesFileNames: [
Assets.images.kicker.left.lit.keyName,
Assets.images.kicker.left.dimmed.keyName,
Assets.images.kicker.right.lit.keyName,
Assets.images.kicker.right.dimmed.keyName,
],
);
static const description = ''' static const description = '''
Shows how Kickers are rendered. Shows how Kickers are rendered.
@ -18,9 +28,9 @@ class KickerGame extends BallGame {
await addAll( await addAll(
[ [
Kicker(side: BoardSide.left) Kicker(side: BoardSide.left)
..initialPosition = Vector2(center.x - (Kicker.size.x * 2), center.y), ..initialPosition = Vector2(center.x - 8.8, center.y),
Kicker(side: BoardSide.right) Kicker(side: BoardSide.right)
..initialPosition = Vector2(center.x + (Kicker.size.x * 2), center.y), ..initialPosition = Vector2(center.x + 8.8, center.y),
], ],
); );

@ -29,7 +29,7 @@ class PlungerGame extends BallGame with KeyboardEvents, Traceable {
final center = screenToWorld(camera.viewport.canvasSize! / 2); final center = screenToWorld(camera.viewport.canvasSize! / 2);
await add( await add(
plunger = Plunger(compressionDistance: 29) plunger = Plunger(compressionDistance: 29)
..initialPosition = Vector2(center.x - (Kicker.size.x * 2), center.y), ..initialPosition = Vector2(center.x - 8.8, center.y),
); );
await traceAllBodies(); await traceAllBodies();
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 70 KiB

@ -0,0 +1,53 @@
// ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.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 'package:pinball_components/src/components/kicker/behaviors/behaviors.dart';
import '../../../../helpers/helpers.dart';
class _MockKickerCubit extends Mock implements KickerCubit {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
group(
'KickerBallContactBehavior',
() {
test('can be instantiated', () {
expect(
KickerBallContactBehavior(),
isA<KickerBallContactBehavior>(),
);
});
flameTester.test(
'beginContact emits onBallContacted when contacts with a ball',
(game) async {
final behavior = KickerBallContactBehavior();
final bloc = _MockKickerCubit();
whenListen(
bloc,
const Stream<KickerState>.empty(),
initialState: KickerState.lit,
);
final kicker = Kicker.test(
side: BoardSide.left,
bloc: bloc,
);
await kicker.add(behavior);
await game.ensureAdd(kicker);
behavior.beginContact(MockBall(), MockContact());
verify(kicker.bloc.onBallContacted).called(1);
},
);
},
);
}

@ -0,0 +1,50 @@
import 'dart:async';
import 'package:bloc_test/bloc_test.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 'package:pinball_components/src/components/kicker/behaviors/behaviors.dart';
import '../../../../helpers/helpers.dart';
class _MockKickerCubit extends Mock implements KickerCubit {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
group(
'KickerBlinkingBehavior',
() {
flameTester.testGameWidget(
'calls onBlinked after 0.05 seconds when dimmed',
setUp: (game, tester) async {
final behavior = KickerBlinkingBehavior();
final bloc = _MockKickerCubit();
final streamController = StreamController<KickerState>();
whenListen(
bloc,
streamController.stream,
initialState: KickerState.lit,
);
final kicker = Kicker.test(
side: BoardSide.left,
bloc: bloc,
);
await kicker.add(behavior);
await game.ensureAdd(kicker);
streamController.add(KickerState.dimmed);
await tester.pump();
game.update(0.05);
await streamController.close();
verify(bloc.onBlinked).called(1);
},
);
},
);
}

@ -0,0 +1,24 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
void main() {
group(
'KickerCubit',
() {
blocTest<KickerCubit, KickerState>(
'onBallContacted emits dimmed',
build: KickerCubit.new,
act: (bloc) => bloc.onBallContacted(),
expect: () => [KickerState.dimmed],
);
blocTest<KickerCubit, KickerState>(
'onBlinked emits lit',
build: KickerCubit.new,
act: (bloc) => bloc.onBlinked(),
expect: () => [KickerState.lit],
);
},
);
}

@ -1,29 +1,44 @@
// ignore_for_file: cascade_invocations // ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart';
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/bumping_behavior.dart';
import 'package:pinball_components/src/components/kicker/behaviors/behaviors.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
class _MockKickerCubit extends Mock implements KickerCubit {}
void main() { void main() {
group('Kicker', () { group('Kicker', () {
final flameTester = FlameTester(TestGame.new); final assets = [
Assets.images.kicker.left.lit.keyName,
Assets.images.kicker.left.dimmed.keyName,
Assets.images.kicker.right.lit.keyName,
Assets.images.kicker.right.dimmed.keyName,
];
final flameTester = FlameTester(() => TestGame(assets));
flameTester.testGameWidget( flameTester.testGameWidget(
'renders correctly', 'renders correctly',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets);
final leftKicker = Kicker( final leftKicker = Kicker(
side: BoardSide.left, side: BoardSide.left,
)..initialPosition = Vector2(-20, 0); )
..initialPosition = Vector2(-20, 0)
..renderBody = false;
final rightKicker = Kicker( final rightKicker = Kicker(
side: BoardSide.right, side: BoardSide.right,
)..initialPosition = Vector2(20, 0); )..initialPosition = Vector2(20, 0);
await game.ensureAddAll([leftKicker, rightKicker]); await game.ensureAddAll([leftKicker, rightKicker]);
game.camera.followVector2(Vector2.zero()); game.camera.followVector2(Vector2.zero());
await tester.pump();
}, },
verify: (game, tester) async { verify: (game, tester) async {
await expectLater( await expectLater(
@ -36,8 +51,9 @@ void main() {
flameTester.test( flameTester.test(
'loads correctly', 'loads correctly',
(game) async { (game) async {
final kicker = Kicker( final kicker = Kicker.test(
side: BoardSide.left, side: BoardSide.left,
bloc: KickerCubit(),
); );
await game.ensureAdd(kicker); await game.ensureAdd(kicker);
@ -45,58 +61,72 @@ void main() {
}, },
); );
flameTester.test('adds new children', (game) async { // TODO(alestiago): Consider refactoring once the following is merged:
final component = Component(); // https://github.com/flame-engine/flame/pull/1538
final kicker = Kicker( // ignore: public_member_api_docs
flameTester.test('closes bloc when removed', (game) async {
final bloc = _MockKickerCubit();
whenListen(
bloc,
const Stream<KickerState>.empty(),
initialState: KickerState.lit,
);
when(bloc.close).thenAnswer((_) async {});
final kicker = Kicker.test(
side: BoardSide.left, side: BoardSide.left,
children: [component], bloc: bloc,
); );
await game.ensureAdd(kicker); await game.ensureAdd(kicker);
expect(kicker.children, contains(component)); game.remove(kicker);
await game.ready();
verify(bloc.close).called(1);
}); });
flameTester.test( group('adds', () {
'body is static', flameTester.test('new children', (game) async {
(game) async { final component = Component();
final kicker = Kicker( final kicker = Kicker(
side: BoardSide.left, side: BoardSide.left,
children: [component],
); );
await game.ensureAdd(kicker); await game.ensureAdd(kicker);
expect(kicker.children, contains(component));
});
expect(kicker.body.bodyType, equals(BodyType.static)); flameTester.test('a BumpingBehavior', (game) async {
},
);
flameTester.test(
'has restitution',
(game) async {
final kicker = Kicker( final kicker = Kicker(
side: BoardSide.left, side: BoardSide.left,
); );
await game.ensureAdd(kicker); await game.ensureAdd(kicker);
expect(
final totalRestitution = kicker.body.fixtures.fold<double>( kicker.children.whereType<BumpingBehavior>().single,
0, isNotNull,
(total, fixture) => total + fixture.restitution,
); );
expect(totalRestitution, greaterThan(0)); });
},
);
flameTester.test( flameTester.test('a KickerBallContactBehavior', (game) async {
'has no friction',
(game) async {
final kicker = Kicker( final kicker = Kicker(
side: BoardSide.left, side: BoardSide.left,
); );
await game.ensureAdd(kicker); await game.ensureAdd(kicker);
expect(
kicker.children.whereType<KickerBallContactBehavior>().single,
isNotNull,
);
});
final totalFriction = kicker.body.fixtures.fold<double>( flameTester.test('a KickerBlinkingBehavior', (game) async {
0, final kicker = Kicker(
(total, fixture) => total + fixture.friction, side: BoardSide.left,
); );
expect(totalFriction, equals(0)); await game.ensureAdd(kicker);
}, expect(
); kicker.children.whereType<KickerBlinkingBehavior>().single,
isNotNull,
);
});
});
}); });
} }

@ -10,6 +10,10 @@ import '../../helpers/helpers.dart';
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
final assets = [ final assets = [
Assets.images.kicker.left.lit.keyName,
Assets.images.kicker.left.dimmed.keyName,
Assets.images.kicker.right.lit.keyName,
Assets.images.kicker.right.dimmed.keyName,
Assets.images.baseboard.left.keyName, Assets.images.baseboard.left.keyName,
Assets.images.baseboard.right.keyName, Assets.images.baseboard.right.keyName,
Assets.images.flipper.left.keyName, Assets.images.flipper.left.keyName,

@ -51,8 +51,10 @@ void main() {
Assets.images.googleWord.letter4.keyName, Assets.images.googleWord.letter4.keyName,
Assets.images.googleWord.letter5.keyName, Assets.images.googleWord.letter5.keyName,
Assets.images.googleWord.letter6.keyName, Assets.images.googleWord.letter6.keyName,
Assets.images.kicker.left.keyName, Assets.images.kicker.left.lit.keyName,
Assets.images.kicker.right.keyName, Assets.images.kicker.left.dimmed.keyName,
Assets.images.kicker.right.lit.keyName,
Assets.images.kicker.right.dimmed.keyName,
Assets.images.launchRamp.ramp.keyName, Assets.images.launchRamp.ramp.keyName,
Assets.images.launchRamp.foregroundRailing.keyName, Assets.images.launchRamp.foregroundRailing.keyName,
Assets.images.launchRamp.backgroundRailing.keyName, Assets.images.launchRamp.backgroundRailing.keyName,

Loading…
Cancel
Save