feat: spaceship drop tube (#79)

* feat: added spaceship drop ramp to board

* refactor: removed findNested extensions (#77)

* refactor: placed drop ramp below spaceship, fixed position and layers

* test: tests coverage

* chore: analysis errors

* refactor: named SpaceshipExitRail

* chore: doc

* Update lib/game/components/spaceship_exit_rail.dart

Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>

* Update test/game/components/spaceship_exit_rail_test.dart

Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>

* Update test/game/components/spaceship_exit_rail_test.dart

Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>

* refactor: changed name for spaceshipexit layer

* refactor: placed curve for spaceship exit rail

* fix: fixed drop tube sizes

* refactor: placed drop tube and fixed layers from spaceship

* fix: fixed exit hole contact size

* refactor: reordered mocks at tests

* chore: reorder params and typo

* fix: moved drop tube

* test: fixed spaceship exit test

Co-authored-by: Alejandro Santiago <dev@alestiago.com>
Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>
Co-authored-by: Erick <erickzanardoo@gmail.com>
pull/114/head
Rui Miguel Alonso 4 years ago committed by GitHub
parent e7ba6dc91d
commit f44c2bc25e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -10,4 +10,5 @@ export 'kicker.dart';
export 'launcher_ramp.dart'; export 'launcher_ramp.dart';
export 'plunger.dart'; export 'plunger.dart';
export 'score_points.dart'; export 'score_points.dart';
export 'spaceship_exit_rail.dart';
export 'wall.dart'; export 'wall.dart';

@ -114,7 +114,7 @@ class _JetpackRampOpening extends RampOpening {
final double _rotation; final double _rotation;
static final Vector2 _size = Vector2(JetpackRamp.width / 3, .1); static final Vector2 _size = Vector2(JetpackRamp.width / 4, .1);
@override @override
Shape get shape => PolygonShape() Shape get shape => PolygonShape()

@ -0,0 +1,198 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:math' as math;
import 'dart:ui';
import 'package:flame/extensions.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
/// {@template spaceship_exit_rail}
/// A [Blueprint] for the spaceship drop tube.
/// {@endtemplate}
class SpaceshipExitRail extends Forge2DBlueprint {
/// {@macro spaceship_exit_rail}
SpaceshipExitRail({required this.position});
/// The [position] where the elements will be created
final Vector2 position;
@override
void build(_) {
addAllContactCallback([
SpaceshipExitRailEndBallContactCallback(),
]);
final spaceshipExitRailRamp = _SpaceshipExitRailRamp()
..initialPosition = position;
final exitRail = SpaceshipExitRailEnd()
..initialPosition = position + _SpaceshipExitRailRamp.exitPoint;
addAll([
spaceshipExitRailRamp,
exitRail,
]);
}
}
class _SpaceshipExitRailRamp extends BodyComponent
with InitialPosition, Layered {
_SpaceshipExitRailRamp() : super(priority: 2) {
layer = Layer.spaceshipExitRail;
// TODO(ruimiguel): remove color once asset is placed.
paint = Paint()
..color = const Color.fromARGB(255, 249, 65, 3)
..style = PaintingStyle.stroke;
}
static final exitPoint = Vector2(9.2, -48.5);
List<FixtureDef> _createFixtureDefs() {
const entranceRotationAngle = 175 * math.pi / 180;
const curveRotationAngle = 275 * math.pi / 180;
const exitRotationAngle = 340 * math.pi / 180;
const width = 5.5;
final fixturesDefs = <FixtureDef>[];
final entranceWall = ArcShape(
center: Vector2(width / 2, 0),
arcRadius: width / 2,
angle: math.pi,
rotation: entranceRotationAngle,
);
final entranceFixtureDef = FixtureDef(entranceWall);
fixturesDefs.add(entranceFixtureDef);
final topLeftControlPoints = [
Vector2(0, 0),
Vector2(10, .5),
Vector2(7, 4),
Vector2(15.5, 8.3),
];
final topLeftCurveShape = BezierCurveShape(
controlPoints: topLeftControlPoints,
)..rotate(curveRotationAngle);
final topLeftFixtureDef = FixtureDef(topLeftCurveShape);
fixturesDefs.add(topLeftFixtureDef);
final topRightControlPoints = [
Vector2(0, width),
Vector2(10, 6.5),
Vector2(7, 10),
Vector2(15.5, 13.2),
];
final topRightCurveShape = BezierCurveShape(
controlPoints: topRightControlPoints,
)..rotate(curveRotationAngle);
final topRightFixtureDef = FixtureDef(topRightCurveShape);
fixturesDefs.add(topRightFixtureDef);
final mediumLeftControlPoints = [
topLeftControlPoints.last,
Vector2(21, 12.9),
Vector2(30, 7.1),
Vector2(32, 4.8),
];
final mediumLeftCurveShape = BezierCurveShape(
controlPoints: mediumLeftControlPoints,
)..rotate(curveRotationAngle);
final mediumLeftFixtureDef = FixtureDef(mediumLeftCurveShape);
fixturesDefs.add(mediumLeftFixtureDef);
final mediumRightControlPoints = [
topRightControlPoints.last,
Vector2(21, 17.2),
Vector2(30, 12.1),
Vector2(32, 10.2),
];
final mediumRightCurveShape = BezierCurveShape(
controlPoints: mediumRightControlPoints,
)..rotate(curveRotationAngle);
final mediumRightFixtureDef = FixtureDef(mediumRightCurveShape);
fixturesDefs.add(mediumRightFixtureDef);
final bottomLeftControlPoints = [
mediumLeftControlPoints.last,
Vector2(40, -1),
Vector2(48, 1.9),
Vector2(50.5, 2.5),
];
final bottomLeftCurveShape = BezierCurveShape(
controlPoints: bottomLeftControlPoints,
)..rotate(curveRotationAngle);
final bottomLeftFixtureDef = FixtureDef(bottomLeftCurveShape);
fixturesDefs.add(bottomLeftFixtureDef);
final bottomRightControlPoints = [
mediumRightControlPoints.last,
Vector2(40, 4),
Vector2(46, 6.5),
Vector2(48.8, 7.6),
];
final bottomRightCurveShape = BezierCurveShape(
controlPoints: bottomRightControlPoints,
)..rotate(curveRotationAngle);
final bottomRightFixtureDef = FixtureDef(bottomRightCurveShape);
fixturesDefs.add(bottomRightFixtureDef);
final exitWall = ArcShape(
center: exitPoint,
arcRadius: width / 2,
angle: math.pi,
rotation: exitRotationAngle,
);
final exitFixtureDef = FixtureDef(exitWall);
fixturesDefs.add(exitFixtureDef);
return fixturesDefs;
}
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition;
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
return body;
}
}
/// {@template spaceship_exit_rail_end}
/// A sensor [BodyComponent] responsible for sending the [Ball]
/// back to the board.
/// {@endtemplate}
class SpaceshipExitRailEnd extends RampOpening {
/// {@macro spaceship_exit_rail_end}
SpaceshipExitRailEnd()
: super(
pathwayLayer: Layer.spaceshipExitRail,
orientation: RampOrientation.down,
) {
layer = Layer.spaceshipExitRail;
}
@override
Shape get shape {
return CircleShape()..radius = 1;
}
}
/// [ContactCallback] that handles the contact between the [Ball]
/// and a [SpaceshipExitRailEnd].
///
/// It resets the [Ball] priority and filter data so it will "be back" on the
/// board.
class SpaceshipExitRailEndBallContactCallback
extends ContactCallback<SpaceshipExitRailEnd, Ball> {
@override
void begin(SpaceshipExitRailEnd exitRail, Ball ball, _) {
ball
..priority = 1
..gameRef.reorderChildren()
..layer = exitRail.outsideLayer;
}
}

@ -53,6 +53,13 @@ class PinballGame extends Forge2DGame
), ),
), ),
); );
unawaited(
addFromBlueprint(
SpaceshipExitRail(
position: Vector2(-34.3, 23.8),
),
),
);
// Fix camera on the center of the board. // Fix camera on the center of the board.
camera camera

@ -61,6 +61,9 @@ enum Layer {
/// Collide only with Spaceship group elements. /// Collide only with Spaceship group elements.
spaceship, spaceship,
/// Collide only with Spaceship exit rail group elements.
spaceshipExitRail,
} }
/// {@template layer_mask_bits} /// {@template layer_mask_bits}
@ -89,6 +92,8 @@ extension LayerMaskBits on Layer {
return 0x0005; return 0x0005;
case Layer.spaceship: case Layer.spaceship:
return 0x000A; return 0x000A;
case Layer.spaceshipExitRail:
return 0x0004;
} }
} }
} }

@ -32,7 +32,10 @@ class Spaceship extends Forge2DBlueprint {
SpaceshipSaucer()..initialPosition = position, SpaceshipSaucer()..initialPosition = position,
SpaceshipEntrance()..initialPosition = position, SpaceshipEntrance()..initialPosition = position,
AndroidHead()..initialPosition = position, AndroidHead()..initialPosition = position,
SpaceshipHole()..initialPosition = position - Vector2(5.2, 4.8), SpaceshipHole(
onExitLayer: Layer.spaceshipExitRail,
onExitElevation: 2,
)..initialPosition = position - Vector2(5.2, 4.8),
SpaceshipHole()..initialPosition = position - Vector2(-7.2, 0.8), SpaceshipHole()..initialPosition = position - Vector2(-7.2, 0.8),
SpaceshipWall()..initialPosition = position, SpaceshipWall()..initialPosition = position,
]); ]);
@ -44,7 +47,8 @@ class Spaceship extends Forge2DBlueprint {
/// {@endtemplate} /// {@endtemplate}
class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_saucer} /// {@macro spaceship_saucer}
SpaceshipSaucer() : super(priority: 2) { // TODO(ruimiguel): apply Elevated when PR merged.
SpaceshipSaucer() : super(priority: 3) {
layer = Layer.spaceship; layer = Layer.spaceship;
} }
@ -88,7 +92,7 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
/// {@endtemplate} /// {@endtemplate}
class AndroidHead extends BodyComponent with InitialPosition, Layered { class AndroidHead extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_bridge} /// {@macro spaceship_bridge}
AndroidHead() : super(priority: 3) { AndroidHead() : super(priority: 4) {
layer = Layer.spaceship; layer = Layer.spaceship;
} }
@ -149,6 +153,10 @@ class SpaceshipEntrance extends RampOpening {
layer = Layer.spaceship; layer = Layer.spaceship;
} }
/// Priority order for [SpaceshipHole] on enter.
// TODO(ruimiguel): apply Elevated when PR merged.
final int onEnterElevation = 4;
@override @override
Shape get shape { Shape get shape {
renderBody = false; renderBody = false;
@ -169,29 +177,31 @@ class SpaceshipEntrance extends RampOpening {
/// {@template spaceship_hole} /// {@template spaceship_hole}
/// A sensor [BodyComponent] responsible for sending the [Ball] /// A sensor [BodyComponent] responsible for sending the [Ball]
/// back to the board. /// out from the [Spaceship].
/// {@endtemplate} /// {@endtemplate}
class SpaceshipHole extends BodyComponent with InitialPosition, Layered { class SpaceshipHole extends RampOpening {
/// {@macro spaceship_hole} /// {@macro spaceship_hole}
SpaceshipHole() { SpaceshipHole({Layer? onExitLayer, this.onExitElevation = 1})
: super(
pathwayLayer: Layer.spaceship,
outsideLayer: onExitLayer,
orientation: RampOrientation.up,
) {
layer = Layer.spaceship; layer = Layer.spaceship;
} }
@override /// Priority order for [SpaceshipHole] on exit.
Body createBody() { // TODO(ruimiguel): apply Elevated when PR merged.
renderBody = false; final int onExitElevation;
final shape = ArcShape(center: Vector2(-3.5, 2), arcRadius: 6, angle: 1);
final bodyDef = BodyDef() @override
..userData = this Shape get shape {
..position = initialPosition return ArcShape(
..angle = 5.2 center: Vector2(0, 4.2),
..type = BodyType.static; arcRadius: 6,
angle: 1,
return world.createBody(bodyDef) rotation: 60 * pi / 180,
..createFixture( );
FixtureDef(shape)..isSensor = true,
);
} }
} }
@ -225,6 +235,7 @@ class _SpaceshipWallShape extends ChainShape {
/// {@endtemplate} /// {@endtemplate}
class SpaceshipWall extends BodyComponent with InitialPosition, Layered { class SpaceshipWall extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_wall} /// {@macro spaceship_wall}
// TODO(ruimiguel): apply Elevated when PR merged
SpaceshipWall() : super(priority: 4) { SpaceshipWall() : super(priority: 4) {
layer = Layer.spaceship; layer = Layer.spaceship;
} }
@ -258,7 +269,8 @@ class SpaceshipEntranceBallContactCallback
@override @override
void begin(SpaceshipEntrance entrance, Ball ball, _) { void begin(SpaceshipEntrance entrance, Ball ball, _) {
ball ball
..priority = 3 // TODO(ruimiguel): apply Elevated when PR merged.
..priority = entrance.onEnterElevation
..gameRef.reorderChildren() ..gameRef.reorderChildren()
..layer = Layer.spaceship; ..layer = Layer.spaceship;
} }
@ -267,15 +279,16 @@ class SpaceshipEntranceBallContactCallback
/// [ContactCallback] that handles the contact between the [Ball] /// [ContactCallback] that handles the contact between the [Ball]
/// and a [SpaceshipHole]. /// and a [SpaceshipHole].
/// ///
/// It resets the [Ball] priority and filter data so it will "be back" on the /// It sets the [Ball] priority and filter data so it will "be back" on the
/// board. /// board.
class SpaceshipHoleBallContactCallback class SpaceshipHoleBallContactCallback
extends ContactCallback<SpaceshipHole, Ball> { extends ContactCallback<SpaceshipHole, Ball> {
@override @override
void begin(SpaceshipHole hole, Ball ball, _) { void begin(SpaceshipHole hole, Ball ball, _) {
ball ball
..priority = 1 // TODO(ruimiguel): apply Elevated when PR merged.
..priority = hole.onExitElevation
..gameRef.reorderChildren() ..gameRef.reorderChildren()
..layer = Layer.board; ..layer = hole.outsideLayer;
} }
} }

@ -61,16 +61,20 @@ void main() {
group('SpaceshipEntranceBallContactCallback', () { group('SpaceshipEntranceBallContactCallback', () {
test('changes the ball priority on contact', () { test('changes the ball priority on contact', () {
when(() => entrance.onEnterElevation).thenReturn(3);
SpaceshipEntranceBallContactCallback().begin( SpaceshipEntranceBallContactCallback().begin(
entrance, entrance,
ball, ball,
MockContact(), MockContact(),
); );
verify(() => ball.priority = 3).called(1); verify(() => ball.priority = entrance.onEnterElevation).called(1);
}); });
test('re order the game children', () { test('re order the game children', () {
when(() => entrance.onEnterElevation).thenReturn(3);
SpaceshipEntranceBallContactCallback().begin( SpaceshipEntranceBallContactCallback().begin(
entrance, entrance,
ball, ball,
@ -83,16 +87,22 @@ void main() {
group('SpaceshipHoleBallContactCallback', () { group('SpaceshipHoleBallContactCallback', () {
test('changes the ball priority on contact', () { test('changes the ball priority on contact', () {
when(() => hole.outsideLayer).thenReturn(Layer.board);
when(() => hole.onExitElevation).thenReturn(1);
SpaceshipHoleBallContactCallback().begin( SpaceshipHoleBallContactCallback().begin(
hole, hole,
ball, ball,
MockContact(), MockContact(),
); );
verify(() => ball.priority = 1).called(1); verify(() => ball.priority = hole.onExitElevation).called(1);
}); });
test('re order the game children', () { test('re order the game children', () {
when(() => hole.outsideLayer).thenReturn(Layer.board);
when(() => hole.onExitElevation).thenReturn(1);
SpaceshipHoleBallContactCallback().begin( SpaceshipHoleBallContactCallback().begin(
hole, hole,
ball, ball,

@ -0,0 +1,60 @@
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import '../../helpers/helpers.dart';
void main() {
group('SpaceshipExitRail', () {
late PinballGame game;
late SpaceshipExitRailEnd exitRailEnd;
late Ball ball;
late Body body;
late Fixture fixture;
late Filter filterData;
setUp(() {
game = MockPinballGame();
exitRailEnd = MockSpaceshipExitRailEnd();
ball = MockBall();
body = MockBody();
when(() => ball.gameRef).thenReturn(game);
when(() => ball.body).thenReturn(body);
fixture = MockFixture();
filterData = MockFilter();
when(() => body.fixtures).thenReturn([fixture]);
when(() => fixture.filterData).thenReturn(filterData);
});
group('SpaceshipExitHoleBallContactCallback', () {
test('changes the ball priority on contact', () {
when(() => exitRailEnd.outsideLayer).thenReturn(Layer.board);
SpaceshipExitRailEndBallContactCallback().begin(
exitRailEnd,
ball,
MockContact(),
);
verify(() => ball.priority = 1).called(1);
});
test('reorders the game children', () {
when(() => exitRailEnd.outsideLayer).thenReturn(Layer.board);
SpaceshipExitRailEndBallContactCallback().begin(
exitRailEnd,
ball,
MockContact(),
);
verify(game.reorderChildren).called(1);
});
});
});
}

@ -70,6 +70,8 @@ class MockSpaceshipEntrance extends Mock implements SpaceshipEntrance {}
class MockSpaceshipHole extends Mock implements SpaceshipHole {} class MockSpaceshipHole extends Mock implements SpaceshipHole {}
class MockSpaceshipExitRailEnd extends Mock implements SpaceshipExitRailEnd {}
class MockComponentSet extends Mock implements ComponentSet {} class MockComponentSet extends Mock implements ComponentSet {}
class MockDashNestBumper extends Mock implements DashNestBumper {} class MockDashNestBumper extends Mock implements DashNestBumper {}

Loading…
Cancel
Save