feat: implemented ComponentController

pull/111/head
alestiago 4 years ago
parent 20879f054e
commit c5b271003c

@ -1,62 +1,15 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
/// {@template ball_type}
/// Specifies the type of [Ball].
///
/// Different [BallType]s are affected by different game mechanics.
/// {@endtemplate}
enum BallType {
/// A [Ball] spawned from the [Plunger].
///
/// [normal] balls decrease the [GameState.balls] when they fall through the
/// the [BottomWall].
normal,
/// A [Ball] that does not alter [GameState.balls].
///
/// For example, a [Ball] spawned by Dash in the [FlutterForest].
extra,
}
/// {@template ball_blueprint}
/// [Blueprint] which cretes a ball game object.
/// {@endtemplate}
class BallBlueprint extends Blueprint<PinballGame> {
/// {@macro ball_blueprint}
BallBlueprint({
required this.position,
required this.type,
});
/// The initial position of the [Ball].
final Vector2 position;
/// {@macro ball_type}
final BallType type;
@override
void build(PinballGame gameRef) {
final baseColor = gameRef.theme.characterTheme.ballColor;
final ball = Ball(baseColor: baseColor)
..add(
BallController(type: type),
);
add(ball..initialPosition = position + Vector2(0, ball.size.y / 2));
}
}
/// {@template ball_controller} /// {@template ball_controller}
/// Controller attached to a [Ball] that handles its game related logic. /// Controller attached to a [Ball] that handles its game related logic.
/// {@endtemplate} /// {@endtemplate}
class BallController extends Component with HasGameRef<PinballGame> { class BallController extends ComponentController<Ball>
with HasGameRef<PinballGame> {
/// {@macro ball_controller} /// {@macro ball_controller}
BallController({required this.type}); BallController(Ball ball) : super(ball);
/// {@macro ball_type}
final BallType type;
/// Removes the [Ball] from a [PinballGame]; spawning a new [Ball] if /// Removes the [Ball] from a [PinballGame]; spawning a new [Ball] if
/// any are left. /// any are left.
@ -64,24 +17,9 @@ class BallController extends Component with HasGameRef<PinballGame> {
/// Triggered by [BottomWallBallContactCallback] when the [Ball] falls into /// Triggered by [BottomWallBallContactCallback] when the [Ball] falls into
/// a [BottomWall]. /// a [BottomWall].
void lost() { void lost() {
parent?.shouldRemove = true;
// TODO(alestiago): Consider adding test for this logic once we remove the
// BallX extension.
if (type != BallType.normal) return;
final bloc = gameRef.read<GameBloc>()..add(const BallLost()); final bloc = gameRef.read<GameBloc>()..add(const BallLost());
final shouldBallRespwan = !bloc.state.isLastBall && !bloc.state.isGameOver; final shouldBallRespwan = !bloc.state.isLastBall && !bloc.state.isGameOver;
if (shouldBallRespwan) {
gameRef.spawnBall();
}
}
}
/// Adds helper methods to the [Ball] if (shouldBallRespwan) gameRef.spawnBall();
extension BallX on Ball {
/// Returns the controller instance of the ball
// TODO(erickzanardo): Remove the need of an extension.
BallController get controller {
return children.whereType<BallController>().first;
} }
} }

@ -97,7 +97,7 @@ class _BottomGroupSide extends Component {
final flipper = Flipper( final flipper = Flipper(
side: _side, side: _side,
)..initialPosition = _position; )..initialPosition = _position;
await flipper.add(FlipperController(flipper)); await FlipperController(flipper).attach();
final baseboard = Baseboard(side: _side) final baseboard = Baseboard(side: _side)
..initialPosition = _position + ..initialPosition = _position +

@ -1,16 +1,17 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
/// {@template flipper_controller} /// {@template flipper_controller}
/// A [Component] that controls the [Flipper]s movement. /// A [ComponentController] that controls a [Flipper]s movement.
/// {@endtemplate} /// {@endtemplate}
class FlipperController extends Component with KeyboardHandler { class FlipperController extends ComponentController<Flipper>
with KeyboardHandler {
/// {@macro flipper_controller} /// {@macro flipper_controller}
FlipperController(this.flipper) : _keys = flipper.side.flipperKeys; FlipperController(Flipper flipper)
: _keys = flipper.side.flipperKeys,
/// The [Flipper] this controller is controlling. super(flipper);
final Flipper flipper;
/// The [LogicalKeyboardKey]s that will control the [Flipper]. /// The [LogicalKeyboardKey]s that will control the [Flipper].
/// ///
@ -25,9 +26,9 @@ class FlipperController extends Component with KeyboardHandler {
if (!_keys.contains(event.logicalKey)) return true; if (!_keys.contains(event.logicalKey)) return true;
if (event is RawKeyDownEvent) { if (event is RawKeyDownEvent) {
flipper.moveUp(); component.moveUp();
} else if (event is RawKeyUpEvent) { } else if (event is RawKeyUpEvent) {
flipper.moveDown(); component.moveDown();
} }
return false; return false;

@ -31,11 +31,10 @@ class FlutterForest extends Component
@override @override
void onNewState(GameState state) { void onNewState(GameState state) {
super.onNewState(state); super.onNewState(state);
gameRef.addFromBlueprint( gameRef.add(
BallBlueprint( Ball(
position: Vector2(17.2, 52.7), baseColor: gameRef.theme.characterTheme.ballColor,
type: BallType.extra, )..initialPosition = Vector2(17.2, 52.7),
),
); );
} }

@ -18,16 +18,23 @@ mixin ScorePoints<T extends Forge2DGame> on BodyComponent<T> {
} }
} }
/// {@template ball_score_points_callbacks}
/// Adds points to the score when a [Ball] collides with a [BodyComponent] that /// Adds points to the score when a [Ball] collides with a [BodyComponent] that
/// implements [ScorePoints]. /// implements [ScorePoints].
/// {@endtemplate}
class BallScorePointsCallback extends ContactCallback<Ball, ScorePoints> { class BallScorePointsCallback extends ContactCallback<Ball, ScorePoints> {
/// {@macro ball_score_points_callbacks}
BallScorePointsCallback(PinballGame game) : _gameRef = game;
final PinballGame _gameRef;
@override @override
void begin( void begin(
Ball ball, Ball _,
ScorePoints scorePoints, ScorePoints scorePoints,
Contact _, Contact __,
) { ) {
ball.controller.gameRef.read<GameBloc>().add( _gameRef.read<GameBloc>().add(
Scored(points: scorePoints.points), Scored(points: scorePoints.points),
); );
} }

@ -78,6 +78,13 @@ class BottomWall extends Wall {
class BottomWallBallContactCallback extends ContactCallback<Ball, BottomWall> { class BottomWallBallContactCallback extends ContactCallback<Ball, BottomWall> {
@override @override
void begin(Ball ball, BottomWall wall, Contact contact) { void begin(Ball ball, BottomWall wall, Contact contact) {
ball.controller.lost(); ball.shouldRemove = true;
// TODO(alestiago): replace with .firstChild when available.
late final BallController? controller;
final children = ball.children.whereType<BallController>();
controller = children.isEmpty ? null : children.first;
if (controller != null) controller.lost();
} }
} }

@ -68,7 +68,7 @@ class PinballGame extends Forge2DGame
} }
void _addContactCallbacks() { void _addContactCallbacks() {
addContactCallback(BallScorePointsCallback()); addContactCallback(BallScorePointsCallback(this));
addContactCallback(BottomWallBallContactCallback()); addContactCallback(BottomWallBallContactCallback());
addContactCallback(BonusLetterBallContactCallback()); addContactCallback(BonusLetterBallContactCallback());
} }
@ -101,12 +101,14 @@ class PinballGame extends Forge2DGame
} }
void spawnBall() { void spawnBall() {
addFromBlueprint( final ball = Ball(
BallBlueprint( baseColor: theme.characterTheme.ballColor,
position: plunger.body.position, )..initialPosition = Vector2(
type: BallType.normal, plunger.body.position.x,
), plunger.body.position.y + Ball.size.y,
); );
BallController(ball).attach();
add(ball);
} }
} }
@ -138,11 +140,9 @@ class DebugPinballGame extends PinballGame with TapDetector {
@override @override
void onTapUp(TapUpInfo info) { void onTapUp(TapUpInfo info) {
addFromBlueprint( add(
BallBlueprint( Ball(baseColor: const Color(0xFFFF0000))
position: info.eventPosition.game, ..initialPosition = info.eventPosition.game,
type: BallType.extra,
),
); );
} }
} }

@ -23,7 +23,7 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
} }
/// The size of the [Ball] /// The size of the [Ball]
final Vector2 size = Vector2.all(3); static final Vector2 size = Vector2.all(3);
/// The base [Color] used to tint this [Ball] /// The base [Color] used to tint this [Ball]
final Color baseColor; final Color baseColor;

Loading…
Cancel
Save