refactor: changed name for ramp area

pull/40/head
RuiAlonso 4 years ago
parent 30636b2bd3
commit 3a6435cb29

@ -6,17 +6,17 @@ import 'package:pinball/game/game.dart';
/// A solid, [BodyType.dynamic] sphere that rolls and bounces along the
/// [PinballGame].
/// {@endtemplate}
class Ball extends BodyComponent<PinballGame> with Layer {
class Ball extends BodyComponent<PinballGame> with Layered {
/// {@macro ball}
Ball({
required Vector2 position,
RampType? layer,
Layer? layer,
}) : _position = position,
_layer = layer ?? RampType.all;
_layer = layer ?? Layer.all;
/// The initial position of the [Ball] body.
final Vector2 _position;
final RampType _layer;
final Layer _layer;
/// The size of the [Ball]
final Vector2 size = Vector2.all(2);

@ -2,10 +2,10 @@ export 'ball.dart';
export 'baseboard.dart';
export 'board_side.dart';
export 'bonus_word.dart';
export 'crossing_ramp.dart';
export 'flipper.dart';
export 'jetpack_ramp.dart';
export 'joint_anchor.dart';
export 'layer.dart';
export 'pathway.dart';
export 'plunger.dart';
export 'round_bumper.dart';

@ -35,7 +35,7 @@ class JetpackRamp extends Component with HasGameRef<PinballGame> {
radius: _radius,
angle: _angle,
rotation: _rotation,
categoryBits: RampType.jetpack.maskBits,
layer: Layer.jetpack,
),
);
@ -59,9 +59,7 @@ class JetpackRamp extends Component with HasGameRef<PinballGame> {
}
/// {@template jetpack_ramp_opening}
/// Implementation of [RampOpening] for sensors in [JetpackRamp].
///
/// [RampOpening] with [RampType.jetpack] to filter [Ball]s collisions
/// [RampOpening] with [Layer.jetpack] to filter [Ball] collisions
/// inside [JetpackRamp].
/// {@endtemplate}
class JetpackRampOpening extends RampOpening {
@ -74,7 +72,7 @@ class JetpackRampOpening extends RampOpening {
_orientation = orientation,
super(
position: position,
layer: RampType.jetpack,
layer: Layer.jetpack,
);
/// Orientation of entrance/exit of [JetpackRamp] where
@ -102,8 +100,8 @@ class JetpackRampOpening extends RampOpening {
}
/// {@template jetpack_ramp_opening_ball_contact_callback}
/// Implementation of [RampOpeningBallContactCallback] to listen when a [Ball]
/// gets into a [JetpackRampOpening].
/// Detects when a [Ball] enters or exits the [JetpackRamp] through a
/// [JetpackRampOpening].
/// {@endtemplate}
class JetpackRampOpeningBallContactCallback
extends RampOpeningBallContactCallback<JetpackRampOpening> {

@ -3,15 +3,16 @@
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/game/game.dart';
/// {@template layer}
/// Modifies maskBits of [BodyComponent] for collisions.
/// Modifies maskBits of [BodyComponent] to control what other bodies it can
/// have physical interactions with.
///
/// Changes the [Filter] data for category and maskBits of
/// the [BodyComponent] to collide with other objects of
/// same bits and ignore others.
/// Changes the [Filter] data for category and maskBits of the [BodyComponent]
/// so it will only collide with bodies having the same bit value and ignore
/// bodies with a different bit value.
/// {@endtemplate}
mixin Layer on BodyComponent<PinballGame> {
void setLayer(RampType layer) {
mixin Layered<T extends Forge2DGame> on BodyComponent<T> {
/// Sets [Filter] category and mask bits for the [BodyComponent]
set layer(Layer layer) {
for (final fixture in body.fixtures) {
fixture
..filterData.categoryBits = layer.maskBits
@ -20,21 +21,11 @@ mixin Layer on BodyComponent<PinballGame> {
}
}
/// Indicates a orientation of the ramp entrance/exit.
/// Indicates the type of a layer.
///
/// Used to know if ramps are looking up or down of the board.
enum RampOrientation {
/// Looking up of the board.
up,
/// Looking down of the board.
down,
}
/// Indicates a type of the ramp.
///
/// Used to set the maskBits of the ramp to determine their possible collisions.
enum RampType {
/// Each layer type is associated with a maskBits value to define possible
/// collisions within that plane.
enum Layer {
/// Collide with all elements.
all,
@ -45,37 +36,43 @@ enum RampType {
sparky,
}
/// Utility methods for [RampType].
extension RampTypeX on RampType {
/// Mask of bits for each [RampType].
int get maskBits => _getRampMaskBits(this);
/// Mask of bits for each [RampType].
int _getRampMaskBits(RampType type) {
switch (type) {
case RampType.all:
return Filter().maskBits;
case RampType.jetpack:
return 0x010;
case RampType.sparky:
/// Utility methods for [Layer].
extension LayerX on Layer {
/// Mask of bits for each [Layer].
int get maskBits {
switch (this) {
case Layer.all:
return 0xFFFF;
case Layer.jetpack:
return 0x0010;
case Layer.sparky:
return 0x0100;
}
}
}
/// Indicates the orientation of a ramp entrance/exit.
///
/// Used to know if a ramp is facing up or down on the board.
enum RampOrientation {
/// Facing up on the board.
up,
/// Facing down on the board.
down,
}
/// {@template ramp_opening}
/// [BodyComponent] located at the entrance and exit of a ramp.
///
/// [RampOpeningBallContactCallback] detects when a [Ball] passes
/// through this opening.
/// Collisions with [RampOpening] are listened
/// by [RampOpeningBallContactCallback].
/// {@endtemplate}
abstract class RampOpening extends BodyComponent {
/// {@macro ramp_opening}
RampOpening({
required Vector2 position,
required RampType layer,
required Layer layer,
}) : _position = position,
_layer = layer {
// TODO(ruialonso): remove paint color for BodyComponent.
@ -83,10 +80,10 @@ abstract class RampOpening extends BodyComponent {
}
final Vector2 _position;
final RampType _layer;
final Layer _layer;
/// Mask of category bits for collision with [RampOpening]
RampType get layer => _layer;
Layer get layer => _layer;
/// The [Shape] of the [RampOpening]
Shape get shape;
@ -112,51 +109,52 @@ abstract class RampOpening extends BodyComponent {
/// {@template ramp_opening_ball_contact_callback}
/// Detects when a [Ball] enters or exits a [Pathway] ramp through a
/// [RampOpening].
/// Modifies [Ball]'s maskBits while is inside the ramp. When [Ball] exits,
///
/// Modifies [Ball]'s maskBits while it is inside the ramp. When [Ball] exits,
/// sets maskBits to collide with all elements.
/// {@endtemplate}
abstract class RampOpeningBallContactCallback<Area extends RampOpening>
extends ContactCallback<Ball, Area> {
abstract class RampOpeningBallContactCallback<Opening extends RampOpening>
extends ContactCallback<Ball, Opening> {
/// Collection of balls inside ramp pathway.
Set get ballsInside;
@override
void begin(
Ball ball,
Area area,
Opening opening,
Contact _,
) {
RampType layer;
Layer layer;
if (!ballsInside.contains(ball)) {
layer = area.layer;
layer = opening.layer;
ballsInside.add(ball);
} else {
layer = RampType.all;
layer = Layer.all;
ballsInside.remove(ball);
}
ball.setLayer(layer);
ball.layer = layer;
}
@override
void end(Ball ball, Area area, Contact contact) {
RampType? layer;
void end(Ball ball, Opening opening, Contact contact) {
Layer? layer;
switch (area.orientation) {
switch (opening.orientation) {
case RampOrientation.up:
if (ball.body.position.y > area._position.y) {
layer = RampType.all;
if (ball.body.position.y > opening._position.y) {
layer = Layer.all;
}
break;
case RampOrientation.down:
if (ball.body.position.y < area._position.y) {
layer = RampType.all;
if (ball.body.position.y < opening._position.y) {
layer = Layer.all;
}
break;
}
if (layer != null) {
ball.setLayer(layer);
ball.layer = layer;
}
}
}

@ -2,6 +2,7 @@ import 'package:flame/extensions.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:geometry/geometry.dart';
import 'package:pinball/game/components/components.dart';
/// {@template pathway}
/// [Pathway] creates lines of various shapes.
@ -14,10 +15,10 @@ class Pathway extends BodyComponent {
Color? color,
required Vector2 position,
required List<List<Vector2>> paths,
int? categoryBits,
Layer? layer,
}) : _position = position,
_paths = paths,
_categoryBits = categoryBits ?? Filter().categoryBits {
_layer = layer {
paint = Paint()
..color = color ?? const Color.fromARGB(0, 0, 0, 0)
..style = PaintingStyle.stroke;
@ -38,7 +39,7 @@ class Pathway extends BodyComponent {
required double width,
double rotation = 0,
bool singleWall = false,
int? categoryBits,
Layer? layer,
}) {
final paths = <List<Vector2>>[];
@ -61,7 +62,7 @@ class Pathway extends BodyComponent {
color: color,
position: position,
paths: paths,
categoryBits: categoryBits,
layer: layer,
);
}
@ -87,7 +88,7 @@ class Pathway extends BodyComponent {
required double angle,
double rotation = 0,
bool singleWall = false,
int? categoryBits,
Layer? layer,
}) {
final paths = <List<Vector2>>[];
@ -114,7 +115,7 @@ class Pathway extends BodyComponent {
color: color,
position: position,
paths: paths,
categoryBits: categoryBits,
layer: layer,
);
}
@ -134,7 +135,7 @@ class Pathway extends BodyComponent {
required double width,
double rotation = 0,
bool singleWall = false,
int? categoryBits,
Layer? layer,
}) {
final paths = <List<Vector2>>[];
@ -157,13 +158,13 @@ class Pathway extends BodyComponent {
color: color,
position: position,
paths: paths,
categoryBits: categoryBits,
layer: layer,
);
}
Vector2 _position;
List<List<Vector2>> _paths;
int _categoryBits;
Layer? _layer;
@override
Body createBody() {
@ -179,7 +180,8 @@ class Pathway extends BodyComponent {
);
final fixtureDef = FixtureDef(chain);
body.createFixture(fixtureDef).filterData.categoryBits = _categoryBits;
body.createFixture(fixtureDef).filterData.categoryBits =
_layer?.maskBits ?? Filter().categoryBits;
}
return body;

@ -32,7 +32,7 @@ class SparkyRamp extends Component with HasGameRef<PinballGame> {
radius: _radius,
angle: _angle,
width: _width,
categoryBits: RampType.sparky.maskBits,
layer: Layer.sparky,
),
);
await add(
@ -54,9 +54,7 @@ class SparkyRamp extends Component with HasGameRef<PinballGame> {
}
/// {@template sparky_ramp_opening}
/// Implementation of [RampOpening] for sensors in [SparkyRamp].
///
/// [RampOpening] with [RampType.sparky] to filter [Ball]s collisions
/// [RampOpening] with [Layer.sparky] to filter [Ball]s collisions
/// inside [SparkyRamp].
/// {@endtemplate}
class SparkyRampOpening extends RampOpening {
@ -69,7 +67,7 @@ class SparkyRampOpening extends RampOpening {
_orientation = orientation,
super(
position: position,
layer: RampType.sparky,
layer: Layer.sparky,
);
/// Orientation of entrance/exit of [SparkyRamp] where
@ -97,8 +95,8 @@ class SparkyRampOpening extends RampOpening {
}
/// {@template sparky_ramp_opening_ball_contact_callback}
/// Implementation of [RampOpeningBallContactCallback] to listen when a [Ball]
/// gets into a [SparkyRampOpening].
/// Detects when a [Ball] enters or exits the [SparkyRamp] through a
/// [SparkyRampOpening].
/// {@endtemplate}
class SparkyRampOpeningBallContactCallback
extends RampOpeningBallContactCallback<SparkyRampOpening> {

@ -172,7 +172,7 @@ void main() {
flameTester.test(
'modifies layer correctly',
(game) async {
const newLayer = RampType.jetpack;
const newLayer = Layer.jetpack;
final ball = Ball(position: Vector2.zero());
await game.ensureAdd(ball);
@ -182,7 +182,7 @@ void main() {
expect(fixture.filterData.categoryBits, equals(1));
expect(fixture.filterData.maskBits, equals(Filter().maskBits));
ball.setLayer(newLayer);
ball.layer = newLayer;
expect(fixture.filterData.categoryBits, equals(newLayer.maskBits));
expect(fixture.filterData.maskBits, equals(newLayer.maskBits));

@ -80,7 +80,7 @@ void main() {
});
});
group('JetpackRampArea', () {
group('JetpackRampOpening', () {
flameTester.test(
'orientation is down',
(game) async {
@ -97,7 +97,7 @@ void main() {
);
});
group('JetpackRampAreaCallback', () {
group('JetpackRampOpeningBallContactCallback', () {
test('has no ball inside on creation', () {
expect(
JetpackRampOpeningBallContactCallback().ballsInside,

@ -7,11 +7,11 @@ import 'package:pinball/game/game.dart';
import '../../helpers/helpers.dart';
class TestRampArea extends RampOpening {
TestRampArea({
class TestRampOpening extends RampOpening {
TestRampOpening({
required Vector2 position,
required RampOrientation orientation,
required RampType layer,
required Layer layer,
}) : _orientation = orientation,
super(position: position, layer: layer);
@ -30,9 +30,9 @@ class TestRampArea extends RampOpening {
]);
}
class TestRampAreaCallback
extends RampOpeningBallContactCallback<TestRampArea> {
TestRampAreaCallback() : super();
class TestRampOpeningBallContactCallback
extends RampOpeningBallContactCallback<TestRampOpening> {
TestRampOpeningBallContactCallback() : super();
final _ballsInside = <Ball>{};
@ -41,37 +41,43 @@ class TestRampAreaCallback
}
void main() {
group('RampType', () {
group('Layer', () {
test('has three values', () {
expect(RampType.values.length, equals(3));
expect(Layer.values.length, equals(3));
});
});
group('RampTypeX', () {
group('LayerX', () {
test('all types are different', () {
expect(Layer.all.maskBits, isNot(equals(Layer.jetpack.maskBits)));
expect(Layer.jetpack.maskBits, isNot(equals(Layer.sparky.maskBits)));
expect(Layer.sparky.maskBits, isNot(equals(Layer.all.maskBits)));
});
test('all type has default maskBits', () {
expect(RampType.all.maskBits, equals(Filter().maskBits));
expect(Layer.all.maskBits, equals(0xFFFF));
});
test('jetpack type has 0x010 maskBits', () {
expect(RampType.jetpack.maskBits, equals(0x010));
expect(Layer.jetpack.maskBits, equals(0x0010));
});
test('sparky type has 0x0100 maskBits', () {
expect(RampType.sparky.maskBits, equals(0x0100));
expect(Layer.sparky.maskBits, equals(0x0100));
});
});
group('RampArea', () {
group('RampOpening', () {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(PinballGameTest.create);
flameTester.test(
'loads correctly',
(game) async {
final ramp = TestRampArea(
final ramp = TestRampOpening(
position: Vector2.zero(),
orientation: RampOrientation.down,
layer: RampType.all,
layer: Layer.all,
);
await game.ready();
await game.ensureAdd(ramp);
@ -85,10 +91,10 @@ void main() {
'positions correctly',
(game) async {
final position = Vector2.all(10);
final ramp = TestRampArea(
final ramp = TestRampOpening(
position: position,
orientation: RampOrientation.down,
layer: RampType.all,
layer: Layer.all,
);
await game.ensureAdd(ramp);
@ -100,10 +106,10 @@ void main() {
flameTester.test(
'is static',
(game) async {
final ramp = TestRampArea(
final ramp = TestRampOpening(
position: Vector2.zero(),
orientation: RampOrientation.down,
layer: RampType.all,
layer: Layer.all,
);
await game.ensureAdd(ramp);
@ -111,13 +117,13 @@ void main() {
},
);
group('fixtures', () {
const layer = RampType.jetpack;
group('fixture', () {
const layer = Layer.jetpack;
flameTester.test(
'exists',
(game) async {
final ramp = TestRampArea(
final ramp = TestRampOpening(
position: Vector2.zero(),
orientation: RampOrientation.down,
layer: layer,
@ -131,7 +137,7 @@ void main() {
flameTester.test(
'shape is a polygon',
(game) async {
final ramp = TestRampArea(
final ramp = TestRampOpening(
position: Vector2.zero(),
orientation: RampOrientation.down,
layer: layer,
@ -146,7 +152,7 @@ void main() {
flameTester.test(
'is sensor',
(game) async {
final ramp = TestRampArea(
final ramp = TestRampOpening(
position: Vector2.zero(),
orientation: RampOrientation.down,
layer: layer,
@ -161,7 +167,7 @@ void main() {
flameTester.test(
'sets filter categoryBits correctly',
(game) async {
final ramp = TestRampArea(
final ramp = TestRampOpening(
position: Vector2.zero(),
orientation: RampOrientation.down,
layer: layer,
@ -180,20 +186,20 @@ void main() {
});
});
group('RampAreaCallback', () {
const layer = RampType.jetpack;
group('RampOpeningBallContactCallback', () {
const layer = Layer.jetpack;
test(
'a ball enters from bottom into a down oriented path and keeps inside, '
'is saved into collection and set maskBits to path', () {
final ball = MockBall();
final body = MockBody();
final area = TestRampArea(
final area = TestRampOpening(
position: Vector2(0, 10),
orientation: RampOrientation.down,
layer: layer,
);
final callback = TestRampAreaCallback();
final callback = TestRampOpeningBallContactCallback();
when(() => body.position).thenReturn(Vector2(0, 20));
when(() => ball.body).thenReturn(body);
@ -203,11 +209,11 @@ void main() {
expect(callback._ballsInside.length, equals(1));
expect(callback._ballsInside.first, ball);
verify(() => ball.setLayer(layer)).called(1);
verify(() => ball.layer = layer).called(1);
callback.end(ball, area, MockContact());
verifyNever(() => ball.setLayer(RampType.all));
verifyNever(() => ball.layer = Layer.all);
});
test(
@ -215,12 +221,12 @@ void main() {
'is saved into collection and set maskBits to path', () {
final ball = MockBall();
final body = MockBody();
final area = TestRampArea(
final area = TestRampOpening(
position: Vector2(0, 10),
orientation: RampOrientation.up,
layer: layer,
);
final callback = TestRampAreaCallback();
final callback = TestRampOpeningBallContactCallback();
when(() => body.position).thenReturn(Vector2.zero());
when(() => ball.body).thenReturn(body);
@ -230,11 +236,11 @@ void main() {
expect(callback._ballsInside.length, equals(1));
expect(callback._ballsInside.first, ball);
verify(() => ball.setLayer(layer)).called(1);
verify(() => ball.layer = layer).called(1);
callback.end(ball, area, MockContact());
verifyNever(() => ball.setLayer(RampType.all));
verifyNever(() => ball.layer = Layer.all);
});
test(
@ -242,12 +248,12 @@ void main() {
'is removed from collection and set maskBits to collide all', () {
final ball = MockBall();
final body = MockBody();
final area = TestRampArea(
final area = TestRampOpening(
position: Vector2(0, 10),
orientation: RampOrientation.down,
layer: layer,
);
final callback = TestRampAreaCallback();
final callback = TestRampOpeningBallContactCallback();
when(() => body.position).thenReturn(Vector2.zero());
when(() => ball.body).thenReturn(body);
@ -257,11 +263,11 @@ void main() {
expect(callback._ballsInside.length, equals(1));
expect(callback._ballsInside.first, ball);
verify(() => ball.setLayer(layer)).called(1);
verify(() => ball.layer = layer).called(1);
callback.end(ball, area, MockContact());
verify(() => ball.setLayer(RampType.all));
verify(() => ball.layer = Layer.all);
});
test(
@ -269,12 +275,13 @@ void main() {
'is removed from collection and set maskBits to collide all', () {
final ball = MockBall();
final body = MockBody();
final area = TestRampArea(
final area = TestRampOpening(
position: Vector2(0, 10),
orientation: RampOrientation.down,
layer: layer,
);
final callback = TestRampAreaCallback()..ballsInside.add(ball);
final callback = TestRampOpeningBallContactCallback()
..ballsInside.add(ball);
when(() => body.position).thenReturn(Vector2.zero());
when(() => ball.body).thenReturn(body);
@ -282,11 +289,11 @@ void main() {
callback.begin(ball, area, MockContact());
expect(callback._ballsInside.isEmpty, isTrue);
verify(() => ball.setLayer(RampType.all)).called(1);
verify(() => ball.layer = Layer.all).called(1);
callback.end(ball, area, MockContact());
verify(() => ball.setLayer(RampType.all)).called(1);
verify(() => ball.layer = Layer.all).called(1);
});
test(
@ -294,12 +301,13 @@ void main() {
'is removed from collection and set maskBits to collide all', () {
final ball = MockBall();
final body = MockBody();
final area = TestRampArea(
final area = TestRampOpening(
position: Vector2(0, 10),
orientation: RampOrientation.up,
layer: layer,
);
final callback = TestRampAreaCallback()..ballsInside.add(ball);
final callback = TestRampOpeningBallContactCallback()
..ballsInside.add(ball);
when(() => body.position).thenReturn(Vector2(0, 20));
when(() => ball.body).thenReturn(body);
@ -307,11 +315,11 @@ void main() {
callback.begin(ball, area, MockContact());
expect(callback._ballsInside.isEmpty, isTrue);
verify(() => ball.setLayer(RampType.all)).called(1);
verify(() => ball.layer = Layer.all).called(1);
callback.end(ball, area, MockContact());
verify(() => ball.setLayer(RampType.all)).called(1);
verify(() => ball.layer = Layer.all).called(1);
});
});
}

@ -176,13 +176,13 @@ void main() {
flameTester.test(
'sets filter categoryBits correctly',
(game) async {
const maskBits = 1234;
const layer = Layer.jetpack;
final pathway = Pathway.straight(
position: Vector2.zero(),
start: Vector2(10, 10),
end: Vector2(20, 20),
width: width,
categoryBits: maskBits,
layer: layer,
);
await game.ready();
await game.ensureAdd(pathway);
@ -191,7 +191,7 @@ void main() {
expect(fixture, isA<Fixture>());
expect(
fixture.filterData.categoryBits,
equals(maskBits),
equals(layer.maskBits),
);
}
},

@ -80,7 +80,7 @@ void main() {
});
});
group('SparkyRampArea', () {
group('SparkyRampOpening', () {
flameTester.test(
'orientation is down',
(game) async {
@ -97,7 +97,7 @@ void main() {
);
});
group('SparkyRampAreaCallback', () {
group('SparkyRampOpeningBallContactCallback', () {
test('has no ball inside on creation', () {
expect(
SparkyRampOpeningBallContactCallback().ballsInside,

Loading…
Cancel
Save