refactor: implemented ContactCallbacks2

pull/202/head
alestiago 3 years ago
parent 6d92eb3f1d
commit 26ee4f7725

@ -1,5 +1,6 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/collisions.dart';
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
@ -22,8 +23,6 @@ class AlienZone extends Component with HasGameRef<PinballGame> {
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(_ControlledAlienBumperBallContactCallback());
final lowerBumper = ControlledAlienBumper.a()
..initialPosition = Vector2(-32.52, -9.34);
final upperBumper = ControlledAlienBumper.b()
@ -41,7 +40,7 @@ class AlienZone extends Component with HasGameRef<PinballGame> {
/// {@endtemplate}
@visibleForTesting
class ControlledAlienBumper extends AlienBumper
with Controls<_AlienBumperController>, ScorePoints {
with Controls<_AlienBumperController>, ScorePoints, ContactCallbacks2 {
/// {@macro controlled_alien_bumper}
ControlledAlienBumper.a() : super.a() {
controller = _AlienBumperController(this);
@ -55,6 +54,12 @@ class ControlledAlienBumper extends AlienBumper
@override
// TODO(ruimiguel): change points when get final points map.
int get points => 20;
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is Ball) controller.hit();
}
}
/// {@template alien_bumper_controller}
@ -80,16 +85,3 @@ class _AlienBumperController extends ComponentController<AlienBumper>
isActivated = !isActivated;
}
}
/// Listens when a [Ball] bounces bounces against a [AlienBumper].
class _ControlledAlienBumperBallContactCallback
extends ContactCallback<Controls<_AlienBumperController>, Ball> {
@override
void begin(
Controls<_AlienBumperController> controlledAlienBumper,
Ball _,
Contact __,
) {
controlledAlienBumper.controller.hit();
}
}

@ -1,5 +1,5 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/forge2d_game.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
@ -9,7 +9,8 @@ import 'package:pinball_theme/pinball_theme.dart';
/// {@template controlled_ball}
/// A [Ball] with a [BallController] attached.
/// {@endtemplate}
class ControlledBall extends Ball with Controls<BallController> {
class ControlledBall extends Ball<PinballGame>
with Controls<BallController>, ContactCallbacks2 {
/// A [Ball] that launches from the [Plunger].
///
/// When a launched [Ball] is lost, it will decrease the [GameState.balls]
@ -39,6 +40,14 @@ class ControlledBall extends Ball with Controls<BallController> {
controller = DebugBallController(this);
priority = Ball.boardPriority;
}
@override
void beginContact(Object other, Contact contact) {
if (other is ScorePoints) {
gameRef.read<GameBloc>().add(Scored(points: other.points));
gameRef.audio.score();
}
}
}
/// {@template ball_controller}
@ -51,11 +60,8 @@ class BallController extends ComponentController<Ball>
/// Removes the [Ball] from a [PinballGame].
///
/// Triggered by [BottomWallBallContactCallback] when the [Ball] falls into
/// a [BottomWall].
void lost() {
component.shouldRemove = true;
}
/// Triggered when the [Ball] falls into a [BottomWall].
void lost() => component.shouldRemove = true;
/// Stops the [Ball] inside of the [SparkyComputer] while the turbo charge
/// sequence runs, then boosts the ball out of the computer.

@ -18,11 +18,11 @@ class ControlledSparkyComputer extends SparkyComputer
}
@override
void build(Forge2DGame _) {
addContactCallback(SparkyTurboChargeSensorBallContactCallback());
void build(_) {
final sparkyTurboChargeSensor = SparkyTurboChargeSensor()
..initialPosition = Vector2(-13, -49.8);
add(sparkyTurboChargeSensor);
super.build(_);
}
}
@ -40,11 +40,12 @@ class SparkyComputerController
}
/// {@template sparky_turbo_charge_sensor}
/// Small sensor body used to detect when a ball has entered the
/// [SparkyComputer] with the [SparkyTurboChargeSensorBallContactCallback].
/// Small sensor body used to detect when a ball has enters the
/// [SparkyComputer].
/// {@endtemplate}
@visibleForTesting
class SparkyTurboChargeSensor extends BodyComponent with InitialPosition {
class SparkyTurboChargeSensor extends BodyComponent
with InitialPosition, ContactCallbacks2 {
/// {@macro sparky_turbo_charge_sensor}
SparkyTurboChargeSensor() {
renderBody = false;
@ -53,32 +54,18 @@ class SparkyTurboChargeSensor extends BodyComponent with InitialPosition {
@override
Body createBody() {
final shape = CircleShape()..radius = 0.1;
final fixtureDef = FixtureDef(shape)..isSensor = true;
final bodyDef = BodyDef()
..position = initialPosition
..userData = this;
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
/// {@template sparky_turbo_charge_sensor_ball_contact_callback}
/// Turbo charges the [Ball] on contact with [SparkyTurboChargeSensor].
/// {@endtemplate}
@visibleForTesting
class SparkyTurboChargeSensorBallContactCallback
extends ContactCallback<SparkyTurboChargeSensor, ControlledBall> {
/// {@macro sparky_turbo_charge_sensor_ball_contact_callback}
SparkyTurboChargeSensorBallContactCallback();
@override
void begin(
SparkyTurboChargeSensor sparkyTurboChargeSensor,
ControlledBall ball,
_,
) {
ball.controller.turboCharge();
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is ControlledBall) other.controller.turboCharge();
}
}

@ -23,8 +23,6 @@ class FlutterForest extends Component
@override
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(_DashNestBumperBallContactCallback());
final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, -58.3);
final bigNest = _BigDashNestBumper()
@ -81,27 +79,40 @@ class _FlutterForestController extends ComponentController<FlutterForest>
// TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D
// ContactCallback process is enhanced.
class _BigDashNestBumper extends BigDashNestBumper with ScorePoints {
class _BigDashNestBumper extends BigDashNestBumper
with ScorePoints, ContactCallbacks2 {
@override
int get points => 20;
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is! Ball) return;
final parent = this.parent;
if (parent is FlutterForest) {
parent.controller.activateBumper(this);
}
}
}
class _SmallDashNestBumper extends SmallDashNestBumper with ScorePoints {
class _SmallDashNestBumper extends SmallDashNestBumper
with ScorePoints, ContactCallbacks2 {
_SmallDashNestBumper.a() : super.a();
_SmallDashNestBumper.b() : super.b();
@override
int get points => 20;
}
class _DashNestBumperBallContactCallback
extends ContactCallback<DashNestBumper, Ball> {
@override
void begin(DashNestBumper dashNestBumper, _, __) {
final parent = dashNestBumper.parent;
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is! Ball) return;
final parent = this.parent;
if (parent is FlutterForest) {
parent.controller.activateBumper(dashNestBumper);
parent.controller.activateBumper(this);
}
}
}

@ -16,34 +16,33 @@ class GoogleWord extends Component
/// {@macro google_word}
GoogleWord({
required Vector2 position,
}) : _position = position {
}) : super(
children: [
_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),
],
) {
controller = _GoogleWordController(this);
}
}
final Vector2 _position;
/// Activates a [GoogleLetter] when it contacts with a [Ball].
class _GoogleLetter extends GoogleLetter with ContactCallbacks2 {
_GoogleLetter(int index) : super(index);
@override
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(_GoogleLetterBallContactCallback());
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),
];
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
final letters = <GoogleLetter>[];
for (var index = 0; index < offsets.length; index++) {
letters.add(
GoogleLetter(index)..initialPosition = _position + offsets[index],
);
}
await addAll(letters);
final parent = this.parent;
if (parent is GoogleWord) parent.controller.activate(this);
}
}
@ -69,15 +68,3 @@ class _GoogleWordController extends ComponentController<GoogleWord>
}
}
}
/// Activates a [GoogleLetter] when it contacts with a [Ball].
class _GoogleLetterBallContactCallback
extends ContactCallback<GoogleLetter, Ball> {
@override
void begin(GoogleLetter googleLetter, _, __) {
final parent = googleLetter.parent;
if (parent is GoogleWord) {
parent.controller.activate(googleLetter);
}
}
}

@ -7,7 +7,7 @@ import 'package:pinball_flame/pinball_flame.dart';
/// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and
/// [LaunchRamp].
/// {@endtemplate}
class Launcher extends Forge2DBlueprint {
class Launcher extends Blueprint {
/// {@macro launcher}
Launcher();
@ -15,7 +15,8 @@ class Launcher extends Forge2DBlueprint {
late final Plunger plunger;
@override
void build(Forge2DGame gameRef) {
// ignore: avoid_renaming_method_parameters
void build(_) {
plunger = ControlledPlunger(compressionDistance: 12.3)
..initialPosition = Vector2(40.1, 38);

@ -1,7 +1,4 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template score_points}
@ -17,24 +14,3 @@ mixin ScorePoints<T extends Forge2DGame> on BodyComponent<T> {
body.userData = this;
}
}
/// {@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 _,
ScorePoints scorePoints,
Contact __,
) {
_gameRef.read<GameBloc>().add(Scored(points: scorePoints.points));
_gameRef.audio.score();
}
}

@ -22,8 +22,6 @@ class SparkyFireZone extends Component with HasGameRef<PinballGame> {
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(_ControlledSparkyBumperBallContactCallback());
final lowerLeftBumper = ControlledSparkyBumper.a()
..initialPosition = Vector2(-23.15, -41.65);
final upperLeftBumper = ControlledSparkyBumper.b()
@ -44,7 +42,7 @@ class SparkyFireZone extends Component with HasGameRef<PinballGame> {
/// {@endtemplate}
@visibleForTesting
class ControlledSparkyBumper extends SparkyBumper
with Controls<_SparkyBumperController>, ScorePoints {
with Controls<_SparkyBumperController>, ScorePoints, ContactCallbacks2 {
///{@macro controlled_sparky_bumper}
ControlledSparkyBumper.a() : super.a() {
controller = _SparkyBumperController(this);
@ -62,6 +60,12 @@ class ControlledSparkyBumper extends SparkyBumper
@override
int get points => 20;
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is Ball) controller.hit();
}
}
/// {@template sparky_bumper_controller}
@ -88,16 +92,3 @@ class _SparkyBumperController extends ComponentController<SparkyBumper>
isActivated = !isActivated;
}
}
/// Listens when a [Ball] bounces bounces against a [SparkyBumper].
class _ControlledSparkyBumperBallContactCallback
extends ContactCallback<Controls<_SparkyBumperController>, Ball> {
@override
void begin(
Controls<_SparkyBumperController> controlledSparkyBumper,
Ball _,
Contact __,
) {
controlledSparkyBumper.controller.hit();
}
}

@ -4,6 +4,7 @@ import 'package:flame/extensions.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template wall}
/// A continuous generic and [BodyType.static] barrier that divides a game area.
@ -57,26 +58,18 @@ List<Wall> createBoundaries(Forge2DGame game) {
/// {@template bottom_wall}
/// [Wall] located at the bottom of the board.
///
/// Collisions with [BottomWall] are listened by
/// [BottomWallBallContactCallback].
/// {@endtemplate}
class BottomWall extends Wall {
class BottomWall extends Wall with ContactCallbacks2 {
/// {@macro bottom_wall}
BottomWall()
: super(
start: BoardDimensions.bounds.bottomLeft.toVector2(),
end: BoardDimensions.bounds.bottomRight.toVector2(),
);
}
/// {@template bottom_wall_ball_contact_callback}
/// Listens when a [ControlledBall] falls into a [BottomWall].
/// {@endtemplate}
class BottomWallBallContactCallback
extends ContactCallback<ControlledBall, BottomWall> {
@override
void begin(ControlledBall ball, BottomWall wall, Contact contact) {
ball.controller.lost();
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is ControlledBall) other.controller.lost();
}
}

@ -36,8 +36,6 @@ class PinballGame extends Forge2DGame
@override
Future<void> onLoad() async {
_addContactCallbacks();
unawaited(add(ScoreEffectController(this)));
unawaited(add(gameFlowController = GameFlowController(this)));
unawaited(add(CameraController(this)));
@ -69,11 +67,6 @@ class PinballGame extends Forge2DGame
await super.onLoad();
}
void _addContactCallbacks() {
addContactCallback(BallScorePointsCallback(this));
addContactCallback(BottomWallBallContactCallback());
}
Future<void> _addGameBoundaries() async {
await add(BottomWall());
createBoundaries(this).forEach(add);

@ -8,7 +8,7 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template boundaries}
/// A [Blueprint] which creates the [_BottomBoundary] and [_OuterBoundary].
///{@endtemplate boundaries}
class Boundaries extends Forge2DBlueprint {
class Boundaries extends Blueprint {
@override
void build(_) {
final bottomBoundary = _BottomBoundary();

@ -11,7 +11,7 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template dinowalls}
/// A [Blueprint] which creates walls for the [ChromeDino].
/// {@endtemplate}
class DinoWalls extends Forge2DBlueprint {
class DinoWalls extends Blueprint {
/// {@macro dinowalls}
DinoWalls();

@ -11,13 +11,9 @@ import 'package:pinball_flame/pinball_flame.dart';
/// A [Blueprint] which creates the [_LaunchRampBase] and
/// [_LaunchRampForegroundRailing].
/// {@endtemplate}
class LaunchRamp extends Forge2DBlueprint {
class LaunchRamp extends Blueprint {
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_LaunchRampExit>(),
]);
final launchRampBase = _LaunchRampBase();
final launchRampForegroundRailing = _LaunchRampForegroundRailing();

@ -2,6 +2,7 @@
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template layer_entrance_orientation}
/// Determines if a layer entrance is oriented [up] or [down] on the board.
@ -23,7 +24,8 @@ enum LayerEntranceOrientation {
/// By default the base [layer] is set to [Layer.board] and the
/// [outsidePriority] is set to the lowest possible [Layer].
/// {@endtemplate}
abstract class LayerSensor extends BodyComponent with InitialPosition, Layered {
abstract class LayerSensor extends BodyComponent
with InitialPosition, Layered, ContactCallbacks2 {
/// {@macro layer_sensor}
LayerSensor({
required Layer insideLayer,
@ -75,35 +77,30 @@ abstract class LayerSensor extends BodyComponent with InitialPosition, Layered {
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
/// {@template layer_sensor_ball_contact_callback}
/// Detects when a [Ball] enters or exits a [Layer] through a [LayerSensor].
///
/// Modifies [Ball]'s [Layer] and render priority depending on whether the
/// [Ball] is on or outside of a [Layer].
/// {@endtemplate}
class LayerSensorBallContactCallback<LayerEntrance extends LayerSensor>
extends ContactCallback<Ball, LayerEntrance> {
@override
void begin(Ball ball, LayerEntrance layerEntrance, Contact _) {
if (ball.layer != layerEntrance.insideLayer) {
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is! Ball) return;
final ball = other;
if (ball.layer != insideLayer) {
final isBallEnteringOpening =
(layerEntrance.orientation == LayerEntranceOrientation.down &&
(orientation == LayerEntranceOrientation.down &&
ball.body.linearVelocity.y < 0) ||
(layerEntrance.orientation == LayerEntranceOrientation.up &&
(orientation == LayerEntranceOrientation.up &&
ball.body.linearVelocity.y > 0);
if (isBallEnteringOpening) {
ball
..layer = layerEntrance.insideLayer
..priority = layerEntrance.insidePriority
..layer = insideLayer
..priority = insidePriority
..reorderChildren();
}
} else {
ball
..layer = layerEntrance.outsideLayer
..priority = layerEntrance.outsidePriority
..layer = outsideLayer
..priority = outsidePriority
..reorderChildren();
}
}

@ -9,7 +9,7 @@ import 'package:pinball_flame/pinball_flame.dart';
/// A [Blueprint] which creates the pair of [Slingshot]s on the right side of
/// the board.
/// {@endtemplate}
class Slingshots extends Forge2DBlueprint {
class Slingshots extends Blueprint {
@override
void build(_) {
final upperSlingshot = Slingshot(

@ -12,7 +12,7 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship}
/// A [Blueprint] which creates the spaceship feature.
/// {@endtemplate}
class Spaceship extends Forge2DBlueprint {
class Spaceship extends Blueprint {
/// {@macro spaceship}
Spaceship({required this.position});
@ -24,11 +24,6 @@ class Spaceship extends Forge2DBlueprint {
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_SpaceshipEntrance>(),
LayerSensorBallContactCallback<_SpaceshipHole>(),
]);
addAll([
SpaceshipSaucer()..initialPosition = position,
_SpaceshipEntrance()..initialPosition = position,

@ -11,16 +11,12 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_rail}
/// A [Blueprint] for the spaceship drop tube.
/// {@endtemplate}
class SpaceshipRail extends Forge2DBlueprint {
class SpaceshipRail extends Blueprint {
/// {@macro spaceship_rail}
SpaceshipRail();
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_SpaceshipRailExit>(),
]);
final railRamp = _SpaceshipRailRamp();
final railEnd = _SpaceshipRailExit();
final topBase = _SpaceshipRailBase(radius: 0.55)

@ -11,16 +11,12 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_ramp}
/// A [Blueprint] which creates the ramp leading into the [Spaceship].
/// {@endtemplate}
class SpaceshipRamp extends Forge2DBlueprint {
class SpaceshipRamp extends Blueprint {
/// {@macro spaceship_ramp}
SpaceshipRamp();
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_SpaceshipRampOpening>(),
]);
final rightOpening = _SpaceshipRampOpening(
// TODO(ruimiguel): set Board priority when defined.
outsidePriority: 1,

@ -9,7 +9,7 @@ import 'package:pinball_flame/pinball_flame.dart';
/// A [Blueprint] which creates the [_ComputerBase] and
/// [_ComputerTopSpriteComponent].
/// {@endtemplate}
class SparkyComputer extends Forge2DBlueprint {
class SparkyComputer extends Blueprint {
@override
void build(_) {
final computerBase = _ComputerBase();

@ -17,7 +17,7 @@ class GoogleLetterGame extends BasicBallGame {
@override
Future<void> onLoad() async {
await super.onLoad();
addContactCallback(_BallGoogleLetterContactCallback());
world.setContactListener(_WorldContactListener());
camera.followVector2(Vector2.zero());
await add(GoogleLetter(0));
@ -26,11 +26,21 @@ class GoogleLetterGame extends BasicBallGame {
}
}
class _BallGoogleLetterContactCallback
extends ContactCallback<Ball, GoogleLetter> {
class _WorldContactListener extends ContactListener {
@override
void begin(Ball<Forge2DGame> a, GoogleLetter b, Contact contact) {
super.begin(a, b, contact);
b.activate();
void beginContact(Contact contact) {
[
contact.bodyA.userData,
contact.bodyB.userData,
].whereType<GoogleLetter>().first.activate();
}
@override
void endContact(Contact contact) {}
@override
void postSolve(Contact contact, ContactImpulse impulse) {}
@override
void preSolve(Contact contact, Manifold oldManifold) {}
}

@ -61,36 +61,6 @@ abstract class Blueprint<T extends FlameGame> extends Component {
List<Blueprint> get blueprints => List.unmodifiable(_blueprints);
}
/// A [Blueprint] that provides additional
/// structures specific to flame_forge2d
abstract class Forge2DBlueprint extends Blueprint<Forge2DGame> {
final List<ContactCallback> _callbacks = [];
/// Adds a single [ContactCallback] to this blueprint
void addContactCallback(ContactCallback callback) {
assert(!_isAttached, _attachedErrorMessage);
_callbacks.add(callback);
}
/// Adds a collection of [ContactCallback]s to this blueprint
void addAllContactCallback(List<ContactCallback> callbacks) {
assert(!_isAttached, _attachedErrorMessage);
_callbacks.addAll(callbacks);
}
@override
Future<void> attach(Forge2DGame game) async {
await super.attach(game);
for (final callback in _callbacks) {
game.addContactCallback(callback);
}
}
/// Returns a copy of the callbacks built by this blueprint
List<ContactCallback> get callbacks => List.unmodifiable(_callbacks);
}
/// Adds helper methods regardin [Blueprint]s to [FlameGame]
extension FlameGameBlueprint on FlameGame {
/// Shortcut to attach a [Blueprint] instance to this game

Loading…
Cancel
Save