refactor: simplified CameraFocusingBehavior

pull/346/head
alestiago 3 years ago
parent 11c5a3b8d3
commit 801deb9ffa

@ -6,7 +6,7 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template focus_data}
/// Model class that defines a focus point of the camera.
/// Defines a [Camera] focus point.
/// {@endtemplate}
class FocusData {
/// {@template focus_data}
@ -22,31 +22,33 @@ class FocusData {
final Vector2 position;
}
///
/// Changes the game focus when the [GameBloc] status changes.
class CameraFocusingBehavior extends Component
with ParentIsA<FlameGame>, BlocComponent<GameBloc, GameState> {
final Map<String, FocusData> _focuses = {};
// @override
// bool listenWhen(GameState? previousState, GameState newState) {
// print('listen');
// return true;
// return previousState?.isGameOver != newState.isGameOver;
// }
@override
bool listenWhen(GameState? previousState, GameState newState) {
return previousState?.status != newState.status;
}
@override
void onNewState(GameState state) {
print(state);
if (state.isGameOver) {
_zoom(_focuses['backbox']!);
} else {
_zoom(_focuses['game']!);
switch (state.status) {
case GameStatus.waiting:
break;
case GameStatus.playing:
_zoom(_focuses['game']!);
break;
case GameStatus.gameOver:
_zoom(_focuses['backbox']!);
break;
}
}
@override
void onGameResize(Vector2 size) {
super.onGameResize(size);
Future<void> onLoad() async {
await super.onLoad();
_focuses['game'] = FocusData(
zoom: parent.size.y / 16,
position: Vector2(0, -7.8),
@ -59,11 +61,7 @@ class CameraFocusingBehavior extends Component
zoom: parent.size.y / 10,
position: Vector2(0, -111),
);
}
@override
Future<void> onLoad() async {
await super.onLoad();
_snap(_focuses['waiting']!);
}
@ -79,6 +77,6 @@ class CameraFocusingBehavior extends Component
zoom.completed.then((_) {
parent.camera.moveTo(data.position);
});
parent.add(zoom);
add(zoom);
}
}

@ -53,7 +53,7 @@ class GameState extends Equatable {
totalScore = 0,
roundScore = 0,
multiplier = 1,
rounds = 3,
rounds = 1,
bonusHistory = const [];
/// The score for the current round of the game.

@ -18,7 +18,6 @@ class GameBlocStatusListener extends Component
break;
case GameStatus.playing:
gameRef.player.play(PinballAudio.backgroundMusic);
gameRef.firstChild<CameraController>()?.focusOnGame();
gameRef.overlays.remove(PinballGame.playButtonOverlay);
break;
case GameStatus.gameOver:
@ -26,7 +25,6 @@ class GameBlocStatusListener extends Component
score: state.displayScore,
characterIconPath: gameRef.characterTheme.leaderboardIcon.keyName,
);
gameRef.firstChild<CameraController>()!.focusOnGameOverBackbox();
break;
}
}

@ -0,0 +1,122 @@
// ignore_for_file: cascade_invocations
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball/game/behaviors/camera_focusing_behavior.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import '../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group(
'CameraFocusingBehavior',
() {
final flameTester = FlameTester(
EmptyPinballTestGame.new,
);
test('can be instantiated', () {
expect(
CameraFocusingBehavior(),
isA<CameraFocusingBehavior>(),
);
});
flameTester.test('loads', (game) async {
final behavior = CameraFocusingBehavior();
await game.ensureAdd(behavior);
expect(game.contains(behavior), isTrue);
});
flameTester.test(
'changes focus when loaded',
(game) async {
final behavior = CameraFocusingBehavior();
final previousZoom = game.camera.zoom;
expect(game.camera.follow, isNull);
await game.ensureAdd(behavior);
expect(game.camera.follow, isNotNull);
expect(game.camera.zoom, isNot(equals(previousZoom)));
},
);
flameTester.test(
'listenWhen only listens when status changes',
(game) async {
final behavior = CameraFocusingBehavior();
const waiting = GameState.initial();
final playing =
const GameState.initial().copyWith(status: GameStatus.playing);
final gameOver =
const GameState.initial().copyWith(status: GameStatus.gameOver);
expect(behavior.listenWhen(waiting, waiting), isFalse);
expect(behavior.listenWhen(waiting, playing), isTrue);
expect(behavior.listenWhen(waiting, gameOver), isTrue);
expect(behavior.listenWhen(playing, playing), isFalse);
expect(behavior.listenWhen(playing, waiting), isTrue);
expect(behavior.listenWhen(playing, gameOver), isTrue);
expect(behavior.listenWhen(gameOver, gameOver), isFalse);
expect(behavior.listenWhen(gameOver, waiting), isTrue);
expect(behavior.listenWhen(gameOver, playing), isTrue);
},
);
group('onNewState', () {
flameTester.test(
'zooms when started playing',
(game) async {
final playing =
const GameState.initial().copyWith(status: GameStatus.playing);
final behavior = CameraFocusingBehavior();
await game.ensureAdd(behavior);
behavior.onNewState(playing);
final previousPosition = game.camera.position.clone();
await game.ready();
final zoom = behavior.children.whereType<CameraZoom>().single;
game.update(zoom.controller.duration!);
expect(
game.camera.position,
isNot(equals(previousPosition)),
);
},
);
flameTester.test(
'zooms when lost',
(game) async {
final playing = const GameState.initial().copyWith(
status: GameStatus.gameOver,
);
final behavior = CameraFocusingBehavior();
await game.ensureAdd(behavior);
behavior.onNewState(playing);
final previousPosition = game.camera.position.clone();
await game.ready();
final zoom = behavior.children.whereType<CameraZoom>().single;
game.update(zoom.controller.duration!);
expect(
game.camera.position,
isNot(equals(previousPosition)),
);
},
);
});
},
);
}

@ -1,113 +0,0 @@
// ignore_for_file: cascade_invocations
import 'package:flame/game.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball/game/components/camera_controller.dart';
import 'package:pinball_components/pinball_components.dart';
void main() {
group('CameraController', () {
late FlameGame game;
late CameraController controller;
setUp(() async {
game = FlameGame()..onGameResize(Vector2(100, 200));
controller = CameraController(game);
await game.ensureAdd(controller);
});
test('loads correctly', () async {
expect(game.firstChild<CameraController>(), isNotNull);
});
test('correctly calculates the zooms', () async {
expect(controller.gameFocus.zoom.toInt(), equals(12));
expect(controller.waitingBackboxFocus.zoom.toInt(), equals(11));
});
test('correctly sets the initial zoom and position', () async {
expect(game.camera.zoom, equals(controller.waitingBackboxFocus.zoom));
expect(
game.camera.follow,
equals(controller.waitingBackboxFocus.position),
);
});
group('focusOnGame', () {
test('changes the zoom', () async {
controller.focusOnGame();
await game.ready();
final zoom = game.firstChild<CameraZoom>();
expect(zoom, isNotNull);
expect(zoom?.value, equals(controller.gameFocus.zoom));
});
test('moves the camera after the zoom is completed', () async {
controller.focusOnGame();
await game.ready();
final cameraZoom = game.firstChild<CameraZoom>()!;
final future = cameraZoom.completed;
game.update(10);
game.update(0); // Ensure that the component was removed
await future;
expect(game.camera.position, Vector2(-4, -120));
});
});
group('focusOnWaitingBackbox', () {
test('changes the zoom', () async {
controller.focusOnWaitingBackbox();
await game.ready();
final zoom = game.firstChild<CameraZoom>();
expect(zoom, isNotNull);
expect(zoom?.value, equals(controller.waitingBackboxFocus.zoom));
});
test('moves the camera after the zoom is completed', () async {
controller.focusOnWaitingBackbox();
await game.ready();
final cameraZoom = game.firstChild<CameraZoom>()!;
final future = cameraZoom.completed;
game.update(10);
game.update(0); // Ensure that the component was removed
await future;
expect(game.camera.position, Vector2(-4.5, -121));
});
});
group('focusOnGameOverBackbox', () {
test('changes the zoom', () async {
controller.focusOnGameOverBackbox();
await game.ready();
final zoom = game.firstChild<CameraZoom>();
expect(zoom, isNotNull);
expect(zoom?.value, equals(controller.gameOverBackboxFocus.zoom));
});
test('moves the camera after the zoom is completed', () async {
controller.focusOnGameOverBackbox();
await game.ready();
final cameraZoom = game.firstChild<CameraZoom>()!;
final future = cameraZoom.completed;
game.update(10);
game.update(0); // Ensure that the component was removed
await future;
expect(game.camera.position, Vector2(-2.5, -117));
});
});
});
}

@ -11,8 +11,6 @@ class _MockPinballGame extends Mock implements PinballGame {}
class _MockBackbox extends Mock implements Backbox {}
class _MockCameraController extends Mock implements CameraController {}
class _MockActiveOverlaysNotifier extends Mock
implements ActiveOverlaysNotifier {}
@ -42,20 +40,18 @@ void main() {
group('onNewState', () {
late PinballGame game;
late Backbox backbox;
late CameraController cameraController;
late GameBlocStatusListener gameFlowController;
late GameBlocStatusListener gameBlocStatusListener;
late PinballPlayer pinballPlayer;
late ActiveOverlaysNotifier overlays;
setUp(() {
game = _MockPinballGame();
backbox = _MockBackbox();
cameraController = _MockCameraController();
gameFlowController = GameBlocStatusListener();
gameBlocStatusListener = GameBlocStatusListener();
overlays = _MockActiveOverlaysNotifier();
pinballPlayer = _MockPinballPlayer();
gameFlowController.mockGameRef(game);
gameBlocStatusListener.mockGameRef(game);
when(
() => backbox.initialsInput(
@ -64,22 +60,18 @@ void main() {
onSubmit: any(named: 'onSubmit'),
),
).thenAnswer((_) async {});
when(cameraController.focusOnWaitingBackbox).thenAnswer((_) async {});
when(cameraController.focusOnGame).thenAnswer((_) async {});
when(() => overlays.remove(any())).thenAnswer((_) => true);
when(() => game.descendants().whereType<Backbox>())
.thenReturn([backbox]);
when(game.firstChild<CameraController>).thenReturn(cameraController);
when(() => game.overlays).thenReturn(overlays);
when(() => game.characterTheme).thenReturn(DashTheme());
when(() => game.player).thenReturn(pinballPlayer);
});
test(
'changes the backbox display and camera correctly '
'when the game is over',
'changes the backbox display when the game is over',
() {
final state = GameState(
totalScore: 0,
@ -89,7 +81,7 @@ void main() {
bonusHistory: const [],
status: GameStatus.gameOver,
);
gameFlowController.onNewState(state);
gameBlocStatusListener.onNewState(state);
verify(
() => backbox.initialsInput(
@ -98,18 +90,16 @@ void main() {
onSubmit: any(named: 'onSubmit'),
),
).called(1);
verify(cameraController.focusOnGameOverBackbox).called(1);
},
);
test(
'changes the backbox and camera correctly when it is not a game over',
'changes the backbox when it is not a game over',
() {
gameFlowController.onNewState(
gameBlocStatusListener.onNewState(
GameState.initial().copyWith(status: GameStatus.playing),
);
verify(cameraController.focusOnGame).called(1);
verify(() => overlays.remove(PinballGame.playButtonOverlay))
.called(1);
},
@ -118,7 +108,7 @@ void main() {
test(
'plays the background music on start',
() {
gameFlowController.onNewState(
gameBlocStatusListener.onNewState(
GameState.initial().copyWith(status: GameStatus.playing),
);

Loading…
Cancel
Save