diff --git a/lib/game/components/bottom_group.dart b/lib/game/components/bottom_group.dart index 921a8e58..d511e5cf 100644 --- a/lib/game/components/bottom_group.dart +++ b/lib/game/components/bottom_group.dart @@ -36,7 +36,7 @@ class _BottomGroupSide extends Component { @override Future onLoad() async { final direction = _side.direction; - final centerXAdjustment = _side.isLeft ? 0 : -6.5; + final centerXAdjustment = _side.isLeft ? 0 : -6.66; final flipper = ControlledFlipper( side: _side, @@ -44,16 +44,17 @@ class _BottomGroupSide extends Component { final baseboard = Baseboard(side: _side) ..initialPosition = Vector2( (25.58 * direction) + centerXAdjustment, - 28.69, + 28.71, ); final kicker = Kicker( side: _side, + bloc: KickerCubit(), children: [ - ScoringBehavior(points: 5000), + ScoringBehavior(points: 5000)..applyTo(['bouncy_edge']), ], )..initialPosition = Vector2( - (22.4 * direction) + centerXAdjustment, - 25, + (22.64 * direction) + centerXAdjustment, + 25.1, ); await addAll([flipper, baseboard, kicker]); diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 744593fb..e48ab7ff 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -24,8 +24,10 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.flipper.right.keyName), images.load(components.Assets.images.baseboard.left.keyName), images.load(components.Assets.images.baseboard.right.keyName), - images.load(components.Assets.images.kicker.left.keyName), - images.load(components.Assets.images.kicker.right.keyName), + images.load(components.Assets.images.kicker.left.lit.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.lower.keyName), images.load(components.Assets.images.launchRamp.ramp.keyName), diff --git a/packages/pinball_components/assets/images/kicker/left.png b/packages/pinball_components/assets/images/kicker/left.png deleted file mode 100644 index 42bd5030..00000000 Binary files a/packages/pinball_components/assets/images/kicker/left.png and /dev/null differ diff --git a/packages/pinball_components/assets/images/kicker/left/dimmed.png b/packages/pinball_components/assets/images/kicker/left/dimmed.png new file mode 100644 index 00000000..70196876 Binary files /dev/null and b/packages/pinball_components/assets/images/kicker/left/dimmed.png differ diff --git a/packages/pinball_components/assets/images/kicker/left/lit.png b/packages/pinball_components/assets/images/kicker/left/lit.png new file mode 100644 index 00000000..d2f57661 Binary files /dev/null and b/packages/pinball_components/assets/images/kicker/left/lit.png differ diff --git a/packages/pinball_components/assets/images/kicker/right.png b/packages/pinball_components/assets/images/kicker/right.png deleted file mode 100644 index 0a746f3c..00000000 Binary files a/packages/pinball_components/assets/images/kicker/right.png and /dev/null differ diff --git a/packages/pinball_components/assets/images/kicker/right/dimmed.png b/packages/pinball_components/assets/images/kicker/right/dimmed.png new file mode 100644 index 00000000..3e4a3b4f Binary files /dev/null and b/packages/pinball_components/assets/images/kicker/right/dimmed.png differ diff --git a/packages/pinball_components/assets/images/kicker/right/lit.png b/packages/pinball_components/assets/images/kicker/right/lit.png new file mode 100644 index 00000000..cfe992b4 Binary files /dev/null and b/packages/pinball_components/assets/images/kicker/right/lit.png differ diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index 94022534..9e6e6df1 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -169,13 +169,8 @@ class $AssetsImagesGoogleWordGen { class $AssetsImagesKickerGen { const $AssetsImagesKickerGen(); - /// File path: assets/images/kicker/left.png - AssetGenImage get left => - const AssetGenImage('assets/images/kicker/left.png'); - - /// File path: assets/images/kicker/right.png - AssetGenImage get right => - const AssetGenImage('assets/images/kicker/right.png'); + $AssetsImagesKickerLeftGen get left => const $AssetsImagesKickerLeftGen(); + $AssetsImagesKickerRightGen get right => const $AssetsImagesKickerRightGen(); } class $AssetsImagesLaunchRampGen { @@ -344,6 +339,30 @@ class $AssetsImagesDinoAnimatronicGen { 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 { const $AssetsImagesMultiplierX2Gen(); diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index 2781030e..9a29e77f 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -17,7 +17,7 @@ export 'flipper.dart'; export 'google_letter/google_letter.dart'; export 'initial_position.dart'; export 'joint_anchor.dart'; -export 'kicker.dart'; +export 'kicker/kicker.dart'; export 'launch_ramp.dart'; export 'layer.dart'; export 'layer_sensor.dart'; diff --git a/packages/pinball_components/lib/src/components/kicker/behaviors/behaviors.dart b/packages/pinball_components/lib/src/components/kicker/behaviors/behaviors.dart index e69de29b..e1098a34 100644 --- a/packages/pinball_components/lib/src/components/kicker/behaviors/behaviors.dart +++ b/packages/pinball_components/lib/src/components/kicker/behaviors/behaviors.dart @@ -0,0 +1,2 @@ +export 'kicker_ball_contact_behavior.dart'; +export 'kicker_blinking_behavior.dart'; diff --git a/packages/pinball_components/lib/src/components/kicker/behaviors/kicker_ball_contact_behavior.dart b/packages/pinball_components/lib/src/components/kicker/behaviors/kicker_ball_contact_behavior.dart index 8b137891..d5d2eb6c 100644 --- a/packages/pinball_components/lib/src/components/kicker/behaviors/kicker_ball_contact_behavior.dart +++ b/packages/pinball_components/lib/src/components/kicker/behaviors/kicker_ball_contact_behavior.dart @@ -1 +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 { + @override + void beginContact(Object other, Contact contact) { + super.beginContact(other, contact); + if (other is! Ball) return; + parent.bloc.onBallContacted(); + } +} diff --git a/packages/pinball_components/lib/src/components/kicker/behaviors/kicker_blinking_behavior.dart b/packages/pinball_components/lib/src/components/kicker/behaviors/kicker_blinking_behavior.dart index 8b137891..569d461f 100644 --- a/packages/pinball_components/lib/src/components/kicker/behaviors/kicker_blinking_behavior.dart +++ b/packages/pinball_components/lib/src/components/kicker/behaviors/kicker_blinking_behavior.dart @@ -1 +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 { + /// {@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 onLoad() async { + await super.onLoad(); + timer.stop(); + parent.bloc.stream.listen(_onNewState); + } + + @override + void onTick() { + super.onTick(); + timer.stop(); + parent.bloc.onBlinked(); + } +} diff --git a/packages/pinball_components/lib/src/components/kicker/cubit/kicker_cubit.dart b/packages/pinball_components/lib/src/components/kicker/cubit/kicker_cubit.dart index 8428a64f..488f4683 100644 --- a/packages/pinball_components/lib/src/components/kicker/cubit/kicker_cubit.dart +++ b/packages/pinball_components/lib/src/components/kicker/cubit/kicker_cubit.dart @@ -5,13 +5,13 @@ import 'package:bloc/bloc.dart'; part 'kicker_state.dart'; class KickerCubit extends Cubit { - KickerCubit() : super(KickerState.dimmed); + KickerCubit() : super(KickerState.lit); void onBallContacted() { - emit(KickerState.lit); + emit(KickerState.dimmed); } void onBlinked() { - emit(KickerState.dimmed); + emit(KickerState.lit); } } diff --git a/packages/pinball_components/lib/src/components/kicker/kicker.dart b/packages/pinball_components/lib/src/components/kicker/kicker.dart index e0ac87ee..3507de2e 100644 --- a/packages/pinball_components/lib/src/components/kicker/kicker.dart +++ b/packages/pinball_components/lib/src/components/kicker/kicker.dart @@ -2,9 +2,15 @@ import 'dart:math' as math; import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/material.dart'; import 'package:geometry/geometry.dart' as geometry show centroid; import 'package:pinball_components/gen/assets.gen.dart'; 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} /// Triangular [BodyType.static] body that propels the [Ball] towards the @@ -16,16 +22,44 @@ class Kicker extends BodyComponent with InitialPosition { /// {@macro kicker} Kicker({ required BoardSide side, + required this.bloc, Iterable? children, }) : _side = side, super( children: [ - _KickerSpriteComponent(side: side), + BumpingBehavior(strength: 15)..applyTo(['bouncy_edge']), + KickerBallContactBehavior()..applyTo(['bouncy_edge']), + KickerBlinkingBehavior(), + _KickerSpriteGroupComponent( + side: side, + state: bloc.state, + ), ...?children, ], renderBody: false, ); + /// Creates an [Kicker] without any children. + /// + /// 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, + }) : _side = BoardSide.left; + + // 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. /// /// A [Kicker] with [BoardSide.left] propels the [Ball] to the right, @@ -86,8 +120,7 @@ class Kicker extends BodyComponent with InitialPosition { FixtureDef(lowerCircle), FixtureDef(wallFacingEdge), FixtureDef(bottomEdge), - // TODO(alestiago): Add BumpingBehaviour. - FixtureDef(bouncyEdge), + FixtureDef(bouncyEdge, userData: 'bouncy_edge'), ]; // TODO(alestiago): Evaluate if there is value on centering the fixtures. @@ -121,25 +154,46 @@ class Kicker extends BodyComponent with InitialPosition { } } -class _KickerSpriteComponent extends SpriteComponent with HasGameRef { - _KickerSpriteComponent({required BoardSide side}) : _side = side; +class _KickerSpriteGroupComponent extends SpriteGroupComponent + with HasGameRef, ParentIsA { + _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; @override Future onLoad() async { await super.onLoad(); - - // TODO(alestiago): Used cached asset. - final sprite = await gameRef.loadSprite( - (_side.isLeft) - ? Assets.images.kicker.left.keyName - : Assets.images.kicker.right.keyName, - ); - this.sprite = sprite; - size = sprite.originalSize / 10; - anchor = Anchor.center; - position = Vector2(0.7 * -_side.direction, -2.2); + // TODO(alestiago): Consider refactoring once the following is merged: + // https://github.com/flame-engine/flame/pull/1538 + // ignore: public_member_api_docs + parent.bloc.stream.listen((state) => current = state); + + final sprites = { + KickerState.lit: Sprite( + gameRef.images.fromCache( + (_side.isLeft) + ? Assets.images.kicker.left.lit.keyName + : 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; } } diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index 29b4e31a..8352afa3 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -64,7 +64,8 @@ flutter: - assets/images/android/bumper/a/ - assets/images/android/bumper/b/ - assets/images/android/bumper/cow/ - - assets/images/kicker/ + - assets/images/kicker/left/ + - assets/images/kicker/right/ - assets/images/plunger/ - assets/images/slingshot/ - assets/images/sparky/ diff --git a/packages/pinball_components/sandbox/lib/stories/bottom_group/kicker_game.dart b/packages/pinball_components/sandbox/lib/stories/bottom_group/kicker_game.dart index 42e21912..d59ed73b 100644 --- a/packages/pinball_components/sandbox/lib/stories/bottom_group/kicker_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/bottom_group/kicker_game.dart @@ -3,6 +3,16 @@ import 'package:pinball_components/pinball_components.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; 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 = ''' Shows how Kickers are rendered. @@ -17,9 +27,9 @@ class KickerGame extends BallGame { final center = screenToWorld(camera.viewport.canvasSize! / 2); await addAll( [ - Kicker(side: BoardSide.left) + Kicker(side: BoardSide.left, bloc: KickerCubit()) ..initialPosition = Vector2(center.x - 8.8, center.y), - Kicker(side: BoardSide.right) + Kicker(side: BoardSide.right, bloc: KickerCubit()) ..initialPosition = Vector2(center.x + 8.8, center.y), ], );