refactor: priority layer (#83)

* refactor: removed findNested extensions (#77)

* feat: added new Elevation for manage priority of BodyComponents

* refactor: changed ball with Elevated

* refactor: changed spaceship with Elevated

* test: tests for Elevated

* test: fix tests

* chore: removed unused

* test: fixed tests

* refactor: changed Elevated mixin to Priority extension

* test: fixed priority test changes

* chore: ignore for file

* test: fixed tests for PriorityX

* chore: unused import

* chore: removed unecessary ignore

* refactor: removed unnecessary comma

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

* refactor: fixed grammatical error in test named

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

* refactor: moved priority set to constructors

* chore: tests names

* doc: doc priority

* refactor: moved priority to flame dir

* feat: added priority to rampopening

* fix: fixed priority changes

* test: fixed tests for spaceship priority changes

* fix: fixed tests for priority

* chore: renamed pathwayLayer and pathwayPriority to inside

* Update packages/pinball_components/lib/src/components/ramp_opening.dart

Co-authored-by: Alejandro Santiago <dev@alestiago.com>

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

@ -107,7 +107,7 @@ class _JetpackRampOpening extends RampOpening {
required double rotation,
}) : _rotation = rotation,
super(
pathwayLayer: Layer.jetpack,
insideLayer: Layer.jetpack,
outsideLayer: outsideLayer,
orientation: RampOrientation.down,
);

@ -122,7 +122,7 @@ class _LauncherRampOpening extends RampOpening {
required double rotation,
}) : _rotation = rotation,
super(
pathwayLayer: Layer.launcher,
insideLayer: Layer.launcher,
orientation: RampOrientation.down,
);

@ -169,7 +169,7 @@ class SpaceshipExitRailEnd extends RampOpening {
/// {@macro spaceship_exit_rail_end}
SpaceshipExitRailEnd()
: super(
pathwayLayer: Layer.spaceshipExitRail,
insideLayer: Layer.spaceshipExitRail,
orientation: RampOrientation.down,
) {
layer = Layer.spaceshipExitRail;

@ -20,28 +20,41 @@ enum RampOrientation {
/// [RampOpeningBallContactCallback] detects when a [Ball] passes
/// through this opening.
///
/// By default the base [layer] is set to [Layer.board].
/// By default the base [layer] is set to [Layer.board] and the
/// [outsidePriority] is set to the lowest possible [Layer].
/// {@endtemplate}
// TODO(ruialonso): Consider renaming the class.
abstract class RampOpening extends BodyComponent with InitialPosition, Layered {
/// {@macro ramp_opening}
RampOpening({
required Layer pathwayLayer,
required Layer insideLayer,
Layer? outsideLayer,
int? insidePriority,
int? outsidePriority,
required this.orientation,
}) : _pathwayLayer = pathwayLayer,
_outsideLayer = outsideLayer ?? Layer.board {
layer = Layer.board;
}) : _insideLayer = insideLayer,
_outsideLayer = outsideLayer ?? Layer.board,
_insidePriority = insidePriority ?? 0,
_outsidePriority = outsidePriority ?? 0 {
layer = Layer.opening;
}
final Layer _pathwayLayer;
final Layer _insideLayer;
final Layer _outsideLayer;
final int _insidePriority;
final int _outsidePriority;
/// Mask of category bits for collision inside pathway.
Layer get pathwayLayer => _pathwayLayer;
/// Mask of category bits for collision inside ramp.
Layer get insideLayer => _insideLayer;
/// Mask of category bits for collision outside pathway.
/// Mask of category bits for collision outside ramp.
Layer get outsideLayer => _outsideLayer;
/// Priority for the [Ball] inside ramp.
int get insidePriority => _insidePriority;
/// Priority for the [Ball] outside ramp.
int get outsidePriority => _outsidePriority;
/// The [Shape] of the [RampOpening].
Shape get shape;
@ -64,8 +77,7 @@ abstract class RampOpening extends BodyComponent with InitialPosition, Layered {
}
/// {@template ramp_opening_ball_contact_callback}
/// Detects when a [Ball] enters or exits a pathway ramp through a
/// [RampOpening].
/// Detects when a [Ball] enters or exits a ramp through a [RampOpening].
///
/// Modifies [Ball]'s [Layer] accordingly depending on whether the [Ball] is
/// outside or inside a ramp.
@ -80,9 +92,11 @@ class RampOpeningBallContactCallback<Opening extends RampOpening>
Layer layer;
if (!_ballsInside.contains(ball)) {
layer = opening.pathwayLayer;
layer = opening.insideLayer;
_ballsInside.add(ball);
ball.layer = layer;
ball
..sendTo(opening.insidePriority)
..layer = layer;
} else {
_ballsInside.remove(ball);
}
@ -103,7 +117,9 @@ class RampOpeningBallContactCallback<Opening extends RampOpening>
ball.body.linearVelocity.y > 0);
if (isBallOutsideOpening) {
ball.layer = opening.outsideLayer;
ball
..sendTo(opening.outsidePriority)
..layer = opening.outsideLayer;
_ballsInside.remove(ball);
}
}

@ -21,6 +21,9 @@ class Spaceship extends Forge2DBlueprint {
/// The [position] where the elements will be created
final Vector2 position;
/// Base priority for wall while be on spaceship.
static const ballPriorityWhenOnSpaceship = 4;
@override
void build(_) {
addAllContactCallback([
@ -33,8 +36,8 @@ class Spaceship extends Forge2DBlueprint {
SpaceshipEntrance()..initialPosition = position,
AndroidHead()..initialPosition = position,
SpaceshipHole(
onExitLayer: Layer.spaceshipExitRail,
onExitElevation: 2,
outsideLayer: Layer.spaceshipExitRail,
outsidePriority: 2,
)..initialPosition = position - Vector2(5.2, 4.8),
SpaceshipHole()..initialPosition = position - Vector2(-7.2, 0.8),
SpaceshipWall()..initialPosition = position,
@ -47,8 +50,8 @@ class Spaceship extends Forge2DBlueprint {
/// {@endtemplate}
class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_saucer}
// TODO(ruimiguel): apply Elevated when PR merged.
SpaceshipSaucer() : super(priority: 3) {
SpaceshipSaucer()
: super(priority: Spaceship.ballPriorityWhenOnSpaceship - 1) {
layer = Layer.spaceship;
}
@ -92,7 +95,7 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
/// {@endtemplate}
class AndroidHead extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_bridge}
AndroidHead() : super(priority: 4) {
AndroidHead() : super(priority: Spaceship.ballPriorityWhenOnSpaceship + 1) {
layer = Layer.spaceship;
}
@ -147,16 +150,13 @@ class SpaceshipEntrance extends RampOpening {
/// {@macro spaceship_entrance}
SpaceshipEntrance()
: super(
pathwayLayer: Layer.spaceship,
insideLayer: Layer.spaceship,
orientation: RampOrientation.up,
insidePriority: Spaceship.ballPriorityWhenOnSpaceship,
) {
layer = Layer.spaceship;
}
/// Priority order for [SpaceshipHole] on enter.
// TODO(ruimiguel): apply Elevated when PR merged.
final int onEnterElevation = 4;
@override
Shape get shape {
renderBody = false;
@ -181,19 +181,17 @@ class SpaceshipEntrance extends RampOpening {
/// {@endtemplate}
class SpaceshipHole extends RampOpening {
/// {@macro spaceship_hole}
SpaceshipHole({Layer? onExitLayer, this.onExitElevation = 1})
SpaceshipHole({Layer? outsideLayer, int? outsidePriority = 1})
: super(
pathwayLayer: Layer.spaceship,
outsideLayer: onExitLayer,
insideLayer: Layer.spaceship,
outsideLayer: outsideLayer,
outsidePriority: outsidePriority,
orientation: RampOrientation.up,
) {
renderBody = false;
layer = Layer.spaceship;
}
/// Priority order for [SpaceshipHole] on exit.
// TODO(ruimiguel): apply Elevated when PR merged.
final int onExitElevation;
@override
Shape get shape {
return ArcShape(
@ -235,8 +233,7 @@ class _SpaceshipWallShape extends ChainShape {
/// {@endtemplate}
class SpaceshipWall extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_wall}
// TODO(ruimiguel): apply Elevated when PR merged
SpaceshipWall() : super(priority: 4) {
SpaceshipWall() : super(priority: Spaceship.ballPriorityWhenOnSpaceship + 1) {
layer = Layer.spaceship;
}
@ -269,9 +266,7 @@ class SpaceshipEntranceBallContactCallback
@override
void begin(SpaceshipEntrance entrance, Ball ball, _) {
ball
// TODO(ruimiguel): apply Elevated when PR merged.
..priority = entrance.onEnterElevation
..gameRef.reorderChildren()
..sendTo(entrance.insidePriority)
..layer = Layer.spaceship;
}
}
@ -279,16 +274,14 @@ class SpaceshipEntranceBallContactCallback
/// [ContactCallback] that handles the contact between the [Ball]
/// and a [SpaceshipHole].
///
/// It sets the [Ball] priority and filter data so it will "be back" on the
/// board.
/// It sets the [Ball] priority and filter data so it will outside of the
/// [Spaceship].
class SpaceshipHoleBallContactCallback
extends ContactCallback<SpaceshipHole, Ball> {
@override
void begin(SpaceshipHole hole, Ball ball, _) {
ball
// TODO(ruimiguel): apply Elevated when PR merged.
..priority = hole.onExitElevation
..gameRef.reorderChildren()
..sendTo(hole.outsidePriority)
..layer = hole.outsideLayer;
}
}

@ -1 +1,2 @@
export 'blueprint.dart';
export 'priority.dart';

@ -0,0 +1,39 @@
import 'dart:math' as math;
import 'package:flame/components.dart';
/// Helper methods to change the [priority] of a [Component].
extension ComponentPriorityX on Component {
static const _lowestPriority = 0;
/// Changes the priority to a specific one.
void sendTo(int destinationPriority) {
if (priority != destinationPriority) {
priority = math.max(destinationPriority, _lowestPriority);
reorderChildren();
}
}
/// Changes the priority to the lowest possible.
void sendToBack() {
if (priority != _lowestPriority) {
priority = _lowestPriority;
reorderChildren();
}
}
/// Decreases the priority to be lower than another [Component].
void showBehindOf(Component other) {
if (priority >= other.priority) {
priority = math.max(other.priority - 1, _lowestPriority);
reorderChildren();
}
}
/// Increases the priority to be higher than another [Component].
void showInFrontOf(Component other) {
if (priority <= other.priority) {
priority = other.priority + 1;
reorderChildren();
}
}
}

@ -1,5 +1,6 @@
import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
@ -24,3 +25,5 @@ class MockContact extends Mock implements Contact {}
class MockContactCallback extends Mock
implements ContactCallback<Object, Object> {}
class MockComponent extends Mock implements Component {}

@ -12,7 +12,7 @@ class TestRampOpening extends RampOpening {
required RampOrientation orientation,
required Layer pathwayLayer,
}) : super(
pathwayLayer: pathwayLayer,
insideLayer: pathwayLayer,
orientation: orientation,
);
@ -129,14 +129,12 @@ void main() {
final callback = TestRampOpeningBallContactCallback();
when(() => ball.body).thenReturn(body);
when(() => ball.priority).thenReturn(1);
when(() => body.position).thenReturn(Vector2.zero());
when(() => ball.layer).thenReturn(Layer.board);
await game.ready();
await game.ensureAdd(area);
callback.begin(ball, area, MockContact());
verify(() => ball.layer = area.pathwayLayer).called(1);
verify(() => ball.layer = area.insideLayer).called(1);
});
flameTester.test(
@ -152,14 +150,12 @@ void main() {
final callback = TestRampOpeningBallContactCallback();
when(() => ball.body).thenReturn(body);
when(() => ball.priority).thenReturn(1);
when(() => body.position).thenReturn(Vector2.zero());
when(() => ball.layer).thenReturn(Layer.board);
await game.ready();
await game.ensureAdd(area);
callback.begin(ball, area, MockContact());
verify(() => ball.layer = area.pathwayLayer).called(1);
verify(() => ball.layer = area.insideLayer).called(1);
});
flameTester.test(
@ -174,15 +170,13 @@ void main() {
final callback = TestRampOpeningBallContactCallback();
when(() => ball.body).thenReturn(body);
when(() => ball.priority).thenReturn(1);
when(() => body.position).thenReturn(Vector2.zero());
when(() => body.linearVelocity).thenReturn(Vector2(0, -1));
when(() => ball.layer).thenReturn(Layer.board);
await game.ready();
await game.ensureAdd(area);
callback.begin(ball, area, MockContact());
verify(() => ball.layer = area.pathwayLayer).called(1);
verify(() => ball.layer = area.insideLayer).called(1);
callback.end(ball, area, MockContact());
verify(() => ball.layer = Layer.board);
@ -200,15 +194,13 @@ void main() {
final callback = TestRampOpeningBallContactCallback();
when(() => ball.body).thenReturn(body);
when(() => ball.priority).thenReturn(1);
when(() => body.position).thenReturn(Vector2.zero());
when(() => body.linearVelocity).thenReturn(Vector2(0, 1));
when(() => ball.layer).thenReturn(Layer.board);
await game.ready();
await game.ensureAdd(area);
callback.begin(ball, area, MockContact());
verify(() => ball.layer = area.pathwayLayer).called(1);
verify(() => ball.layer = area.insideLayer).called(1);
callback.end(ball, area, MockContact());
verify(() => ball.layer = Layer.board);
@ -226,21 +218,19 @@ void main() {
final callback = TestRampOpeningBallContactCallback();
when(() => ball.body).thenReturn(body);
when(() => ball.priority).thenReturn(1);
when(() => body.position).thenReturn(Vector2.zero());
when(() => body.linearVelocity).thenReturn(Vector2(0, 1));
when(() => ball.layer).thenReturn(Layer.board);
await game.ready();
await game.ensureAdd(area);
callback.begin(ball, area, MockContact());
verify(() => ball.layer = area.pathwayLayer).called(1);
verify(() => ball.layer = area.insideLayer).called(1);
callback.end(ball, area, MockContact());
verifyNever(() => ball.layer = Layer.board);
callback.begin(ball, area, MockContact());
verifyNever(() => ball.layer = area.pathwayLayer);
verifyNever(() => ball.layer = area.insideLayer);
callback.end(ball, area, MockContact());
verify(() => ball.layer = Layer.board);

@ -59,7 +59,8 @@ void main() {
group('SpaceshipEntranceBallContactCallback', () {
test('changes the ball priority on contact', () {
when(() => entrance.onEnterElevation).thenReturn(3);
when(() => ball.priority).thenReturn(2);
when(() => entrance.insidePriority).thenReturn(3);
SpaceshipEntranceBallContactCallback().begin(
entrance,
@ -67,39 +68,15 @@ void main() {
MockContact(),
);
verify(() => ball.priority = entrance.onEnterElevation).called(1);
});
test('re order the game children', () {
when(() => entrance.onEnterElevation).thenReturn(3);
SpaceshipEntranceBallContactCallback().begin(
entrance,
ball,
MockContact(),
);
verify(game.reorderChildren).called(1);
verify(() => ball.sendTo(entrance.insidePriority)).called(1);
});
});
group('SpaceshipHoleBallContactCallback', () {
test('changes the ball priority on contact', () {
when(() => ball.priority).thenReturn(2);
when(() => hole.outsideLayer).thenReturn(Layer.board);
when(() => hole.onExitElevation).thenReturn(1);
SpaceshipHoleBallContactCallback().begin(
hole,
ball,
MockContact(),
);
verify(() => ball.priority = hole.onExitElevation).called(1);
});
test('re order the game children', () {
when(() => hole.outsideLayer).thenReturn(Layer.board);
when(() => hole.onExitElevation).thenReturn(1);
when(() => hole.outsidePriority).thenReturn(1);
SpaceshipHoleBallContactCallback().begin(
hole,
@ -107,7 +84,7 @@ void main() {
MockContact(),
);
verify(game.reorderChildren).called(1);
verify(() => ball.sendTo(hole.outsidePriority)).called(1);
});
});
});

@ -0,0 +1,221 @@
// ignore_for_file: cascade_invocations
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/src/flame/priority.dart';
import '../../helpers/helpers.dart';
class TestBodyComponent extends BodyComponent {
@override
Body createBody() {
final fixtureDef = FixtureDef(CircleShape());
return world.createBody(BodyDef())..createFixture(fixtureDef);
}
}
void main() {
final flameTester = FlameTester(Forge2DGame.new);
group('ComponentPriorityX', () {
group('sendTo', () {
flameTester.test(
'changes the priority correctly to other level',
(game) async {
const newPriority = 5;
final component = TestBodyComponent()..priority = 4;
component.sendTo(newPriority);
expect(component.priority, equals(newPriority));
},
);
flameTester.test(
'calls reorderChildren if the new priority is different',
(game) async {
const newPriority = 5;
final component = MockComponent();
when(() => component.priority).thenReturn(4);
component.sendTo(newPriority);
verify(component.reorderChildren).called(1);
},
);
flameTester.test(
"doesn't call reorderChildren if the priority is the same",
(game) async {
const newPriority = 5;
final component = MockComponent();
when(() => component.priority).thenReturn(newPriority);
component.sendTo(newPriority);
verifyNever(component.reorderChildren);
},
);
});
group('sendToBack', () {
flameTester.test(
'changes the priority correctly to board level',
(game) async {
final component = TestBodyComponent()..priority = 4;
component.sendToBack();
expect(component.priority, equals(0));
},
);
flameTester.test(
'calls reorderChildren if the priority is greater than lowest level',
(game) async {
final component = MockComponent();
when(() => component.priority).thenReturn(4);
component.sendToBack();
verify(component.reorderChildren).called(1);
},
);
flameTester.test(
"doesn't call reorderChildren if the priority is the lowest level",
(game) async {
final component = MockComponent();
when(() => component.priority).thenReturn(0);
component.sendToBack();
verifyNever(component.reorderChildren);
},
);
});
group('showBehindOf', () {
flameTester.test(
'changes the priority if it is greater than other component',
(game) async {
const startPriority = 2;
final component = TestBodyComponent()..priority = startPriority;
final otherComponent = TestBodyComponent()
..priority = startPriority - 1;
component.showBehindOf(otherComponent);
expect(component.priority, equals(otherComponent.priority - 1));
},
);
flameTester.test(
"doesn't change the priority if it is lower than other component",
(game) async {
const startPriority = 2;
final component = TestBodyComponent()..priority = startPriority;
final otherComponent = TestBodyComponent()
..priority = startPriority + 1;
component.showBehindOf(otherComponent);
expect(component.priority, equals(startPriority));
},
);
flameTester.test(
'calls reorderChildren if the priority is greater than other component',
(game) async {
const startPriority = 2;
final component = MockComponent();
final otherComponent = MockComponent();
when(() => component.priority).thenReturn(startPriority);
when(() => otherComponent.priority).thenReturn(startPriority - 1);
component.showBehindOf(otherComponent);
verify(component.reorderChildren).called(1);
},
);
flameTester.test(
"doesn't call reorderChildren if the priority is lower than other "
'component',
(game) async {
const startPriority = 2;
final component = MockComponent();
final otherComponent = MockComponent();
when(() => component.priority).thenReturn(startPriority);
when(() => otherComponent.priority).thenReturn(startPriority + 1);
component.showBehindOf(otherComponent);
verifyNever(component.reorderChildren);
},
);
});
group('showInFrontOf', () {
flameTester.test(
'changes the priority if it is lower than other component',
(game) async {
const startPriority = 2;
final component = TestBodyComponent()..priority = startPriority;
final otherComponent = TestBodyComponent()
..priority = startPriority + 1;
component.showInFrontOf(otherComponent);
expect(component.priority, equals(otherComponent.priority + 1));
},
);
flameTester.test(
"doesn't change the priority if it is greater than other component",
(game) async {
const startPriority = 2;
final component = TestBodyComponent()..priority = startPriority;
final otherComponent = TestBodyComponent()
..priority = startPriority - 1;
component.showInFrontOf(otherComponent);
expect(component.priority, equals(startPriority));
},
);
flameTester.test(
'calls reorderChildren if the priority is lower than other component',
(game) async {
const startPriority = 2;
final component = MockComponent();
final otherComponent = MockComponent();
when(() => component.priority).thenReturn(startPriority);
when(() => otherComponent.priority).thenReturn(startPriority + 1);
component.showInFrontOf(otherComponent);
verify(component.reorderChildren).called(1);
},
);
flameTester.test(
"doesn't call reorderChildren if the priority is greater than other "
'component',
(game) async {
const startPriority = 2;
final component = MockComponent();
final otherComponent = MockComponent();
when(() => component.priority).thenReturn(startPriority);
when(() => otherComponent.priority).thenReturn(startPriority - 1);
component.showInFrontOf(otherComponent);
verifyNever(component.reorderChildren);
},
);
});
});
}

@ -66,10 +66,6 @@ class MockFilter extends Mock implements Filter {}
class MockFixture extends Mock implements Fixture {}
class MockSpaceshipEntrance extends Mock implements SpaceshipEntrance {}
class MockSpaceshipHole extends Mock implements SpaceshipHole {}
class MockSpaceshipExitRailEnd extends Mock implements SpaceshipExitRailEnd {}
class MockComponentSet extends Mock implements ComponentSet {}

Loading…
Cancel
Save