Merge branch 'main' into refactor/renamed-loading-tests

pull/450/head
Alejandro Santiago 3 years ago committed by GitHub
commit 0d1b35ae38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -19,21 +19,31 @@ class AssetsManagerCubit extends Cubit<AssetsManagerState> {
/// do its job without adding too much delay for the user, we are letting
/// the UI paint first, and then we start loading the assets.
await Future<void>.delayed(const Duration(seconds: 1));
final loadables = <Future<void> Function()>[
_game.preFetchLeaderboard,
..._game.preLoadAssets(),
..._audioPlayer.load(),
...BonusAnimation.loadAssets(),
...SelectedCharacter.loadAssets(),
];
emit(
state.copyWith(
loadables: [
_game.preFetchLeaderboard(),
..._game.preLoadAssets(),
..._audioPlayer.load(),
...BonusAnimation.loadAssets(),
...SelectedCharacter.loadAssets(),
],
assetsCount: loadables.length,
),
);
final all = state.loadables.map((loadable) async {
await loadable;
emit(state.copyWith(loaded: [...state.loaded, loadable]));
}).toList();
await Future.wait(all);
late void Function() _triggerLoad;
_triggerLoad = () async {
if (loadables.isEmpty) return;
final loadable = loadables.removeAt(0);
await loadable();
_triggerLoad();
emit(state.copyWith(loaded: state.loaded + 1));
};
const _throttleSize = 3;
for (var i = 0; i < _throttleSize; i++) {
_triggerLoad();
}
}
}

@ -1,44 +1,42 @@
part of 'assets_manager_cubit.dart';
/// {@template assets_manager_state}
/// State used to load the game assets
/// State used to load the game assets.
/// {@endtemplate}
class AssetsManagerState extends Equatable {
/// {@macro assets_manager_state}
const AssetsManagerState({
required this.loadables,
required this.assetsCount,
required this.loaded,
});
/// {@macro assets_manager_state}
const AssetsManagerState.initial()
: this(loadables: const [], loaded: const []);
const AssetsManagerState.initial() : this(assetsCount: 0, loaded: 0);
/// List of futures to load
final List<Future> loadables;
/// Number of assets to load.
final int assetsCount;
/// List of loaded futures
final List<Future> loaded;
/// Number of already loaded assets.
final int loaded;
/// Returns a value between 0 and 1 to indicate the loading progress
double get progress =>
loadables.isEmpty ? 0 : loaded.length / loadables.length;
/// Returns a value between 0 and 1 to indicate the loading progress.
double get progress => loaded == 0 ? 0 : loaded / assetsCount;
/// Only returns false if all the assets have been loaded
/// Only returns false if all the assets have been loaded.
bool get isLoading => progress != 1;
/// Returns a copy of this instance with the given parameters
/// updated
/// updated.
AssetsManagerState copyWith({
List<Future>? loadables,
List<Future>? loaded,
int? assetsCount,
int? loaded,
}) {
return AssetsManagerState(
loadables: loadables ?? this.loadables,
assetsCount: assetsCount ?? this.assetsCount,
loaded: loaded ?? this.loaded,
);
}
@override
List<Object> get props => [loaded, loadables];
List<Object> get props => [loaded, assetsCount];
}

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame_bloc/flame_bloc.dart';
@ -7,9 +9,9 @@ import 'package:pinball_components/pinball_components.dart';
/// {@template focus_data}
/// Defines a [Camera] focus point.
/// {@endtemplate}
class FocusData {
/// {@template focus_data}
FocusData({
class _FocusData {
/// {@macro focus_data}
const _FocusData({
required this.zoom,
required this.position,
});
@ -24,7 +26,11 @@ class FocusData {
/// Changes the game focus when the [GameBloc] status changes.
class CameraFocusingBehavior extends Component
with FlameBlocListenable<GameBloc, GameState>, HasGameRef {
late final Map<String, FocusData> _foci;
final Map<GameStatus, _FocusData> _foci = {};
GameStatus? _activeFocus;
final _previousSize = Vector2.zero();
@override
bool listenWhen(GameState? previousState, GameState newState) {
@ -32,51 +38,62 @@ class CameraFocusingBehavior extends Component
}
@override
void onNewState(GameState state) {
switch (state.status) {
case GameStatus.waiting:
break;
case GameStatus.playing:
_zoom(_foci['game']!);
break;
case GameStatus.gameOver:
_zoom(_foci['backbox']!);
break;
}
}
void onNewState(GameState state) => _zoomTo(state.status);
@override
Future<void> onLoad() async {
await super.onLoad();
_foci = {
'game': FocusData(
zoom: gameRef.size.y / 16,
position: Vector2(0, -7.8),
),
'waiting': FocusData(
zoom: gameRef.size.y / 18,
void onGameResize(Vector2 size) {
super.onGameResize(size);
if (size == _previousSize) {
return;
}
_previousSize.setFrom(size);
final maxWidth = size.x / 90;
final maxHeight = size.y / 160;
final scale = min(maxHeight, maxWidth);
_foci.addAll({
GameStatus.waiting: _FocusData(
zoom: scale + (maxWidth > maxHeight ? 0.3 : -0.5),
position: Vector2(0, -112),
),
'backbox': FocusData(
zoom: gameRef.size.y / 10,
GameStatus.playing: _FocusData(
zoom: scale + (maxWidth > maxHeight ? 0.5 : -0.2),
position: Vector2(0, -7.8),
),
GameStatus.gameOver: _FocusData(
zoom: scale + (maxWidth > maxHeight ? 2.8 : 3.3),
position: Vector2(0, -111),
),
};
});
if (_activeFocus != null) {
_snap(_activeFocus!);
}
}
_snap(_foci['waiting']!);
@override
Future<void> onLoad() async {
await super.onLoad();
_snap(GameStatus.waiting);
}
void _snap(FocusData data) {
void _snap(GameStatus focusKey) {
final focusData = _foci[_activeFocus = focusKey]!;
gameRef.camera
..speed = 100
..followVector2(data.position)
..zoom = data.zoom;
..followVector2(focusData.position)
..zoom = focusData.zoom;
}
void _zoom(FocusData data) {
final zoom = CameraZoom(value: data.zoom);
void _zoomTo(GameStatus focusKey) {
final focusData = _foci[_activeFocus = focusKey]!;
final zoom = CameraZoom(value: focusData.zoom);
zoom.completed.then((_) {
gameRef.camera.moveTo(data.position);
gameRef.camera.moveTo(focusData.position);
});
add(zoom);
}

@ -6,158 +6,196 @@ import 'package:pinball_theme/pinball_theme.dart' hide Assets;
/// Add methods to help loading and caching game assets.
extension PinballGameAssetsX on PinballGame {
/// Returns a list of assets to be loaded
List<Future<Image>> preLoadAssets() {
List<Future<Image> Function()> preLoadAssets() {
const dashTheme = DashTheme();
const sparkyTheme = SparkyTheme();
const androidTheme = AndroidTheme();
const dinoTheme = DinoTheme();
return [
images.load(components.Assets.images.boardBackground.keyName),
images.load(components.Assets.images.ball.flameEffect.keyName),
images.load(components.Assets.images.signpost.inactive.keyName),
images.load(components.Assets.images.signpost.active1.keyName),
images.load(components.Assets.images.signpost.active2.keyName),
images.load(components.Assets.images.signpost.active3.keyName),
images.load(components.Assets.images.flipper.left.keyName),
images.load(components.Assets.images.flipper.right.keyName),
images.load(components.Assets.images.baseboard.left.keyName),
images.load(components.Assets.images.baseboard.right.keyName),
images.load(components.Assets.images.kicker.left.lit.keyName),
images.load(components.Assets.images.kicker.left.dimmed.keyName),
images.load(components.Assets.images.kicker.right.lit.keyName),
images.load(components.Assets.images.kicker.right.dimmed.keyName),
images.load(components.Assets.images.slingshot.upper.keyName),
images.load(components.Assets.images.slingshot.lower.keyName),
images.load(components.Assets.images.launchRamp.ramp.keyName),
images.load(
components.Assets.images.launchRamp.foregroundRailing.keyName,
),
images.load(
components.Assets.images.launchRamp.backgroundRailing.keyName,
),
images.load(components.Assets.images.dino.bottomWall.keyName),
images.load(components.Assets.images.dino.topWall.keyName),
images.load(components.Assets.images.dino.topWallTunnel.keyName),
images.load(components.Assets.images.dino.animatronic.head.keyName),
images.load(components.Assets.images.dino.animatronic.mouth.keyName),
images.load(components.Assets.images.dash.animatronic.keyName),
images.load(components.Assets.images.dash.bumper.a.active.keyName),
images.load(components.Assets.images.dash.bumper.a.inactive.keyName),
images.load(components.Assets.images.dash.bumper.b.active.keyName),
images.load(components.Assets.images.dash.bumper.b.inactive.keyName),
images.load(components.Assets.images.dash.bumper.main.active.keyName),
images.load(components.Assets.images.dash.bumper.main.inactive.keyName),
images.load(components.Assets.images.plunger.plunger.keyName),
images.load(components.Assets.images.plunger.rocket.keyName),
images.load(components.Assets.images.boundary.bottom.keyName),
images.load(components.Assets.images.boundary.outer.keyName),
images.load(components.Assets.images.boundary.outerBottom.keyName),
images.load(components.Assets.images.android.spaceship.saucer.keyName),
images
() => images.load(components.Assets.images.boardBackground.keyName),
() => images.load(components.Assets.images.ball.flameEffect.keyName),
() => images.load(components.Assets.images.signpost.inactive.keyName),
() => images.load(components.Assets.images.signpost.active1.keyName),
() => images.load(components.Assets.images.signpost.active2.keyName),
() => images.load(components.Assets.images.signpost.active3.keyName),
() => images.load(components.Assets.images.flipper.left.keyName),
() => images.load(components.Assets.images.flipper.right.keyName),
() => images.load(components.Assets.images.baseboard.left.keyName),
() => images.load(components.Assets.images.baseboard.right.keyName),
() => images.load(components.Assets.images.kicker.left.lit.keyName),
() => images.load(components.Assets.images.kicker.left.dimmed.keyName),
() => images.load(components.Assets.images.kicker.right.lit.keyName),
() => images.load(components.Assets.images.kicker.right.dimmed.keyName),
() => images.load(components.Assets.images.slingshot.upper.keyName),
() => images.load(components.Assets.images.slingshot.lower.keyName),
() => images.load(components.Assets.images.launchRamp.ramp.keyName),
() => images.load(
components.Assets.images.launchRamp.foregroundRailing.keyName,
),
() => images.load(
components.Assets.images.launchRamp.backgroundRailing.keyName,
),
() => images.load(components.Assets.images.dino.bottomWall.keyName),
() => images.load(components.Assets.images.dino.topWall.keyName),
() => images.load(components.Assets.images.dino.topWallTunnel.keyName),
() => images.load(components.Assets.images.dino.animatronic.head.keyName),
() =>
images.load(components.Assets.images.dino.animatronic.mouth.keyName),
() => images.load(components.Assets.images.dash.animatronic.keyName),
() => images.load(components.Assets.images.dash.bumper.a.active.keyName),
() =>
images.load(components.Assets.images.dash.bumper.a.inactive.keyName),
() => images.load(components.Assets.images.dash.bumper.b.active.keyName),
() =>
images.load(components.Assets.images.dash.bumper.b.inactive.keyName),
() =>
images.load(components.Assets.images.dash.bumper.main.active.keyName),
() => images
.load(components.Assets.images.dash.bumper.main.inactive.keyName),
() => images.load(components.Assets.images.plunger.plunger.keyName),
() => images.load(components.Assets.images.plunger.rocket.keyName),
() => images.load(components.Assets.images.boundary.bottom.keyName),
() => images.load(components.Assets.images.boundary.outer.keyName),
() => images.load(components.Assets.images.boundary.outerBottom.keyName),
() => images
.load(components.Assets.images.android.spaceship.saucer.keyName),
() => images
.load(components.Assets.images.android.spaceship.animatronic.keyName),
images.load(components.Assets.images.android.spaceship.lightBeam.keyName),
images.load(components.Assets.images.android.ramp.boardOpening.keyName),
images.load(
components.Assets.images.android.ramp.railingForeground.keyName,
),
images.load(
components.Assets.images.android.ramp.railingBackground.keyName,
),
images.load(components.Assets.images.android.ramp.main.keyName),
images.load(components.Assets.images.android.ramp.arrow.inactive.keyName),
images.load(
components.Assets.images.android.ramp.arrow.active1.keyName,
),
images.load(
components.Assets.images.android.ramp.arrow.active2.keyName,
),
images.load(
components.Assets.images.android.ramp.arrow.active3.keyName,
),
images.load(
components.Assets.images.android.ramp.arrow.active4.keyName,
),
images.load(
components.Assets.images.android.ramp.arrow.active5.keyName,
),
images.load(components.Assets.images.android.rail.main.keyName),
images.load(components.Assets.images.android.rail.exit.keyName),
images.load(components.Assets.images.android.bumper.a.lit.keyName),
images.load(components.Assets.images.android.bumper.a.dimmed.keyName),
images.load(components.Assets.images.android.bumper.b.lit.keyName),
images.load(components.Assets.images.android.bumper.b.dimmed.keyName),
images.load(components.Assets.images.android.bumper.cow.lit.keyName),
images.load(components.Assets.images.android.bumper.cow.dimmed.keyName),
images.load(components.Assets.images.sparky.computer.top.keyName),
images.load(components.Assets.images.sparky.computer.base.keyName),
images.load(components.Assets.images.sparky.computer.glow.keyName),
images.load(components.Assets.images.sparky.animatronic.keyName),
images.load(components.Assets.images.sparky.bumper.a.lit.keyName),
images.load(components.Assets.images.sparky.bumper.a.dimmed.keyName),
images.load(components.Assets.images.sparky.bumper.b.lit.keyName),
images.load(components.Assets.images.sparky.bumper.b.dimmed.keyName),
images.load(components.Assets.images.sparky.bumper.c.lit.keyName),
images.load(components.Assets.images.sparky.bumper.c.dimmed.keyName),
images.load(components.Assets.images.backbox.marquee.keyName),
images.load(components.Assets.images.backbox.displayDivider.keyName),
images.load(components.Assets.images.backbox.button.facebook.keyName),
images.load(components.Assets.images.backbox.button.twitter.keyName),
images.load(
components.Assets.images.backbox.displayTitleDecoration.keyName,
),
images.load(components.Assets.images.googleWord.letter1.lit.keyName),
images.load(components.Assets.images.googleWord.letter1.dimmed.keyName),
images.load(components.Assets.images.googleWord.letter2.lit.keyName),
images.load(components.Assets.images.googleWord.letter2.dimmed.keyName),
images.load(components.Assets.images.googleWord.letter3.lit.keyName),
images.load(components.Assets.images.googleWord.letter3.dimmed.keyName),
images.load(components.Assets.images.googleWord.letter4.lit.keyName),
images.load(components.Assets.images.googleWord.letter4.dimmed.keyName),
images.load(components.Assets.images.googleWord.letter5.lit.keyName),
images.load(components.Assets.images.googleWord.letter5.dimmed.keyName),
images.load(components.Assets.images.googleWord.letter6.lit.keyName),
images.load(components.Assets.images.googleWord.letter6.dimmed.keyName),
images.load(components.Assets.images.googleRollover.left.decal.keyName),
images.load(components.Assets.images.googleRollover.left.pin.keyName),
images.load(components.Assets.images.googleRollover.right.decal.keyName),
images.load(components.Assets.images.googleRollover.right.pin.keyName),
images.load(components.Assets.images.multiball.lit.keyName),
images.load(components.Assets.images.multiball.dimmed.keyName),
images.load(components.Assets.images.multiplier.x2.lit.keyName),
images.load(components.Assets.images.multiplier.x2.dimmed.keyName),
images.load(components.Assets.images.multiplier.x3.lit.keyName),
images.load(components.Assets.images.multiplier.x3.dimmed.keyName),
images.load(components.Assets.images.multiplier.x4.lit.keyName),
images.load(components.Assets.images.multiplier.x4.dimmed.keyName),
images.load(components.Assets.images.multiplier.x5.lit.keyName),
images.load(components.Assets.images.multiplier.x5.dimmed.keyName),
images.load(components.Assets.images.multiplier.x6.lit.keyName),
images.load(components.Assets.images.multiplier.x6.dimmed.keyName),
images.load(components.Assets.images.score.fiveThousand.keyName),
images.load(components.Assets.images.score.twentyThousand.keyName),
images.load(components.Assets.images.score.twoHundredThousand.keyName),
images.load(components.Assets.images.score.oneMillion.keyName),
images.load(components.Assets.images.flapper.backSupport.keyName),
images.load(components.Assets.images.flapper.frontSupport.keyName),
images.load(components.Assets.images.flapper.flap.keyName),
images.load(components.Assets.images.skillShot.decal.keyName),
images.load(components.Assets.images.skillShot.pin.keyName),
images.load(components.Assets.images.skillShot.lit.keyName),
images.load(components.Assets.images.skillShot.dimmed.keyName),
images.load(components.Assets.images.displayArrows.arrowLeft.keyName),
images.load(components.Assets.images.displayArrows.arrowRight.keyName),
images.load(androidTheme.leaderboardIcon.keyName),
images.load(androidTheme.ball.keyName),
images.load(dashTheme.leaderboardIcon.keyName),
images.load(dashTheme.ball.keyName),
images.load(dinoTheme.leaderboardIcon.keyName),
images.load(dinoTheme.ball.keyName),
images.load(sparkyTheme.leaderboardIcon.keyName),
images.load(sparkyTheme.ball.keyName),
images.load(androidTheme.background.keyName),
images.load(dashTheme.background.keyName),
images.load(dinoTheme.background.keyName),
images.load(sparkyTheme.background.keyName),
() => images
.load(components.Assets.images.android.spaceship.lightBeam.keyName),
() => images
.load(components.Assets.images.android.ramp.boardOpening.keyName),
() => images.load(
components.Assets.images.android.ramp.railingForeground.keyName,
),
() => images.load(
components.Assets.images.android.ramp.railingBackground.keyName,
),
() => images.load(components.Assets.images.android.ramp.main.keyName),
() => images
.load(components.Assets.images.android.ramp.arrow.inactive.keyName),
() => images.load(
components.Assets.images.android.ramp.arrow.active1.keyName,
),
() => images.load(
components.Assets.images.android.ramp.arrow.active2.keyName,
),
() => images.load(
components.Assets.images.android.ramp.arrow.active3.keyName,
),
() => images.load(
components.Assets.images.android.ramp.arrow.active4.keyName,
),
() => images.load(
components.Assets.images.android.ramp.arrow.active5.keyName,
),
() => images.load(components.Assets.images.android.rail.main.keyName),
() => images.load(components.Assets.images.android.rail.exit.keyName),
() => images.load(components.Assets.images.android.bumper.a.lit.keyName),
() =>
images.load(components.Assets.images.android.bumper.a.dimmed.keyName),
() => images.load(components.Assets.images.android.bumper.b.lit.keyName),
() =>
images.load(components.Assets.images.android.bumper.b.dimmed.keyName),
() =>
images.load(components.Assets.images.android.bumper.cow.lit.keyName),
() => images
.load(components.Assets.images.android.bumper.cow.dimmed.keyName),
() => images.load(components.Assets.images.sparky.computer.top.keyName),
() => images.load(components.Assets.images.sparky.computer.base.keyName),
() => images.load(components.Assets.images.sparky.computer.glow.keyName),
() => images.load(components.Assets.images.sparky.animatronic.keyName),
() => images.load(components.Assets.images.sparky.bumper.a.lit.keyName),
() =>
images.load(components.Assets.images.sparky.bumper.a.dimmed.keyName),
() => images.load(components.Assets.images.sparky.bumper.b.lit.keyName),
() =>
images.load(components.Assets.images.sparky.bumper.b.dimmed.keyName),
() => images.load(components.Assets.images.sparky.bumper.c.lit.keyName),
() =>
images.load(components.Assets.images.sparky.bumper.c.dimmed.keyName),
() => images.load(components.Assets.images.backbox.marquee.keyName),
() =>
images.load(components.Assets.images.backbox.displayDivider.keyName),
() =>
images.load(components.Assets.images.backbox.button.facebook.keyName),
() =>
images.load(components.Assets.images.backbox.button.twitter.keyName),
() => images.load(
components.Assets.images.backbox.displayTitleDecoration.keyName,
),
() =>
images.load(components.Assets.images.googleWord.letter1.lit.keyName),
() => images
.load(components.Assets.images.googleWord.letter1.dimmed.keyName),
() =>
images.load(components.Assets.images.googleWord.letter2.lit.keyName),
() => images
.load(components.Assets.images.googleWord.letter2.dimmed.keyName),
() =>
images.load(components.Assets.images.googleWord.letter3.lit.keyName),
() => images
.load(components.Assets.images.googleWord.letter3.dimmed.keyName),
() =>
images.load(components.Assets.images.googleWord.letter4.lit.keyName),
() => images
.load(components.Assets.images.googleWord.letter4.dimmed.keyName),
() =>
images.load(components.Assets.images.googleWord.letter5.lit.keyName),
() => images
.load(components.Assets.images.googleWord.letter5.dimmed.keyName),
() =>
images.load(components.Assets.images.googleWord.letter6.lit.keyName),
() => images
.load(components.Assets.images.googleWord.letter6.dimmed.keyName),
() => images
.load(components.Assets.images.googleRollover.left.decal.keyName),
() =>
images.load(components.Assets.images.googleRollover.left.pin.keyName),
() => images
.load(components.Assets.images.googleRollover.right.decal.keyName),
() => images
.load(components.Assets.images.googleRollover.right.pin.keyName),
() => images.load(components.Assets.images.multiball.lit.keyName),
() => images.load(components.Assets.images.multiball.dimmed.keyName),
() => images.load(components.Assets.images.multiplier.x2.lit.keyName),
() => images.load(components.Assets.images.multiplier.x2.dimmed.keyName),
() => images.load(components.Assets.images.multiplier.x3.lit.keyName),
() => images.load(components.Assets.images.multiplier.x3.dimmed.keyName),
() => images.load(components.Assets.images.multiplier.x4.lit.keyName),
() => images.load(components.Assets.images.multiplier.x4.dimmed.keyName),
() => images.load(components.Assets.images.multiplier.x5.lit.keyName),
() => images.load(components.Assets.images.multiplier.x5.dimmed.keyName),
() => images.load(components.Assets.images.multiplier.x6.lit.keyName),
() => images.load(components.Assets.images.multiplier.x6.dimmed.keyName),
() => images.load(components.Assets.images.score.fiveThousand.keyName),
() => images.load(components.Assets.images.score.twentyThousand.keyName),
() => images
.load(components.Assets.images.score.twoHundredThousand.keyName),
() => images.load(components.Assets.images.score.oneMillion.keyName),
() => images.load(components.Assets.images.flapper.backSupport.keyName),
() => images.load(components.Assets.images.flapper.frontSupport.keyName),
() => images.load(components.Assets.images.flapper.flap.keyName),
() => images.load(components.Assets.images.skillShot.decal.keyName),
() => images.load(components.Assets.images.skillShot.pin.keyName),
() => images.load(components.Assets.images.skillShot.lit.keyName),
() => images.load(components.Assets.images.skillShot.dimmed.keyName),
() =>
images.load(components.Assets.images.displayArrows.arrowLeft.keyName),
() => images
.load(components.Assets.images.displayArrows.arrowRight.keyName),
() => images.load(androidTheme.leaderboardIcon.keyName),
() => images.load(androidTheme.ball.keyName),
() => images.load(dashTheme.leaderboardIcon.keyName),
() => images.load(dashTheme.ball.keyName),
() => images.load(dinoTheme.leaderboardIcon.keyName),
() => images.load(dinoTheme.ball.keyName),
() => images.load(sparkyTheme.leaderboardIcon.keyName),
() => images.load(sparkyTheme.ball.keyName),
() => images.load(androidTheme.background.keyName),
() => images.load(dashTheme.background.keyName),
() => images.load(dinoTheme.background.keyName),
() => images.load(sparkyTheme.background.keyName),
];
}
}

@ -72,14 +72,16 @@ class BonusAnimation extends StatefulWidget {
final VoidCallback? _onCompleted;
/// Returns a list of assets to be loaded for animations.
static List<Future> loadAssets() {
static List<Future Function()> loadAssets() {
Flame.images.prefix = '';
return [
Flame.images.load(Assets.images.bonusAnimation.dashNest.keyName),
Flame.images.load(Assets.images.bonusAnimation.sparkyTurboCharge.keyName),
Flame.images.load(Assets.images.bonusAnimation.dinoChomp.keyName),
Flame.images.load(Assets.images.bonusAnimation.androidSpaceship.keyName),
Flame.images.load(Assets.images.bonusAnimation.googleWord.keyName),
() => Flame.images.load(Assets.images.bonusAnimation.dashNest.keyName),
() => Flame.images
.load(Assets.images.bonusAnimation.sparkyTurboCharge.keyName),
() => Flame.images.load(Assets.images.bonusAnimation.dinoChomp.keyName),
() => Flame.images
.load(Assets.images.bonusAnimation.androidSpaceship.keyName),
() => Flame.images.load(Assets.images.bonusAnimation.googleWord.keyName),
];
}

@ -22,12 +22,12 @@ class SelectedCharacter extends StatefulWidget {
State<SelectedCharacter> createState() => _SelectedCharacterState();
/// Returns a list of assets to be loaded.
static List<Future> loadAssets() {
static List<Future Function()> loadAssets() {
return [
Flame.images.load(const DashTheme().animation.keyName),
Flame.images.load(const AndroidTheme().animation.keyName),
Flame.images.load(const DinoTheme().animation.keyName),
Flame.images.load(const SparkyTheme().animation.keyName),
() => Flame.images.load(const DashTheme().animation.keyName),
() => Flame.images.load(const AndroidTheme().animation.keyName),
() => Flame.images.load(const DinoTheme().animation.keyName),
() => Flame.images.load(const SparkyTheme().animation.keyName),
];
}
}

@ -334,10 +334,10 @@ class PinballAudioPlayer {
late final Map<PinballAudio, _Audio> audios;
/// Loads the sounds effects into the memory.
List<Future<void>> load() {
List<Future<void> Function()> load() {
_configureAudioCache(FlameAudio.audioCache);
return audios.values.map((a) => a.load()).toList();
return audios.values.map((a) => a.load).toList();
}
/// Plays the received audio.

@ -102,7 +102,9 @@ void main() {
group('load', () {
test('creates the bumpers pools', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
verify(
() => createAudioPool.onCall(
@ -122,7 +124,9 @@ void main() {
});
test('creates the kicker pools', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
verify(
() => createAudioPool.onCall(
@ -142,7 +146,9 @@ void main() {
});
test('configures the audio cache instance', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
verify(() => configureAudioCache.onCall(FlameAudio.audioCache))
.called(1);
@ -154,13 +160,17 @@ void main() {
playSingleAudio: playSingleAudio.onCall,
preCacheSingleAudio: preCacheSingleAudio.onCall,
);
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
expect(FlameAudio.audioCache.prefix, equals(''));
});
test('pre cache the assets', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
verify(
() => preCacheSingleAudio
@ -242,7 +252,9 @@ void main() {
group('when seed is true', () {
test('plays the bumper A sound pool', () async {
when(seed.nextBool).thenReturn(true);
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.bumper);
verify(() => bumperAPool.start(volume: 0.6)).called(1);
@ -252,7 +264,9 @@ void main() {
group('when seed is false', () {
test('plays the bumper B sound pool', () async {
when(seed.nextBool).thenReturn(false);
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.bumper);
verify(() => bumperBPool.start(volume: 0.6)).called(1);
@ -291,7 +305,9 @@ void main() {
group('when seed is true', () {
test('plays the kicker A sound pool', () async {
when(seed.nextBool).thenReturn(true);
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.kicker);
verify(() => kickerAPool.start(volume: 0.6)).called(1);
@ -301,7 +317,9 @@ void main() {
group('when seed is false', () {
test('plays the kicker B sound pool', () async {
when(seed.nextBool).thenReturn(false);
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.kicker);
verify(() => kickerBPool.start(volume: 0.6)).called(1);
@ -311,7 +329,9 @@ void main() {
group('cow moo', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.cowMoo);
verify(
@ -324,7 +344,9 @@ void main() {
final clock = _MockClock();
await withClock(clock, () async {
when(clock.now).thenReturn(DateTime(2022));
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer
..play(PinballAudio.cowMoo)
..play(PinballAudio.cowMoo);
@ -347,7 +369,9 @@ void main() {
group('google', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.google);
verify(
@ -361,7 +385,9 @@ void main() {
group('sparky', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.sparky);
verify(
@ -375,7 +401,9 @@ void main() {
group('dino', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.dino);
verify(
@ -389,7 +417,9 @@ void main() {
group('android', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.android);
verify(
@ -403,7 +433,9 @@ void main() {
group('dash', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.dash);
verify(
@ -417,7 +449,9 @@ void main() {
group('launcher', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.launcher);
verify(
@ -431,7 +465,9 @@ void main() {
group('rollover', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.rollover);
verify(
@ -445,7 +481,9 @@ void main() {
group('ioPinballVoiceOver', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.ioPinballVoiceOver);
verify(
@ -459,7 +497,9 @@ void main() {
group('gameOverVoiceOver', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.gameOverVoiceOver);
verify(
@ -473,7 +513,9 @@ void main() {
group('backgroundMusic', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer.play(PinballAudio.backgroundMusic);
verify(
@ -485,7 +527,9 @@ void main() {
});
test('plays only once', () async {
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer
..play(PinballAudio.backgroundMusic)
..play(PinballAudio.backgroundMusic);
@ -503,7 +547,9 @@ void main() {
'throws assertions error when playing an unregistered audio',
() async {
audioPlayer.audios.remove(PinballAudio.google);
await Future.wait(audioPlayer.load());
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
expect(
() => audioPlayer.play(PinballAudio.google),

@ -57,7 +57,7 @@ class _TurboChargeSpriteAnimationComponent extends SpriteAnimationComponent
Future<void> onLoad() async {
await super.onLoad();
final spriteSheet = await gameRef.images.load(
final spriteSheet = gameRef.images.fromCache(
Assets.images.ball.flameEffect.keyName,
);

@ -109,7 +109,7 @@ class _OuterBoundary extends BodyComponent with InitialPosition, ZIndex {
final topLeftCurve = BezierCurveShape(
controlPoints: [
topWall.vertex1,
topWall.vertex2,
Vector2(-31.5, -69.9),
Vector2(-32.3, -57.2),
],
@ -123,7 +123,7 @@ class _OuterBoundary extends BodyComponent with InitialPosition, ZIndex {
final upperLeftWallCurve = BezierCurveShape(
controlPoints: [
topLeftWall.vertex1,
topLeftWall.vertex2,
Vector2(-33.9, -40.7),
Vector2(-32.5, -39),
],

@ -108,7 +108,7 @@ class _PlungerSpriteAnimationGroupComponent
@override
Future<void> onLoad() async {
await super.onLoad();
final spriteSheet = await gameRef.images.load(
final spriteSheet = gameRef.images.fromCache(
Assets.images.plunger.plunger.keyName,
);
const amountPerRow = 20;

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flame/input.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
@ -29,11 +30,14 @@ class AndroidSpaceshipGame extends BallGame {
await super.onLoad();
camera.followVector2(Vector2.zero());
await addAll(
[
AndroidSpaceship(position: Vector2.zero()),
AndroidAnimatronic(),
],
await add(
FlameBlocProvider<AndroidSpaceshipCubit, AndroidSpaceshipState>(
create: AndroidSpaceshipCubit.new,
children: [
AndroidSpaceship(position: Vector2.zero()),
AndroidAnimatronic(),
],
),
);
await traceAllBodies();

@ -10,6 +10,11 @@ class LaunchRampGame extends BallGame {
: super(
ballPriority: ZIndexes.ballOnLaunchRamp,
ballLayer: Layer.launcher,
imagesFileNames: [
Assets.images.launchRamp.ramp.keyName,
Assets.images.launchRamp.backgroundRailing.keyName,
Assets.images.launchRamp.foregroundRailing.keyName,
],
);
static const description = '''

@ -1,10 +1,18 @@
import 'package:flame/input.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class PlungerGame extends BallGame
with HasKeyboardHandlerComponents, Traceable {
PlungerGame()
: super(
imagesFileNames: [
Assets.images.plunger.plunger.keyName,
],
);
static const description = '''
Shows how Plunger is rendered.
@ -19,7 +27,12 @@ class PlungerGame extends BallGame
final center = screenToWorld(camera.viewport.canvasSize! / 2);
final plunger = Plunger()
..initialPosition = Vector2(center.x - 8.8, center.y);
await add(plunger);
await add(
FlameBlocProvider<PlungerCubit, PlungerState>(
create: PlungerCubit.new,
children: [plunger],
),
);
await plunger.add(PlungerKeyControllingBehavior());
await traceAllBodies();

@ -128,7 +128,7 @@ packages:
source: hosted
version: "1.0.2"
flame_bloc:
dependency: transitive
dependency: "direct main"
description:
name: flame_bloc
url: "https://pub.dartlang.org"

@ -9,6 +9,7 @@ environment:
dependencies:
dashbook: ^0.1.7
flame: ^1.1.1
flame_bloc: ^1.4.0
flame_forge2d:
git:
url: https://github.com/flame-engine/flame

@ -14,8 +14,11 @@ void main() {
group(
'BallTurboChargingBehavior',
() {
final asset = theme.Assets.images.dash.ball.keyName;
final flameTester = FlameTester(() => TestGame([asset]));
final assets = [
theme.Assets.images.dash.ball.keyName,
Assets.images.ball.flameEffect.keyName,
];
final flameTester = FlameTester(() => TestGame(assets));
test('can be instantiated', () {
expect(

@ -10,7 +10,8 @@ import '../../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
final asset = Assets.images.plunger.plunger.keyName;
final flameTester = FlameTester(() => TestGame([asset]));
group('Plunger', () {
test('can be instantiated', () {
@ -69,6 +70,7 @@ void main() {
flameTester.testGameWidget(
'pulling',
setUp: (game, tester) async {
await game.images.load(asset);
await game.ensureAdd(Plunger());
game.camera.followVector2(Vector2.zero());
game.camera.zoom = 4.1;
@ -92,6 +94,7 @@ void main() {
flameTester.testGameWidget(
'releasing',
setUp: (game, tester) async {
await game.images.load(asset);
await game.ensureAdd(Plunger());
game.camera.followVector2(Vector2.zero());
game.camera.zoom = 4.1;

@ -30,8 +30,10 @@ class PinballButton extends StatelessWidget {
),
),
child: Center(
child: GestureDetector(
child: InkWell(
onTap: onTap,
highlightColor: PinballColors.transparent,
splashColor: PinballColors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 32,

@ -53,8 +53,10 @@ class PinballDpadButton extends StatelessWidget {
Widget build(BuildContext context) {
return Material(
color: PinballColors.transparent,
child: GestureDetector(
child: InkWell(
onTap: onTap,
highlightColor: PinballColors.transparent,
splashColor: PinballColors.transparent,
child: Image.asset(
direction.toAsset(),
width: 60,

@ -37,7 +37,7 @@ void main() {
shareRepository = _MockShareRepository();
pinballAudioPlayer = _MockPinballAudioPlayer();
platformHelper = _MockPlatformHelper();
when(pinballAudioPlayer.load).thenAnswer((_) => [Future.value()]);
when(pinballAudioPlayer.load).thenAnswer((_) => [Future.value]);
});
testWidgets('renders PinballGamePage', (tester) async {

@ -7,7 +7,7 @@ void main() {
group('AssetsManagerState', () {
test('can be instantiated', () {
expect(
AssetsManagerState(loadables: const [], loaded: const []),
AssetsManagerState(assetsCount: 0, loaded: 0),
isNotNull,
);
});
@ -17,22 +17,19 @@ void main() {
AssetsManagerState.initial(),
equals(
AssetsManagerState(
loadables: const [],
loaded: const [],
assetsCount: 0,
loaded: 0,
),
),
);
});
group('progress', () {
final future1 = Future<void>.value();
final future2 = Future<void>.value();
test('returns 0 when no future is loaded', () {
expect(
AssetsManagerState(
loadables: [future1, future2],
loaded: const [],
assetsCount: 2,
loaded: 0,
).progress,
equals(0),
);
@ -41,8 +38,8 @@ void main() {
test('returns the correct value when some of the futures are loaded', () {
expect(
AssetsManagerState(
loadables: [future1, future2],
loaded: [future1],
assetsCount: 2,
loaded: 1,
).progress,
equals(0.5),
);
@ -51,8 +48,8 @@ void main() {
test('returns the 1 when all futures are loaded', () {
expect(
AssetsManagerState(
loadables: [future1, future2],
loaded: [future1, future2],
assetsCount: 2,
loaded: 2,
).progress,
equals(1),
);
@ -60,18 +57,16 @@ void main() {
});
group('copyWith', () {
final future = Future<void>.value();
test('returns a copy with the updated loadables', () {
test('returns a copy with the updated assetsCount', () {
expect(
AssetsManagerState(
loadables: const [],
loaded: const [],
).copyWith(loadables: [future]),
assetsCount: 0,
loaded: 0,
).copyWith(assetsCount: 1),
equals(
AssetsManagerState(
loadables: [future],
loaded: const [],
assetsCount: 1,
loaded: 0,
),
),
);
@ -80,13 +75,13 @@ void main() {
test('returns a copy with the updated loaded', () {
expect(
AssetsManagerState(
loadables: const [],
loaded: const [],
).copyWith(loaded: [future]),
assetsCount: 0,
loaded: 0,
).copyWith(loaded: 1),
equals(
AssetsManagerState(
loadables: const [],
loaded: [future],
assetsCount: 0,
loaded: 1,
),
),
);
@ -94,47 +89,29 @@ void main() {
});
test('supports value comparison', () {
final future1 = Future<void>.value();
final future2 = Future<void>.value();
expect(
AssetsManagerState(
loadables: const [],
loaded: const [],
assetsCount: 0,
loaded: 0,
),
equals(
AssetsManagerState(
loadables: const [],
loaded: const [],
),
),
);
expect(
AssetsManagerState(
loadables: [future1],
loaded: const [],
),
isNot(
equals(
AssetsManagerState(
loadables: [future2],
loaded: const [],
),
assetsCount: 0,
loaded: 0,
),
),
);
expect(
AssetsManagerState(
loadables: const [],
loaded: [future1],
assetsCount: 1,
loaded: 0,
),
isNot(
equals(
AssetsManagerState(
loadables: const [],
loaded: [future2],
assetsCount: 1,
loaded: 1,
),
),
),

@ -12,9 +12,9 @@ void main() {
late AssetsManagerCubit assetsManagerCubit;
setUp(() {
final initialAssetsState = AssetsManagerState(
loadables: [Future<void>.value()],
loaded: const [],
const initialAssetsState = AssetsManagerState(
assetsCount: 1,
loaded: 0,
);
assetsManagerCubit = _MockAssetsManagerCubit();
whenListen(

@ -53,6 +53,20 @@ void main() {
},
);
flameTester.test('sets zoom on resize', (game) async {
final behavior = CameraFocusingBehavior();
await game.ensureAdd(
FlameBlocProvider<GameBloc, GameState>.value(
value: GameBloc(),
children: [behavior],
),
);
game.onGameResize(game.canvasSize * 2);
expect(game.camera.zoom, equals(6.55));
});
flameTester.test(
'listenWhen only listens when status changes',
(game) async {

@ -35,7 +35,7 @@ class _TestPinballGame extends PinballGame {
@override
Future<void> onLoad() async {
images.prefix = '';
final futures = preLoadAssets();
final futures = preLoadAssets().map((loadableBuilder) => loadableBuilder());
await Future.wait<void>(futures);
await super.onLoad();
}
@ -56,7 +56,7 @@ class _TestDebugPinballGame extends DebugPinballGame {
@override
Future<void> onLoad() async {
images.prefix = '';
final futures = preLoadAssets();
final futures = preLoadAssets().map((loadableBuilder) => loadableBuilder());
await Future.wait<void>(futures);
await super.onLoad();
}
@ -215,7 +215,8 @@ void main() {
'paints sprites with FilterQuality.medium',
setUp: (game, tester) async {
game.images.prefix = '';
final futures = game.preLoadAssets();
final futures =
game.preLoadAssets().map((loadableBuilder) => loadableBuilder());
await Future.wait<void>(futures);
await game.ready();

@ -37,9 +37,13 @@ class _TestPinballGame extends PinballGame {
images.prefix = '';
final futures = [
...preLoadAssets(),
preFetchLeaderboard(),
...BonusAnimation.loadAssets(),
...SelectedCharacter.loadAssets(),
preFetchLeaderboard,
];
await Future.wait<void>(futures);
await Future.wait<void>(
futures.map((loadableBuilder) => loadableBuilder()).toList(),
);
return super.onLoad();
}
@ -78,7 +82,9 @@ void main() {
late GameBloc gameBloc;
setUp(() async {
await Future.wait<void>(game.preLoadAssets());
await Future.wait<void>(
game.preLoadAssets().map((loadableBuilder) => loadableBuilder()),
);
characterThemeCubit = _MockCharacterThemeCubit();
gameBloc = _MockGameBloc();
@ -122,8 +128,8 @@ void main() {
(tester) async {
final assetsManagerCubit = _MockAssetsManagerCubit();
final initialAssetsState = AssetsManagerState(
loadables: [Future<void>.value()],
loaded: const [],
assetsCount: 1,
loaded: 0,
);
whenListen(
assetsManagerCubit,
@ -146,8 +152,8 @@ void main() {
final startGameBloc = _MockStartGameBloc();
final loadedAssetsState = AssetsManagerState(
loadables: [Future<void>.value()],
loaded: [Future<void>.value()],
assetsCount: 1,
loaded: 1,
);
whenListen(
assetsManagerCubit,
@ -179,7 +185,9 @@ void main() {
final startGameBloc = _MockStartGameBloc();
setUp(() async {
await Future.wait<void>(game.preLoadAssets());
await Future.wait<void>(
game.preLoadAssets().map((loadableBuilder) => loadableBuilder()),
);
whenListen(
gameBloc,

@ -34,15 +34,15 @@ class _MockPlatformHelper extends Mock implements PlatformHelper {}
PinballAudioPlayer _buildDefaultPinballAudioPlayer() {
final audioPlayer = _MockPinballAudioPlayer();
when(audioPlayer.load).thenAnswer((_) => [Future.value()]);
when(audioPlayer.load).thenAnswer((_) => [Future.value]);
return audioPlayer;
}
AssetsManagerCubit _buildDefaultAssetsManagerCubit() {
final cubit = _MockAssetsManagerCubit();
final state = AssetsManagerState(
loadables: [Future<void>.value()],
loaded: [Future<void>.value()],
const state = AssetsManagerState(
assetsCount: 1,
loaded: 1,
);
whenListen(
cubit,

Loading…
Cancel
Save