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