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:pinball/flame/flame.dart';
import 'package:pinball/game/game.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}
/// Controller attached to a [Ball] that handles its game related logic.
/// {@endtemplate}
class BallController extends Component with HasGameRef<PinballGame> {
class BallController extends ComponentController<Ball>
with HasGameRef<PinballGame> {
/// {@macro ball_controller}
BallController({required this.type});
/// {@macro ball_type}
final BallType type;
BallController(Ball ball) : super(ball);
/// Removes the [Ball] from a [PinballGame]; spawning a new [Ball] if
/// any are left.
@ -64,24 +17,9 @@ class BallController extends Component with HasGameRef<PinballGame> {
/// Triggered by [BottomWallBallContactCallback] when the [Ball] falls into
/// a [BottomWall].
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 shouldBallRespwan = !bloc.state.isLastBall && !bloc.state.isGameOver;
if (shouldBallRespwan) {
gameRef.spawnBall();
}
}
}
/// Adds helper methods to the [Ball]
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;
if (shouldBallRespwan) gameRef.spawnBall();
}
}

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

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

@ -31,11 +31,10 @@ class FlutterForest extends Component
@override
void onNewState(GameState state) {
super.onNewState(state);
gameRef.addFromBlueprint(
BallBlueprint(
position: Vector2(17.2, 52.7),
type: BallType.extra,
),
gameRef.add(
Ball(
baseColor: gameRef.theme.characterTheme.ballColor,
)..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
/// implements [ScorePoints].
/// {@endtemplate}
class BallScorePointsCallback extends ContactCallback<Ball, ScorePoints> {
/// {@macro ball_score_points_callbacks}
BallScorePointsCallback(PinballGame game) : _gameRef = game;
final PinballGame _gameRef;
@override
void begin(
Ball ball,
Ball _,
ScorePoints scorePoints,
Contact _,
Contact __,
) {
ball.controller.gameRef.read<GameBloc>().add(
_gameRef.read<GameBloc>().add(
Scored(points: scorePoints.points),
);
}

@ -78,6 +78,13 @@ class BottomWall extends Wall {
class BottomWallBallContactCallback extends ContactCallback<Ball, BottomWall> {
@override
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() {
addContactCallback(BallScorePointsCallback());
addContactCallback(BallScorePointsCallback(this));
addContactCallback(BottomWallBallContactCallback());
addContactCallback(BonusLetterBallContactCallback());
}
@ -101,12 +101,14 @@ class PinballGame extends Forge2DGame
}
void spawnBall() {
addFromBlueprint(
BallBlueprint(
position: plunger.body.position,
type: BallType.normal,
),
);
final ball = Ball(
baseColor: theme.characterTheme.ballColor,
)..initialPosition = Vector2(
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
void onTapUp(TapUpInfo info) {
addFromBlueprint(
BallBlueprint(
position: info.eventPosition.game,
type: BallType.extra,
),
add(
Ball(baseColor: const Color(0xFFFF0000))
..initialPosition = info.eventPosition.game,
);
}
}

@ -23,7 +23,7 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
}
/// 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]
final Color baseColor;

Loading…
Cancel
Save