mirror of https://github.com/flutter/pinball.git
parent
b9c2f3a54f
commit
d809145152
@ -1,47 +0,0 @@
|
||||
// ignore_for_file: avoid_renaming_method_parameters
|
||||
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template score_points}
|
||||
/// Specifies the amount of points received on [Ball] collision.
|
||||
/// {@endtemplate}
|
||||
mixin ScorePoints<T extends Forge2DGame> on BodyComponent<T> {
|
||||
/// {@macro score_points}
|
||||
int get points;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
body.userData = this;
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template ball_score_points_callbacks}
|
||||
/// Adds points to the score when a [Ball] collides with a [BodyComponent] that
|
||||
/// implements [ScorePoints].
|
||||
/// {@endtemplate}
|
||||
class BallScorePointsCallback extends ContactCallback<Ball, ScorePoints> {
|
||||
/// {@macro ball_score_points_callbacks}
|
||||
BallScorePointsCallback(PinballGame game) : _gameRef = game;
|
||||
|
||||
final PinballGame _gameRef;
|
||||
|
||||
@override
|
||||
void begin(
|
||||
Ball ball,
|
||||
ScorePoints scorePoints,
|
||||
Contact _,
|
||||
) {
|
||||
_gameRef.read<GameBloc>().add(Scored(points: scorePoints.points));
|
||||
_gameRef.audio.score();
|
||||
|
||||
_gameRef.add(
|
||||
ScoreText(
|
||||
text: scorePoints.points.toString(),
|
||||
position: ball.body.position,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
// ignore_for_file: avoid_renaming_method_parameters
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template scoring_behaviour}
|
||||
///
|
||||
/// {@endtemplate}
|
||||
class ScoringBehaviour extends Component
|
||||
with ContactCallbacks, HasGameRef<PinballGame> {
|
||||
/// {@macro scoring_behaviour}
|
||||
ScoringBehaviour({
|
||||
required int points,
|
||||
}) : _points = points;
|
||||
|
||||
final int _points;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
// TODO(alestiago): Refactor once the following is merged:
|
||||
// https://github.com/flame-engine/flame/pull/1566
|
||||
final parent = this.parent;
|
||||
if (parent is BodyComponent) {
|
||||
final userData = parent.body.userData;
|
||||
if (userData is ContactCallbacks) {
|
||||
userData.add(this);
|
||||
} else {
|
||||
parent.body.userData = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void beginContact(Object other, Contact contact) {
|
||||
super.beginContact(other, contact);
|
||||
if (other is! Ball) return;
|
||||
|
||||
gameRef.read<GameBloc>().add(Scored(points: _points));
|
||||
gameRef.audio.score();
|
||||
|
||||
gameRef.add(
|
||||
ScoreText(
|
||||
text: _points.toString(),
|
||||
position: other.body.position,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
|
||||
/// {@template contact_callbacks_adder}
|
||||
///
|
||||
/// {@endtemplate}
|
||||
// TODO(alestiago): Consider adding streams to [ContactCallbacks].
|
||||
extension ContactCallbacksAdder on ContactCallbacks {
|
||||
/// {@macro contact_callbacks_adder}
|
||||
void add(ContactCallbacks contactCallbacks) {
|
||||
if (contactCallbacks.onBeginContact != null) {
|
||||
onBeginContact = (other, contact) {
|
||||
onBeginContact?.call(other, contact);
|
||||
contactCallbacks.beginContact(other, contact);
|
||||
};
|
||||
}
|
||||
|
||||
if (contactCallbacks.onEndContact != null) {
|
||||
onEndContact = (other, contact) {
|
||||
onEndContact?.call(other, contact);
|
||||
contactCallbacks.endContact(other, contact);
|
||||
};
|
||||
}
|
||||
|
||||
if (contactCallbacks.onPreSolve != null) {
|
||||
onPreSolve = (other, contact, oldManifold) {
|
||||
onPreSolve?.call(other, contact, oldManifold);
|
||||
contactCallbacks.preSolve(other, contact, oldManifold);
|
||||
};
|
||||
}
|
||||
|
||||
if (contactCallbacks.onPostSolve != null) {
|
||||
onPostSolve = (other, contact, impulse) {
|
||||
onPostSolve?.call(other, contact, impulse);
|
||||
contactCallbacks.postSolve(other, contact, impulse);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
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_audio/pinball_audio.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
class FakeScorePoints extends BodyComponent with ScorePoints {
|
||||
@override
|
||||
Body createBody() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
int get points => 2;
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('BallScorePointsCallback', () {
|
||||
late PinballGame game;
|
||||
late GameBloc bloc;
|
||||
late PinballAudio audio;
|
||||
late Ball ball;
|
||||
late FakeScorePoints fakeScorePoints;
|
||||
|
||||
setUp(() {
|
||||
game = MockPinballGame();
|
||||
bloc = MockGameBloc();
|
||||
audio = MockPinballAudio();
|
||||
fakeScorePoints = FakeScorePoints();
|
||||
|
||||
ball = MockBall();
|
||||
final ballBody = MockBody();
|
||||
when(() => ball.body).thenReturn(ballBody);
|
||||
when(() => ballBody.position).thenReturn(Vector2.all(4));
|
||||
});
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(FakeGameEvent());
|
||||
});
|
||||
|
||||
group('begin', () {
|
||||
test(
|
||||
'emits Scored event with points',
|
||||
() {
|
||||
when(game.read<GameBloc>).thenReturn(bloc);
|
||||
when(() => game.audio).thenReturn(audio);
|
||||
|
||||
BallScorePointsCallback(game).begin(
|
||||
ball,
|
||||
fakeScorePoints,
|
||||
FakeContact(),
|
||||
);
|
||||
|
||||
verify(
|
||||
() => bloc.add(
|
||||
Scored(points: fakeScorePoints.points),
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'plays a Score sound',
|
||||
() {
|
||||
when(game.read<GameBloc>).thenReturn(bloc);
|
||||
when(() => game.audio).thenReturn(audio);
|
||||
|
||||
BallScorePointsCallback(game).begin(
|
||||
ball,
|
||||
fakeScorePoints,
|
||||
FakeContact(),
|
||||
);
|
||||
|
||||
verify(audio.score).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"adds a ScoreText component at Ball's position",
|
||||
() {
|
||||
when(game.read<GameBloc>).thenReturn(bloc);
|
||||
when(() => game.audio).thenReturn(audio);
|
||||
|
||||
BallScorePointsCallback(game).begin(
|
||||
ball,
|
||||
fakeScorePoints,
|
||||
FakeContact(),
|
||||
);
|
||||
|
||||
verify(
|
||||
() => game.add(
|
||||
ScoreText(
|
||||
text: fakeScorePoints.points.toString(),
|
||||
position: ball.body.position,
|
||||
),
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
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/game/game.dart';
|
||||
import 'package:pinball_audio/pinball_audio.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('ScoringBehaviour', () {
|
||||
group('beginContact', () {
|
||||
late GameBloc bloc;
|
||||
late PinballAudio audio;
|
||||
late Ball ball;
|
||||
|
||||
setUp(() {
|
||||
audio = MockPinballAudio();
|
||||
|
||||
ball = MockBall();
|
||||
final ballBody = MockBody();
|
||||
when(() => ball.body).thenReturn(ballBody);
|
||||
when(() => ballBody.position).thenReturn(Vector2.all(4));
|
||||
});
|
||||
|
||||
final flameBlocTester = FlameBlocTester<EmptyPinballTestGame, GameBloc>(
|
||||
gameBuilder: () => EmptyPinballTestGame(
|
||||
audio: audio,
|
||||
),
|
||||
blocBuilder: () {
|
||||
bloc = MockGameBloc();
|
||||
const state = GameState(
|
||||
score: 0,
|
||||
balls: 0,
|
||||
bonusHistory: [],
|
||||
);
|
||||
whenListen(bloc, Stream.value(state), initialState: state);
|
||||
return bloc;
|
||||
},
|
||||
);
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
'emits Scored event with points',
|
||||
setUp: (game, tester) async {
|
||||
const points = 20;
|
||||
final scoringBehaviour = ScoringBehaviour(points: points);
|
||||
await game.ensureAdd(scoringBehaviour);
|
||||
|
||||
scoringBehaviour.beginContact(ball, MockContact());
|
||||
|
||||
verify(
|
||||
() => bloc.add(
|
||||
const Scored(points: points),
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
'plays score sound',
|
||||
setUp: (game, tester) async {
|
||||
const points = 20;
|
||||
final scoringBehaviour = ScoringBehaviour(points: points);
|
||||
await game.ensureAdd(scoringBehaviour);
|
||||
|
||||
scoringBehaviour.beginContact(ball, MockContact());
|
||||
|
||||
verify(audio.score).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
"adds a ScoreText component at Ball's position with points",
|
||||
setUp: (game, tester) async {
|
||||
const points = 20;
|
||||
final scoringBehaviour = ScoringBehaviour(points: points);
|
||||
await game.ensureAdd(scoringBehaviour);
|
||||
|
||||
scoringBehaviour.beginContact(ball, MockContact());
|
||||
await game.ready();
|
||||
|
||||
final scoreText = game.descendants().whereType<ScoreText>();
|
||||
expect(scoreText.length, equals(1));
|
||||
expect(
|
||||
scoreText.first.text,
|
||||
equals(points.toString()),
|
||||
);
|
||||
expect(
|
||||
scoreText.first.position,
|
||||
equals(ball.body.position),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in new issue