feat: resolved missing ContactCallbacks with Behaviors

pull/234/head
alestiago 3 years ago
parent 31a92c0a38
commit 5019fe1701

@ -1,7 +1,6 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/game/components/alien_zone/alien_bumper/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
@ -9,8 +8,6 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template alien_zone}
/// Area positioned below [Spaceship] where the [Ball]
/// can bounce off [AlienBumper]s.
///
/// When a [Ball] hits an [AlienBumper], the bumper animates.
/// {@endtemplate}
class AlienZone extends Blueprint {
/// {@macro alien_zone}
@ -20,15 +17,11 @@ class AlienZone extends Blueprint {
AlienBumper.a(
children: [
ScoringBehaviour(points: 20),
AlienBumperContactBehavior(),
AlienBumperSpriteBehavior(),
],
)..initialPosition = Vector2(-32.52, -9.1),
AlienBumper.b(
children: [
ScoringBehaviour(points: 20),
AlienBumperContactBehavior(),
AlienBumperSpriteBehavior(),
],
)..initialPosition = Vector2(-22.89, -17.35),
],

@ -1,2 +0,0 @@
export 'alien_bumper_contact_behavior.dart';
export 'alien_bumper_sprite_behavior.dart';

@ -1,4 +1,4 @@
export 'alien_zone/alien_zone.dart';
export 'alien_zone.dart';
export 'board.dart';
export 'camera_controller.dart';
export 'controlled_ball.dart';
@ -6,7 +6,7 @@ export 'controlled_flipper.dart';
export 'controlled_plunger.dart';
export 'flutter_forest.dart';
export 'game_flow_controller.dart';
export 'google_word.dart';
export 'google_word/google_word.dart';
export 'launcher.dart';
export 'scoring_behaviour.dart';
export 'sparky_fire_zone.dart';

@ -1,82 +0,0 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:async';
import 'package:flame/components.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template google_word}
/// Loads all [GoogleLetter]s to compose a [GoogleWord].
/// {@endtemplate}
class GoogleWord extends Component
with HasGameRef<PinballGame>, Controls<_GoogleWordController> {
/// {@macro google_word}
GoogleWord({
required Vector2 position,
}) : _position = position {
controller = _GoogleWordController(this);
}
final Vector2 _position;
@override
Future<void> onLoad() async {
await super.onLoad();
final offsets = [
Vector2(-12.92, 1.82),
Vector2(-8.33, -0.65),
Vector2(-2.88, -1.75),
Vector2(2.88, -1.75),
Vector2(8.33, -0.65),
Vector2(12.92, 1.82),
];
final letters = <GoogleLetter>[];
for (var index = 0; index < offsets.length; index++) {
letters.add(
GoogleLetter(index)..initialPosition = _position + offsets[index],
);
}
await addAll(letters);
}
}
class _GoogleWordController extends ComponentController<GoogleWord>
with HasGameRef<PinballGame> {
_GoogleWordController(GoogleWord googleWord) : super(googleWord);
final _activatedLetters = <GoogleLetter>{};
void activate(GoogleLetter googleLetter) {
if (!_activatedLetters.add(googleLetter)) return;
googleLetter.activate();
final activatedBonus = _activatedLetters.length == 6;
if (activatedBonus) {
gameRef.audio.googleBonus();
gameRef.read<GameBloc>().add(const BonusActivated(GameBonus.googleWord));
component.children.whereType<GoogleLetter>().forEach(
(letter) => letter.deactivate(),
);
_activatedLetters.clear();
}
}
}
/// Activates a [GoogleLetter] when it contacts with a [Ball].
// TODO(alestiago): Add animation behaviour.
// class _GoogleLetterBallContactCallback
// extends ContactCallback<GoogleLetter, Ball> {
// @override
// void begin(GoogleLetter googleLetter, _, __) {
// final parent = googleLetter.parent;
// if (parent is GoogleWord) {
// parent.controller.activate(googleLetter);
// }
// }
// }

@ -0,0 +1,37 @@
import 'dart:async';
import 'package:flame/components.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
class BonusBehaviour extends Component
with HasGameRef<PinballGame>, ParentIsA<GoogleWord> {
BonusBehaviour(
Iterable<GoogleLetter> googleLetters,
) : _googleLetters = googleLetters;
Iterable<GoogleLetter> _googleLetters;
@override
Future<void> onLoad() async {
await super.onLoad();
for (final letter in _googleLetters) {
letter.bloc.stream.listen((_) {
final achievedBonus = _googleLetters
.every((letter) => letter.bloc.state == GoogleLetterState.active);
if (achievedBonus) {
gameRef.audio.googleBonus();
gameRef
.read<GameBloc>()
.add(const BonusActivated(GameBonus.googleWord));
for (final letter in _googleLetters) {
letter.bloc.onReset();
}
}
});
}
}
}

@ -0,0 +1,38 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:async';
import 'package:flame/components.dart';
import 'package:pinball/game/components/google_word/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template google_word}
/// Loads all [GoogleLetter]s to compose a [GoogleWord].
/// {@endtemplate}
class GoogleWord extends Component with HasGameRef<PinballGame> {
/// {@macro google_word}
GoogleWord({
required Vector2 position,
}) : _position = position;
final Vector2 _position;
@override
Future<void> onLoad() async {
await super.onLoad();
final letters = [
GoogleLetter(0)..initialPosition = _position + Vector2(-12.92, 1.82),
GoogleLetter(1)..initialPosition = _position + Vector2(-8.33, -0.65),
GoogleLetter(2)..initialPosition = _position + Vector2(-2.88, -1.75),
GoogleLetter(3)..initialPosition = _position + Vector2(2.88, -1.75),
GoogleLetter(4)..initialPosition = _position + Vector2(8.33, -0.65),
GoogleLetter(5)..initialPosition = _position + Vector2(12.92, 1.82)
];
await addAll([
...letters,
BonusBehaviour(letters),
]);
}
}

@ -40,21 +40,6 @@ class SparkyFireZone extends Blueprint {
);
}
/// Listens when a [Ball] bounces bounces against a [SparkyBumper].
// TODO(alestiago): Add animation behaviour.
// @visibleForTesting
// class SparkyBumperBallContactCallback
// extends ContactCallback<SparkyBumper, Ball> {
// @override
// void begin(
// SparkyBumper sparkyBumper,
// Ball _,
// Contact __,
// ) {
// sparkyBumper.animate();
// }
// }
/// {@template sparky_computer_sensor}
/// Small sensor body used to detect when a ball has entered the
/// [SparkyComputer].

@ -58,7 +58,6 @@ class PinballGame extends Forge2DGame
await addFromBlueprint(SparkyFireZone());
unawaited(addFromBlueprint(Slingshots()));
unawaited(addFromBlueprint(DinoWalls()));
unawaited(_addBonusWord());
unawaited(addFromBlueprint(SpaceshipRamp()));
unawaited(
addFromBlueprint(
@ -68,12 +67,6 @@ class PinballGame extends Forge2DGame
),
);
unawaited(addFromBlueprint(SpaceshipRail()));
controller.attachTo(launcher.components.whereType<Plunger>().first);
await super.onLoad();
}
Future<void> _addBonusWord() async {
await add(
GoogleWord(
position: Vector2(
@ -82,6 +75,9 @@ class PinballGame extends Forge2DGame
),
),
);
controller.attachTo(launcher.components.whereType<Plunger>().first);
await super.onLoad();
}
}

@ -3,43 +3,15 @@ import 'dart:async';
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/alien_bumper/behaviors/behaviors.dart';
import 'package:pinball_flame/pinball_flame.dart';
abstract class State<T> {
// TODO(alestiago): Investigate approaches to avoid having this as late.
late T _state;
T get state => _state;
set state(T value) {
if (value == _state) return;
_state = value;
_stream.sink.add(value);
}
final StreamController<T> _stream = StreamController<T>.broadcast();
Stream<T> get stream => _stream.stream;
void close() {
_stream.close();
}
}
/// Indicates the [AlienBumper]'s current sprite state.
enum AlienBumperState {
/// A lit up bumper.
active,
/// A dimmed bumper.
inactive,
}
export 'cubit/alien_bumper_cubit.dart';
/// {@template alien_bumper}
/// Bumper for area under the [Spaceship].
/// {@endtemplate}
class AlienBumper extends BodyComponent
with InitialPosition, State<AlienBumperState> {
class AlienBumper extends BodyComponent with InitialPosition {
/// {@macro alien_bumper}
AlienBumper._({
required double majorRadius,
@ -47,22 +19,23 @@ class AlienBumper extends BodyComponent
required String onAssetPath,
required String offAssetPath,
Iterable<Component>? children,
required this.bloc,
}) : _majorRadius = majorRadius,
_minorRadius = minorRadius,
super(
priority: RenderPriority.alienBumper,
children: [
ContactBehavior(),
SpriteBehavior(),
_AlienBumperSpriteGroupComponent(
offAssetPath: offAssetPath,
onAssetPath: onAssetPath,
state: bloc.state,
),
if (children != null) ...children,
],
renderBody: false,
) {
_state = AlienBumperState.active;
_spriteGroupComponent = _AlienBumperSpriteGroupComponent(
offAssetPath: offAssetPath,
onAssetPath: onAssetPath,
state: state,
);
}
);
/// {@macro alien_bumper}
AlienBumper.a({
@ -72,6 +45,7 @@ class AlienBumper extends BodyComponent
minorRadius: 2.97,
onAssetPath: Assets.images.alienBumper.a.active.keyName,
offAssetPath: Assets.images.alienBumper.a.inactive.keyName,
bloc: AlienBumperCubit(),
children: children,
);
@ -83,6 +57,7 @@ class AlienBumper extends BodyComponent
minorRadius: 2.79,
onAssetPath: Assets.images.alienBumper.b.active.keyName,
offAssetPath: Assets.images.alienBumper.b.inactive.keyName,
bloc: AlienBumperCubit(),
children: children,
);
@ -90,17 +65,12 @@ class AlienBumper extends BodyComponent
final double _minorRadius;
late final _AlienBumperSpriteGroupComponent _spriteGroupComponent;
@override
Future<void> onLoad() async {
await super.onLoad();
await add(_spriteGroupComponent);
}
// TODO(alestiago): Evaluate testing this.
final AlienBumperCubit bloc;
@override
void onRemove() {
close();
bloc.close();
super.onRemove();
}
@ -144,6 +114,7 @@ class _AlienBumperSpriteGroupComponent
@override
Future<void> onLoad() async {
await super.onLoad();
parent.bloc.stream.listen((state) => current = state);
final sprites = {
AlienBumperState.active: Sprite(
@ -154,7 +125,5 @@ class _AlienBumperSpriteGroupComponent
};
this.sprites = sprites;
size = sprites[current]!.originalSize / 10;
parent.stream.listen((state) => current = state);
}
}

@ -0,0 +1,2 @@
export 'contact_behavior.dart';
export 'sprite_behavior.dart';

@ -0,0 +1,33 @@
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';
class ContactBehavior extends Component
with ContactCallbacks, ParentIsA<AlienBumper> {
@override
Future<void> onLoad() async {
await super.onLoad();
// TODO(alestiago): Consider defining a generic ContactBehaviour to get
// rid of this repeated logic.
final userData = parent.body.userData;
if (userData is ContactCallbacksGroup) {
userData.addContactCallbacks(this);
} else if (userData is ContactCallbacks) {
final notifier = ContactCallbacksGroup()
..addContactCallbacks(userData)
..addContactCallbacks(this);
parent.body.userData = notifier;
} else {
parent.body.userData = this;
}
}
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is! Ball) return;
parent.bloc.onBallContacted();
}
}

@ -5,10 +5,9 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template alien_bumper_sprite_behavior}
///
/// {@endtemplate}
class AlienBumperSpriteBehavior extends TimerComponent
with ParentIsA<AlienBumper> {
class SpriteBehavior extends TimerComponent with ParentIsA<AlienBumper> {
/// {@macro alien_bumper_sprite_behavior}
AlienBumperSpriteBehavior()
SpriteBehavior()
: super(
period: 0.05,
removeOnFinish: false,
@ -31,12 +30,12 @@ class AlienBumperSpriteBehavior extends TimerComponent
Future<void> onLoad() async {
await super.onLoad();
timer.stop();
parent.stream.listen(_onNewState);
parent.bloc.stream.listen(_onNewState);
}
@override
void onTick() {
super.onTick();
parent.state = AlienBumperState.active;
parent.bloc.onAnimated();
}
}

@ -0,0 +1,19 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
part 'alien_bumper_state.dart';
class AlienBumperCubit extends Cubit<AlienBumperState> {
AlienBumperCubit() : super(AlienBumperState.active);
void onBallContacted() {
emit(AlienBumperState.inactive);
// Future<void>.delayed(const Duration(milliseconds: 500)).whenComplete(
// () => emit(AlienBumperState.active),
// );
}
void onAnimated() {
emit(AlienBumperState.active);
}
}

@ -0,0 +1,10 @@
part of 'alien_bumper_cubit.dart';
/// Indicates the [AlienBumperCubit]'s current state.
enum AlienBumperState {
/// A lit up bumper.
active,
/// A dimmed bumper.
inactive,
}

@ -1,4 +1,4 @@
export 'alien_bumper.dart';
export 'alien_bumper/alien_bumper.dart';
export 'backboard/backboard.dart';
export 'ball.dart';
export 'baseboard.dart';
@ -12,7 +12,7 @@ export 'dash_nest_bumper.dart';
export 'dino_walls.dart';
export 'fire_effect.dart';
export 'flipper.dart';
export 'google_letter.dart';
export 'google_letter/google_letter.dart';
export 'initial_position.dart';
export 'joint_anchor.dart';
export 'kicker.dart';
@ -30,5 +30,5 @@ export 'spaceship.dart';
export 'spaceship_rail.dart';
export 'spaceship_ramp.dart';
export 'sparky_animatronic.dart';
export 'sparky_bumper.dart';
export 'sparky_computer.dart';
export 'sparky_bumper/sparky_bumper.dart';
export 'sparky_computer/sparky_computer.dart';

@ -0,0 +1,33 @@
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';
class ContactBehavior extends Component
with ContactCallbacks, ParentIsA<GoogleLetter> {
@override
Future<void> onLoad() async {
await super.onLoad();
// TODO(alestiago): Consider defining a generic ContactBehaviour to get
// rid of this repeated logic.
final userData = parent.body.userData;
if (userData is ContactCallbacksGroup) {
userData.addContactCallbacks(this);
} else if (userData is ContactCallbacks) {
final notifier = ContactCallbacksGroup()
..addContactCallbacks(userData)
..addContactCallbacks(this);
parent.body.userData = notifier;
} else {
parent.body.userData = this;
}
}
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is! Ball) return;
parent.bloc.onBallContacted();
}
}

@ -0,0 +1,15 @@
import 'package:bloc/bloc.dart';
part 'google_letter_state.dart';
class GoogleLetterCubit extends Cubit<GoogleLetterState> {
GoogleLetterCubit() : super(GoogleLetterState.inactive);
void onBallContacted() {
emit(GoogleLetterState.active);
}
void onReset() {
emit(GoogleLetterState.inactive);
}
}

@ -0,0 +1,7 @@
part of 'google_letter_cubit.dart';
enum GoogleLetterState {
active,
inactive,
}

@ -3,6 +3,10 @@ import 'package:flame/effects.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/google_letter/behaviors/behaviors.dart';
import 'package:pinball_flame/pinball_flame.dart';
export 'cubit/google_letter_cubit.dart';
/// {@template google_letter}
/// Circular sensor that represents a letter in "GOOGLE" for a given index.
@ -10,25 +14,15 @@ import 'package:pinball_components/pinball_components.dart';
class GoogleLetter extends BodyComponent with InitialPosition {
/// {@macro google_letter}
GoogleLetter(int index)
: _sprite = _GoogleLetterSprite(
_GoogleLetterSprite.spritePaths[index],
: super(
children: [
ContactBehavior(),
_GoogleLetterSprite(_GoogleLetterSprite.spritePaths[index])
],
);
final _GoogleLetterSprite _sprite;
/// Activates this [GoogleLetter].
// TODO(alestiago): Improve doc comment once activate and deactivate
// are implemented with the actual assets.
Future<void> activate() => _sprite.activate();
/// Deactivates this [GoogleLetter].
Future<void> deactivate() => _sprite.deactivate();
@override
Future<void> onLoad() async {
await super.onLoad();
await add(_sprite);
}
// TODO(alestiago): Evaluate testing this.
final GoogleLetterCubit bloc = GoogleLetterCubit();
@override
Body createBody() {
@ -46,8 +40,11 @@ class GoogleLetter extends BodyComponent with InitialPosition {
}
}
class _GoogleLetterSprite extends SpriteComponent with HasGameRef {
_GoogleLetterSprite(String path) : _path = path;
class _GoogleLetterSprite extends SpriteComponent
with HasGameRef, ParentIsA<GoogleLetter> {
_GoogleLetterSprite(String path)
: _path = path,
super(anchor: Anchor.center);
static final spritePaths = [
Assets.images.googleWord.letter1.keyName,
@ -60,30 +57,29 @@ class _GoogleLetterSprite extends SpriteComponent with HasGameRef {
final String _path;
// TODO(alestiago): Correctly implement activate and deactivate once the
// assets are provided.
Future<void> activate() async {
await add(
_GoogleLetterColorEffect(color: Colors.green),
);
}
Future<void> deactivate() async {
await add(
_GoogleLetterColorEffect(color: Colors.red),
);
void _onNewState(GoogleLetterState state) {
switch (state) {
case GoogleLetterState.active:
add(_GoogleLetterColorEffect(color: Colors.green));
break;
case GoogleLetterState.inactive:
add(
_GoogleLetterColorEffect(color: Colors.red),
);
break;
}
}
@override
Future<void> onLoad() async {
await super.onLoad();
parent.bloc.stream.listen(_onNewState);
// TODO(alestiago): Used cached assets.
final sprite = await gameRef.loadSprite(_path);
this.sprite = sprite;
// TODO(alestiago): Size correctly once the assets are provided.
size = sprite.originalSize / 5;
anchor = Anchor.center;
}
}

@ -0,0 +1,2 @@
export 'contact_behavior.dart';
export 'sprite_behavior.dart';

@ -3,8 +3,8 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
class AlienBumperContactBehavior extends Component
with ContactCallbacks, ParentIsA<AlienBumper> {
class ContactBehavior extends Component
with ContactCallbacks, ParentIsA<SparkyBumper> {
@override
Future<void> onLoad() async {
await super.onLoad();
@ -26,6 +26,6 @@ class AlienBumperContactBehavior extends Component
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is! Ball) return;
parent.state = AlienBumperState.inactive;
parent.bloc.onBallContacted();
}
}

@ -0,0 +1,41 @@
import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template alien_bumper_sprite_behavior}
///
/// {@endtemplate}
class SpriteBehavior extends TimerComponent with ParentIsA<SparkyBumper> {
/// {@macro alien_bumper_sprite_behavior}
SpriteBehavior()
: super(
period: 0.05,
removeOnFinish: false,
);
void _onNewState(SparkyBumperState state) {
switch (state) {
case SparkyBumperState.active:
timer.stop();
break;
case SparkyBumperState.inactive:
timer
..reset()
..start();
break;
}
}
@override
Future<void> onLoad() async {
await super.onLoad();
timer.stop();
parent.bloc.stream.listen(_onNewState);
}
@override
void onTick() {
super.onTick();
parent.bloc.onAnimated();
}
}

@ -0,0 +1,19 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
part 'sparky_bumper_state.dart';
class SparkyBumperCubit extends Cubit<SparkyBumperState> {
SparkyBumperCubit() : super(SparkyBumperState.active);
void onBallContacted() {
emit(SparkyBumperState.inactive);
// Future<void>.delayed(const Duration(milliseconds: 500)).whenComplete(
// () => emit(AlienBumperState.active),
// );
}
void onAnimated() {
emit(SparkyBumperState.active);
}
}

@ -0,0 +1,10 @@
part of 'sparky_bumper_cubit.dart';
/// Indicates the [SparkyBumperCubit]'s current state.
enum SparkyBumperState {
/// A lit up bumper.
active,
/// A dimmed bumper.
inactive,
}

@ -2,8 +2,11 @@ import 'dart:math' as math;
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/sparky_bumper/behaviors/behaviors.dart';
import 'package:pinball_flame/pinball_flame.dart';
export 'cubit/sparky_bumper_cubit.dart';
/// {@template sparky_bumper}
/// Bumper for Sparky area.
@ -16,16 +19,20 @@ class SparkyBumper extends BodyComponent with InitialPosition {
required String onAssetPath,
required String offAssetPath,
required Vector2 spritePosition,
required this.bloc,
Iterable<Component>? children,
}) : _majorRadius = majorRadius,
_minorRadius = minorRadius,
super(
priority: RenderPriority.sparkyBumper,
children: [
ContactBehavior(),
SpriteBehavior(),
_SparkyBumperSpriteGroupComponent(
onAssetPath: onAssetPath,
offAssetPath: offAssetPath,
position: spritePosition,
state: bloc.state,
),
if (children != null) ...children,
],
@ -41,6 +48,7 @@ class SparkyBumper extends BodyComponent with InitialPosition {
onAssetPath: Assets.images.sparky.bumper.a.active.keyName,
offAssetPath: Assets.images.sparky.bumper.a.inactive.keyName,
spritePosition: Vector2(0, -0.25),
bloc: SparkyBumperCubit(),
children: children,
);
@ -53,6 +61,7 @@ class SparkyBumper extends BodyComponent with InitialPosition {
onAssetPath: Assets.images.sparky.bumper.b.active.keyName,
offAssetPath: Assets.images.sparky.bumper.b.inactive.keyName,
spritePosition: Vector2(0, -0.35),
bloc: SparkyBumperCubit(),
children: children,
);
@ -65,12 +74,16 @@ class SparkyBumper extends BodyComponent with InitialPosition {
onAssetPath: Assets.images.sparky.bumper.c.active.keyName,
offAssetPath: Assets.images.sparky.bumper.c.inactive.keyName,
spritePosition: Vector2(0, -0.4),
bloc: SparkyBumperCubit(),
children: children,
);
final double _majorRadius;
final double _minorRadius;
// TODO(alestiago): Evaluate testing this.
final SparkyBumperCubit bloc;
@override
Body createBody() {
final shape = EllipseShape(
@ -88,37 +101,22 @@ class SparkyBumper extends BodyComponent with InitialPosition {
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
/// Animates the [DashNestBumper].
Future<void> animate() async {
final spriteGroupComponent = firstChild<_SparkyBumperSpriteGroupComponent>()
?..current = SparkyBumperSpriteState.inactive;
await Future<void>.delayed(const Duration(milliseconds: 50));
spriteGroupComponent?.current = SparkyBumperSpriteState.active;
}
}
/// Indicates the [SparkyBumper]'s current sprite state.
@visibleForTesting
enum SparkyBumperSpriteState {
/// A lit up bumper.
active,
/// A dimmed bumper.
inactive,
}
class _SparkyBumperSpriteGroupComponent
extends SpriteGroupComponent<SparkyBumperSpriteState> with HasGameRef {
extends SpriteGroupComponent<SparkyBumperState>
with HasGameRef, ParentIsA<SparkyBumper> {
_SparkyBumperSpriteGroupComponent({
required String onAssetPath,
required String offAssetPath,
required Vector2 position,
required SparkyBumperState state,
}) : _onAssetPath = onAssetPath,
_offAssetPath = offAssetPath,
super(
anchor: Anchor.center,
position: position,
current: state,
);
final String _onAssetPath;
@ -127,15 +125,14 @@ class _SparkyBumperSpriteGroupComponent
@override
Future<void> onLoad() async {
await super.onLoad();
parent.bloc.stream.listen((state) => current = state);
final sprites = {
SparkyBumperSpriteState.active:
Sprite(gameRef.images.fromCache(_onAssetPath)),
SparkyBumperSpriteState.inactive:
SparkyBumperState.active: Sprite(gameRef.images.fromCache(_onAssetPath)),
SparkyBumperState.inactive:
Sprite(gameRef.images.fromCache(_offAssetPath)),
};
this.sprites = sprites;
current = SparkyBumperSpriteState.active;
size = sprites[current]!.originalSize / 10;
}
}

@ -7,9 +7,10 @@ environment:
sdk: ">=2.16.0 <3.0.0"
dependencies:
bloc: ^8.0.3
flame: ^1.1.1
flame_forge2d:
git:
git:
url: https://github.com/flame-engine/flame/
path: packages/flame_forge2d/
ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f
@ -23,7 +24,6 @@ dependencies:
pinball_theme:
path: ../pinball_theme
dev_dependencies:
flame_test: ^1.3.0
flutter_test:

Loading…
Cancel
Save