mirror of https://github.com/flutter/pinball.git
feat: Flipper (#15)
* feat: explicitely imported Anchor * feat: implemented Flipper * feat: implemented Flipper in PinballGame * feat: implemented calculateRequiredSpeed * feat: included right and left constructors * feat: used right and left constructors * refactor: cleaned calcualteSpeed method * refactor: used width and height instead of size * feat: implemented FlipperAnchor * feat: implemented BoardSide enum * docs: used prose in doc comment * feat: implemented BoardSideX * refactor: used isLeft instead of isRight * refactor: implemented unlock method * refactor: same line assignment Co-authored-by: Erick <erickzanardoo@gmail.com> * feat: add themes * feat: add theme cubit * test: character themes * test: remove grouping * refactor: move themes to package * chore: add workflow * fix: workflow * docs: character themes update * refactor: one theme for entire game * chore: add to props * fix: changing ball spawning point to avoid context errors * refactor: modified unlock method due to invalid cast * feat: included test for BoardSide * refactor: removed sweepingAnimationDuration * feat: tested flipper.dart * refactor: included flippersPosition * refactor: implemented _addFlippers method * feat: centered vertices * feat: modified test to match new center * refactor: removed unecessary parenthesis * refactor: removed unecessary calculation * fix: changing ball spawning point to avoid context errors * chore: rebasing * docs: included FIXME comment * feat: moved key listening to Flipper * docs: include TOOD comment * feat: including test and refactor * docs: fixed doc comment typo Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * docs: fixed do comment template name Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * refactor: removed unnecessary verbose multiplication Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * refactor: removed unnecessary verbose multiplication * refactor: used ensureAddAll instead of ensureAdd Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * docs: fixed doc comment typo * refactor: used bigCircleShape.radius * refactor: reorganized methods * docs: improved doc comment * refactor: removed unecessary class variables * docs: fix doc comment typo * refactor: removed unused helper * fix: simplified keyEvents * fix: corrected erroneous key tests * refactor: modified component tests * refactor: capitalized Flipper test description * refactor: changed angle calculations * fix: tests * refactor: removed exta line Co-authored-by: Erick <erickzanardoo@gmail.com> Co-authored-by: Allison Ryan <allisonryan0002@gmail.com> Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>pull/31/head
parent
064ae1b1ed
commit
360b5876cf
@ -0,0 +1,22 @@
|
||||
import 'package:pinball/game/game.dart';
|
||||
|
||||
/// Indicates a side of the board.
|
||||
///
|
||||
/// Usually used to position or mirror elements of a [PinballGame]; such as a
|
||||
/// [Flipper].
|
||||
enum BoardSide {
|
||||
/// The left side of the board.
|
||||
left,
|
||||
|
||||
/// The right side of the board.
|
||||
right,
|
||||
}
|
||||
|
||||
/// Utility methods for [BoardSide].
|
||||
extension BoardSideX on BoardSide {
|
||||
/// Whether this side is [BoardSide.left].
|
||||
bool get isLeft => this == BoardSide.left;
|
||||
|
||||
/// Whether this side is [BoardSide.right].
|
||||
bool get isRight => this == BoardSide.right;
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
export 'anchor.dart';
|
||||
export 'ball.dart';
|
||||
export 'board_side.dart';
|
||||
export 'flipper.dart';
|
||||
export 'plunger.dart';
|
||||
export 'score_points.dart';
|
||||
export 'wall.dart';
|
||||
|
@ -0,0 +1,241 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
|
||||
/// {@template flipper}
|
||||
/// A bat, typically found in pairs at the bottom of the board.
|
||||
///
|
||||
/// [Flipper] can be controlled by the player in an arc motion.
|
||||
/// {@endtemplate flipper}
|
||||
class Flipper extends BodyComponent with KeyboardHandler {
|
||||
/// {@macro flipper}
|
||||
Flipper._({
|
||||
required Vector2 position,
|
||||
required this.side,
|
||||
required List<LogicalKeyboardKey> keys,
|
||||
}) : _position = position,
|
||||
_keys = keys {
|
||||
// TODO(alestiago): Use sprite instead of color when provided.
|
||||
paint = Paint()
|
||||
..color = const Color(0xFF00FF00)
|
||||
..style = PaintingStyle.fill;
|
||||
}
|
||||
|
||||
/// A left positioned [Flipper].
|
||||
Flipper.left({
|
||||
required Vector2 position,
|
||||
}) : this._(
|
||||
position: position,
|
||||
side: BoardSide.left,
|
||||
keys: [
|
||||
LogicalKeyboardKey.arrowLeft,
|
||||
LogicalKeyboardKey.keyA,
|
||||
],
|
||||
);
|
||||
|
||||
/// A right positioned [Flipper].
|
||||
Flipper.right({
|
||||
required Vector2 position,
|
||||
}) : this._(
|
||||
position: position,
|
||||
side: BoardSide.right,
|
||||
keys: [
|
||||
LogicalKeyboardKey.arrowRight,
|
||||
LogicalKeyboardKey.keyD,
|
||||
],
|
||||
);
|
||||
|
||||
/// The width of the [Flipper].
|
||||
static const width = 12.0;
|
||||
|
||||
/// The height of the [Flipper].
|
||||
static const height = 2.8;
|
||||
|
||||
/// The speed required to move the [Flipper] to its highest position.
|
||||
///
|
||||
/// The higher the value, the faster the [Flipper] will move.
|
||||
static const double _speed = 60;
|
||||
|
||||
/// Whether the [Flipper] is on the left or right side of the board.
|
||||
///
|
||||
/// A [Flipper] with [BoardSide.left] has a counter-clockwise arc motion,
|
||||
/// whereas a [Flipper] with [BoardSide.right] has a clockwise arc motion.
|
||||
final BoardSide side;
|
||||
|
||||
/// The initial position of the [Flipper] body.
|
||||
final Vector2 _position;
|
||||
|
||||
/// The [LogicalKeyboardKey]s that will control the [Flipper].
|
||||
///
|
||||
/// [onKeyEvent] method listens to when one of these keys is pressed.
|
||||
final List<LogicalKeyboardKey> _keys;
|
||||
|
||||
/// Applies downward linear velocity to the [Flipper], moving it to its
|
||||
/// resting position.
|
||||
void _moveDown() {
|
||||
body.linearVelocity = Vector2(0, -_speed);
|
||||
}
|
||||
|
||||
/// Applies upward linear velocity to the [Flipper], moving it to its highest
|
||||
/// position.
|
||||
void _moveUp() {
|
||||
body.linearVelocity = Vector2(0, _speed);
|
||||
}
|
||||
|
||||
List<FixtureDef> _createFixtureDefs() {
|
||||
final fixtures = <FixtureDef>[];
|
||||
final isLeft = side.isLeft;
|
||||
|
||||
final bigCircleShape = CircleShape()..radius = height / 2;
|
||||
bigCircleShape.position.setValues(
|
||||
isLeft
|
||||
? -(width / 2) + bigCircleShape.radius
|
||||
: (width / 2) - bigCircleShape.radius,
|
||||
0,
|
||||
);
|
||||
final bigCircleFixtureDef = FixtureDef(bigCircleShape);
|
||||
fixtures.add(bigCircleFixtureDef);
|
||||
|
||||
final smallCircleShape = CircleShape()..radius = bigCircleShape.radius / 2;
|
||||
smallCircleShape.position.setValues(
|
||||
isLeft
|
||||
? (width / 2) - smallCircleShape.radius
|
||||
: -(width / 2) + smallCircleShape.radius,
|
||||
0,
|
||||
);
|
||||
final smallCircleFixtureDef = FixtureDef(smallCircleShape);
|
||||
fixtures.add(smallCircleFixtureDef);
|
||||
|
||||
final trapeziumVertices = isLeft
|
||||
? [
|
||||
Vector2(bigCircleShape.position.x, bigCircleShape.radius),
|
||||
Vector2(smallCircleShape.position.x, smallCircleShape.radius),
|
||||
Vector2(smallCircleShape.position.x, -smallCircleShape.radius),
|
||||
Vector2(bigCircleShape.position.x, -bigCircleShape.radius),
|
||||
]
|
||||
: [
|
||||
Vector2(smallCircleShape.position.x, smallCircleShape.radius),
|
||||
Vector2(bigCircleShape.position.x, bigCircleShape.radius),
|
||||
Vector2(bigCircleShape.position.x, -bigCircleShape.radius),
|
||||
Vector2(smallCircleShape.position.x, -smallCircleShape.radius),
|
||||
];
|
||||
final trapezium = PolygonShape()..set(trapeziumVertices);
|
||||
final trapeziumFixtureDef = FixtureDef(trapezium)
|
||||
..density = 50.0 // TODO(alestiago): Use a proper density.
|
||||
..friction = .1; // TODO(alestiago): Use a proper friction.
|
||||
fixtures.add(trapeziumFixtureDef);
|
||||
|
||||
return fixtures;
|
||||
}
|
||||
|
||||
@override
|
||||
Body createBody() {
|
||||
final bodyDef = BodyDef()
|
||||
..gravityScale = 0
|
||||
..type = BodyType.dynamic
|
||||
..position = _position;
|
||||
|
||||
final body = world.createBody(bodyDef);
|
||||
_createFixtureDefs().forEach(body.createFixture);
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
// TODO(erickzanardo): Remove this once the issue is solved:
|
||||
// https://github.com/flame-engine/flame/issues/1417
|
||||
final Completer hasMounted = Completer<void>();
|
||||
|
||||
@override
|
||||
void onMount() {
|
||||
super.onMount();
|
||||
hasMounted.complete();
|
||||
}
|
||||
|
||||
@override
|
||||
bool onKeyEvent(
|
||||
RawKeyEvent event,
|
||||
Set<LogicalKeyboardKey> keysPressed,
|
||||
) {
|
||||
// TODO(alestiago): Check why false cancels the event for other components.
|
||||
// Investigate why return is of type [bool] expected instead of a type
|
||||
// [KeyEventResult].
|
||||
if (!_keys.contains(event.logicalKey)) return true;
|
||||
|
||||
if (event is RawKeyDownEvent) {
|
||||
_moveUp();
|
||||
} else if (event is RawKeyUpEvent) {
|
||||
_moveDown();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template flipper_anchor}
|
||||
/// [Anchor] positioned at the end of a [Flipper].
|
||||
///
|
||||
/// The end of a [Flipper] depends on its [Flipper.side].
|
||||
/// {@endtemplate}
|
||||
class FlipperAnchor extends Anchor {
|
||||
/// {@macro flipper_anchor}
|
||||
FlipperAnchor({
|
||||
required Flipper flipper,
|
||||
}) : super(
|
||||
position: Vector2(
|
||||
flipper.side.isLeft
|
||||
? flipper.body.position.x - Flipper.width / 2
|
||||
: flipper.body.position.x + Flipper.width / 2,
|
||||
flipper.body.position.y,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// {@template flipper_anchor_revolute_joint_def}
|
||||
/// Hinges one end of [Flipper] to a [Anchor] to achieve an arc motion.
|
||||
/// {@endtemplate}
|
||||
class FlipperAnchorRevoluteJointDef extends RevoluteJointDef {
|
||||
/// {@macro flipper_anchor_revolute_joint_def}
|
||||
FlipperAnchorRevoluteJointDef({
|
||||
required Flipper flipper,
|
||||
required Anchor anchor,
|
||||
}) {
|
||||
initialize(
|
||||
flipper.body,
|
||||
anchor.body,
|
||||
anchor.body.position,
|
||||
);
|
||||
enableLimit = true;
|
||||
|
||||
final angle = (flipper.side.isLeft ? _sweepingAngle : -_sweepingAngle) / 2;
|
||||
lowerAngle = upperAngle = angle;
|
||||
}
|
||||
|
||||
/// The total angle of the arc motion.
|
||||
static const _sweepingAngle = math.pi / 3.5;
|
||||
|
||||
/// Unlocks the [Flipper] from its resting position.
|
||||
///
|
||||
/// The [Flipper] is locked when initialized in order to force it to be at
|
||||
/// its resting position.
|
||||
// TODO(alestiago): consider refactor once the issue is solved:
|
||||
// https://github.com/flame-engine/forge2d/issues/36
|
||||
static void unlock(RevoluteJoint joint, BoardSide side) {
|
||||
late final double upperLimit, lowerLimit;
|
||||
switch (side) {
|
||||
case BoardSide.left:
|
||||
lowerLimit = -joint.lowerLimit;
|
||||
upperLimit = joint.upperLimit;
|
||||
break;
|
||||
case BoardSide.right:
|
||||
lowerLimit = joint.lowerLimit;
|
||||
upperLimit = -joint.upperLimit;
|
||||
}
|
||||
|
||||
joint.setLimits(lowerLimit, upperLimit);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
|
||||
void main() {
|
||||
group(
|
||||
'BoardSide',
|
||||
() {
|
||||
test('has two values', () {
|
||||
expect(BoardSide.values.length, equals(2));
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
group('BoardSideX', () {
|
||||
test('isLeft is correct', () {
|
||||
const side = BoardSide.left;
|
||||
expect(side.isLeft, isTrue);
|
||||
expect(side.isRight, isFalse);
|
||||
});
|
||||
|
||||
test('isRight is correct', () {
|
||||
const side = BoardSide.right;
|
||||
expect(side.isLeft, isFalse);
|
||||
expect(side.isRight, isTrue);
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,401 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final flameTester = FlameTester(PinballGame.new);
|
||||
group(
|
||||
'Flipper',
|
||||
() {
|
||||
flameTester.test(
|
||||
'loads correctly',
|
||||
(game) async {
|
||||
final leftFlipper = Flipper.left(position: Vector2.zero());
|
||||
final rightFlipper = Flipper.right(position: Vector2.zero());
|
||||
await game.ensureAddAll([leftFlipper, rightFlipper]);
|
||||
|
||||
expect(game.contains(leftFlipper), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
group('constructor', () {
|
||||
test('sets BoardSide', () {
|
||||
final leftFlipper = Flipper.left(position: Vector2.zero());
|
||||
expect(leftFlipper.side, equals(leftFlipper.side));
|
||||
|
||||
final rightFlipper = Flipper.right(position: Vector2.zero());
|
||||
expect(rightFlipper.side, equals(rightFlipper.side));
|
||||
});
|
||||
});
|
||||
|
||||
group('body', () {
|
||||
flameTester.test(
|
||||
'positions correctly',
|
||||
(game) async {
|
||||
final position = Vector2.all(10);
|
||||
final flipper = Flipper.left(position: position);
|
||||
await game.ensureAdd(flipper);
|
||||
game.contains(flipper);
|
||||
|
||||
expect(flipper.body.position, position);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'is dynamic',
|
||||
(game) async {
|
||||
final flipper = Flipper.left(position: Vector2.zero());
|
||||
await game.ensureAdd(flipper);
|
||||
|
||||
expect(flipper.body.bodyType, equals(BodyType.dynamic));
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'ignores gravity',
|
||||
(game) async {
|
||||
final flipper = Flipper.left(position: Vector2.zero());
|
||||
await game.ensureAdd(flipper);
|
||||
|
||||
expect(flipper.body.gravityScale, isZero);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'has greater mass than Ball',
|
||||
(game) async {
|
||||
final flipper = Flipper.left(position: Vector2.zero());
|
||||
final ball = Ball(position: Vector2.zero());
|
||||
|
||||
await game.ensureAddAll([flipper, ball]);
|
||||
|
||||
expect(
|
||||
flipper.body.getMassData().mass,
|
||||
greaterThan(ball.body.getMassData().mass),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('fixtures', () {
|
||||
flameTester.test(
|
||||
'has three',
|
||||
(game) async {
|
||||
final flipper = Flipper.left(position: Vector2.zero());
|
||||
await game.ensureAdd(flipper);
|
||||
|
||||
expect(flipper.body.fixtures.length, equals(3));
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'has density',
|
||||
(game) async {
|
||||
final flipper = Flipper.left(position: Vector2.zero());
|
||||
await game.ensureAdd(flipper);
|
||||
|
||||
final fixtures = flipper.body.fixtures;
|
||||
final density = fixtures.fold<double>(
|
||||
0,
|
||||
(sum, fixture) => sum + fixture.density,
|
||||
);
|
||||
|
||||
expect(density, greaterThan(0));
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('onKeyEvent', () {
|
||||
final leftKeys = UnmodifiableListView([
|
||||
LogicalKeyboardKey.arrowLeft,
|
||||
LogicalKeyboardKey.keyA,
|
||||
]);
|
||||
final rightKeys = UnmodifiableListView([
|
||||
LogicalKeyboardKey.arrowRight,
|
||||
LogicalKeyboardKey.keyD,
|
||||
]);
|
||||
|
||||
group('and Flipper is left', () {
|
||||
late Flipper flipper;
|
||||
|
||||
setUp(() {
|
||||
flipper = Flipper.left(position: Vector2.zero());
|
||||
});
|
||||
|
||||
testRawKeyDownEvents(leftKeys, (event) {
|
||||
flameTester.test(
|
||||
'moves upwards '
|
||||
'when ${event.logicalKey.keyLabel} is pressed',
|
||||
(game) async {
|
||||
await game.ensureAdd(flipper);
|
||||
flipper.onKeyEvent(event, {});
|
||||
|
||||
expect(flipper.body.linearVelocity.y, isPositive);
|
||||
expect(flipper.body.linearVelocity.x, isZero);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
testRawKeyUpEvents(leftKeys, (event) {
|
||||
flameTester.test(
|
||||
'moves downwards '
|
||||
'when ${event.logicalKey.keyLabel} is released',
|
||||
(game) async {
|
||||
await game.ensureAdd(flipper);
|
||||
flipper.onKeyEvent(event, {});
|
||||
|
||||
expect(flipper.body.linearVelocity.y, isNegative);
|
||||
expect(flipper.body.linearVelocity.x, isZero);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
testRawKeyUpEvents(rightKeys, (event) {
|
||||
flameTester.test(
|
||||
'does nothing '
|
||||
'when ${event.logicalKey.keyLabel} is released',
|
||||
(game) async {
|
||||
await game.ensureAdd(flipper);
|
||||
flipper.onKeyEvent(event, {});
|
||||
|
||||
expect(flipper.body.linearVelocity.y, isZero);
|
||||
expect(flipper.body.linearVelocity.x, isZero);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
testRawKeyDownEvents(rightKeys, (event) {
|
||||
flameTester.test(
|
||||
'does nothing '
|
||||
'when ${event.logicalKey.keyLabel} is pressed',
|
||||
(game) async {
|
||||
await game.ensureAdd(flipper);
|
||||
flipper.onKeyEvent(event, {});
|
||||
|
||||
expect(flipper.body.linearVelocity.y, isZero);
|
||||
expect(flipper.body.linearVelocity.x, isZero);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('and Flipper is right', () {
|
||||
late Flipper flipper;
|
||||
|
||||
setUp(() {
|
||||
flipper = Flipper.right(position: Vector2.zero());
|
||||
});
|
||||
|
||||
testRawKeyDownEvents(rightKeys, (event) {
|
||||
flameTester.test(
|
||||
'moves upwards '
|
||||
'when ${event.logicalKey.keyLabel} is pressed',
|
||||
(game) async {
|
||||
await game.ensureAdd(flipper);
|
||||
flipper.onKeyEvent(event, {});
|
||||
|
||||
expect(flipper.body.linearVelocity.y, isPositive);
|
||||
expect(flipper.body.linearVelocity.x, isZero);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
testRawKeyUpEvents(rightKeys, (event) {
|
||||
flameTester.test(
|
||||
'moves downwards '
|
||||
'when ${event.logicalKey.keyLabel} is released',
|
||||
(game) async {
|
||||
await game.ensureAdd(flipper);
|
||||
flipper.onKeyEvent(event, {});
|
||||
|
||||
expect(flipper.body.linearVelocity.y, isNegative);
|
||||
expect(flipper.body.linearVelocity.x, isZero);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
testRawKeyUpEvents(leftKeys, (event) {
|
||||
flameTester.test(
|
||||
'does nothing '
|
||||
'when ${event.logicalKey.keyLabel} is released',
|
||||
(game) async {
|
||||
await game.ensureAdd(flipper);
|
||||
flipper.onKeyEvent(event, {});
|
||||
|
||||
expect(flipper.body.linearVelocity.y, isZero);
|
||||
expect(flipper.body.linearVelocity.x, isZero);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
testRawKeyDownEvents(leftKeys, (event) {
|
||||
flameTester.test(
|
||||
'does nothing '
|
||||
'when ${event.logicalKey.keyLabel} is pressed',
|
||||
(game) async {
|
||||
await game.ensureAdd(flipper);
|
||||
flipper.onKeyEvent(event, {});
|
||||
|
||||
expect(flipper.body.linearVelocity.y, isZero);
|
||||
expect(flipper.body.linearVelocity.x, isZero);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
group(
|
||||
'FlipperAnchor',
|
||||
() {
|
||||
flameTester.test(
|
||||
'position is at the left of the left Flipper',
|
||||
(game) async {
|
||||
final flipper = Flipper.left(position: Vector2.zero());
|
||||
await game.ensureAdd(flipper);
|
||||
|
||||
final flipperAnchor = FlipperAnchor(flipper: flipper);
|
||||
await game.ensureAdd(flipperAnchor);
|
||||
|
||||
expect(flipperAnchor.body.position.x, equals(-Flipper.width / 2));
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'position is at the right of the right Flipper',
|
||||
(game) async {
|
||||
final flipper = Flipper.right(position: Vector2.zero());
|
||||
await game.ensureAdd(flipper);
|
||||
|
||||
final flipperAnchor = FlipperAnchor(flipper: flipper);
|
||||
await game.ensureAdd(flipperAnchor);
|
||||
|
||||
expect(flipperAnchor.body.position.x, equals(Flipper.width / 2));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
group('FlipperAnchorRevoluteJointDef', () {
|
||||
group('initializes with', () {
|
||||
flameTester.test(
|
||||
'limits enabled',
|
||||
(game) async {
|
||||
final flipper = Flipper.left(position: Vector2.zero());
|
||||
await game.ensureAdd(flipper);
|
||||
|
||||
final flipperAnchor = FlipperAnchor(flipper: flipper);
|
||||
await game.ensureAdd(flipperAnchor);
|
||||
|
||||
final jointDef = FlipperAnchorRevoluteJointDef(
|
||||
flipper: flipper,
|
||||
anchor: flipperAnchor,
|
||||
);
|
||||
|
||||
expect(jointDef.enableLimit, isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
group('equal upper and lower limits', () {
|
||||
flameTester.test(
|
||||
'when Flipper is left',
|
||||
(game) async {
|
||||
final flipper = Flipper.left(position: Vector2.zero());
|
||||
await game.ensureAdd(flipper);
|
||||
|
||||
final flipperAnchor = FlipperAnchor(flipper: flipper);
|
||||
await game.ensureAdd(flipperAnchor);
|
||||
|
||||
final jointDef = FlipperAnchorRevoluteJointDef(
|
||||
flipper: flipper,
|
||||
anchor: flipperAnchor,
|
||||
);
|
||||
|
||||
expect(jointDef.lowerAngle, equals(jointDef.upperAngle));
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'when Flipper is right',
|
||||
(game) async {
|
||||
final flipper = Flipper.right(position: Vector2.zero());
|
||||
await game.ensureAdd(flipper);
|
||||
|
||||
final flipperAnchor = FlipperAnchor(flipper: flipper);
|
||||
await game.ensureAdd(flipperAnchor);
|
||||
|
||||
final jointDef = FlipperAnchorRevoluteJointDef(
|
||||
flipper: flipper,
|
||||
anchor: flipperAnchor,
|
||||
);
|
||||
|
||||
expect(jointDef.lowerAngle, equals(jointDef.upperAngle));
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group(
|
||||
'unlocks',
|
||||
() {
|
||||
flameTester.test(
|
||||
'when Flipper is left',
|
||||
(game) async {
|
||||
final flipper = Flipper.left(position: Vector2.zero());
|
||||
await game.ensureAdd(flipper);
|
||||
|
||||
final flipperAnchor = FlipperAnchor(flipper: flipper);
|
||||
await game.ensureAdd(flipperAnchor);
|
||||
|
||||
final jointDef = FlipperAnchorRevoluteJointDef(
|
||||
flipper: flipper,
|
||||
anchor: flipperAnchor,
|
||||
);
|
||||
final joint = game.world.createJoint(jointDef) as RevoluteJoint;
|
||||
|
||||
FlipperAnchorRevoluteJointDef.unlock(joint, flipper.side);
|
||||
|
||||
expect(
|
||||
joint.upperLimit,
|
||||
isNot(equals(joint.lowerLimit)),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'when Flipper is right',
|
||||
(game) async {
|
||||
final flipper = Flipper.right(position: Vector2.zero());
|
||||
await game.ensureAdd(flipper);
|
||||
|
||||
final flipperAnchor = FlipperAnchor(flipper: flipper);
|
||||
await game.ensureAdd(flipperAnchor);
|
||||
|
||||
final jointDef = FlipperAnchorRevoluteJointDef(
|
||||
flipper: flipper,
|
||||
anchor: flipperAnchor,
|
||||
);
|
||||
final joint = game.world.createJoint(jointDef) as RevoluteJoint;
|
||||
|
||||
FlipperAnchorRevoluteJointDef.unlock(joint, flipper.side);
|
||||
|
||||
expect(
|
||||
joint.upperLimit,
|
||||
isNot(equals(joint.lowerLimit)),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
@ -1,9 +1,54 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
|
||||
void main() {
|
||||
group('PinballGame', () {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final flameTester = FlameTester(PinballGame.new);
|
||||
|
||||
// TODO(alestiago): test if [PinballGame] registers
|
||||
// [BallScorePointsCallback] once the following issue is resolved:
|
||||
// https://github.com/flame-engine/flame/issues/1416
|
||||
group(
|
||||
'components',
|
||||
() {
|
||||
group('Flippers', () {
|
||||
bool Function(Component) flipperSelector(BoardSide side) =>
|
||||
(component) => component is Flipper && component.side == side;
|
||||
|
||||
flameTester.test(
|
||||
'has only one left Flipper',
|
||||
(game) async {
|
||||
await game.ready();
|
||||
|
||||
expect(
|
||||
() => game.children.singleWhere(
|
||||
flipperSelector(BoardSide.left),
|
||||
),
|
||||
returnsNormally,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'has only one right Flipper',
|
||||
(game) async {
|
||||
await game.ready();
|
||||
|
||||
expect(
|
||||
() => game.children.singleWhere(
|
||||
flipperSelector(BoardSide.right),
|
||||
),
|
||||
returnsNormally,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
@isTest
|
||||
void testRawKeyUpEvents(
|
||||
List<LogicalKeyboardKey> keys,
|
||||
Function(RawKeyUpEvent) test,
|
||||
) {
|
||||
for (final key in keys) {
|
||||
test(_mockKeyUpEvent(key));
|
||||
}
|
||||
}
|
||||
|
||||
RawKeyUpEvent _mockKeyUpEvent(LogicalKeyboardKey key) {
|
||||
final event = MockRawKeyUpEvent();
|
||||
when(() => event.logicalKey).thenReturn(key);
|
||||
return event;
|
||||
}
|
||||
|
||||
@isTest
|
||||
void testRawKeyDownEvents(
|
||||
List<LogicalKeyboardKey> keys,
|
||||
Function(RawKeyDownEvent) test,
|
||||
) {
|
||||
for (final key in keys) {
|
||||
test(_mockKeyDownEvent(key));
|
||||
}
|
||||
}
|
||||
|
||||
RawKeyDownEvent _mockKeyDownEvent(LogicalKeyboardKey key) {
|
||||
final event = MockRawKeyDownEvent();
|
||||
when(() => event.logicalKey).thenReturn(key);
|
||||
return event;
|
||||
}
|
Loading…
Reference in new issue