mirror of https://github.com/flutter/pinball.git
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
parent
e7ba6dc91d
commit
f44c2bc25e
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in new issue