feat: add arcade backgrounds (#405)
* feat: add arcade backgrounds * chore: lighter assets * chore: swap for smaller assetspull/414/head
@ -1,7 +1,7 @@
|
||||
export 'ball_spawning_behavior.dart';
|
||||
export 'ball_theming_behavior.dart';
|
||||
export 'bonus_ball_spawning_behavior.dart';
|
||||
export 'bonus_noise_behavior.dart';
|
||||
export 'bumper_noise_behavior.dart';
|
||||
export 'camera_focusing_behavior.dart';
|
||||
export 'character_selection_behavior.dart';
|
||||
export 'scoring_behavior.dart';
|
||||
|
@ -0,0 +1,92 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_bloc/flame_bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
import 'package:pinball_theme/pinball_theme.dart' as theme;
|
||||
|
||||
export 'cubit/arcade_background_cubit.dart';
|
||||
|
||||
/// {@template arcade_background}
|
||||
/// Background of the arcade that the pinball machine lives in.
|
||||
/// {@endtemplate}
|
||||
class ArcadeBackground extends Component with ZIndex {
|
||||
/// {@macro arcade_background}
|
||||
ArcadeBackground({String? assetPath})
|
||||
: this._(
|
||||
bloc: ArcadeBackgroundCubit(),
|
||||
assetPath: assetPath,
|
||||
);
|
||||
|
||||
ArcadeBackground._({required this.bloc, String? assetPath})
|
||||
: super(
|
||||
children: [
|
||||
FlameBlocProvider<ArcadeBackgroundCubit,
|
||||
ArcadeBackgroundState>.value(
|
||||
value: bloc,
|
||||
children: [ArcadeBackgroundSpriteComponent(assetPath: assetPath)],
|
||||
)
|
||||
],
|
||||
) {
|
||||
zIndex = ZIndexes.arcadeBackground;
|
||||
}
|
||||
|
||||
/// Creates an [ArcadeBackground] without any behaviors.
|
||||
///
|
||||
/// This can be used for testing [ArcadeBackground]'s behaviors in isolation.
|
||||
@visibleForTesting
|
||||
ArcadeBackground.test({
|
||||
ArcadeBackgroundCubit? bloc,
|
||||
String? assetPath,
|
||||
}) : bloc = bloc ?? ArcadeBackgroundCubit(),
|
||||
super(
|
||||
children: [
|
||||
FlameBlocProvider<ArcadeBackgroundCubit,
|
||||
ArcadeBackgroundState>.value(
|
||||
value: bloc ?? ArcadeBackgroundCubit(),
|
||||
children: [ArcadeBackgroundSpriteComponent(assetPath: assetPath)],
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
/// Bloc to update the arcade background sprite when a new character is
|
||||
/// selected.
|
||||
final ArcadeBackgroundCubit bloc;
|
||||
}
|
||||
|
||||
/// {@template arcade_background_sprite_component}
|
||||
/// [SpriteComponent] for the [ArcadeBackground].
|
||||
/// {@endtemplate}
|
||||
@visibleForTesting
|
||||
class ArcadeBackgroundSpriteComponent extends SpriteComponent
|
||||
with
|
||||
FlameBlocListenable<ArcadeBackgroundCubit, ArcadeBackgroundState>,
|
||||
HasGameRef {
|
||||
/// {@macro arcade_background_sprite_component}
|
||||
ArcadeBackgroundSpriteComponent({required String? assetPath})
|
||||
: _assetPath = assetPath,
|
||||
super(
|
||||
anchor: Anchor.bottomCenter,
|
||||
position: Vector2(0, 72.3),
|
||||
);
|
||||
|
||||
final String? _assetPath;
|
||||
|
||||
@override
|
||||
void onNewState(ArcadeBackgroundState state) {
|
||||
sprite = Sprite(
|
||||
gameRef.images.fromCache(state.characterTheme.background.keyName),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
final sprite = Sprite(
|
||||
gameRef.images
|
||||
.fromCache(_assetPath ?? theme.Assets.images.dash.background.keyName),
|
||||
);
|
||||
this.sprite = sprite;
|
||||
size = sprite.originalSize / 10;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:pinball_theme/pinball_theme.dart';
|
||||
|
||||
part 'arcade_background_state.dart';
|
||||
|
||||
class ArcadeBackgroundCubit extends Cubit<ArcadeBackgroundState> {
|
||||
ArcadeBackgroundCubit() : super(const ArcadeBackgroundState.initial());
|
||||
|
||||
void onCharacterSelected(CharacterTheme characterTheme) {
|
||||
emit(ArcadeBackgroundState(characterTheme: characterTheme));
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
part of 'arcade_background_cubit.dart';
|
||||
|
||||
class ArcadeBackgroundState extends Equatable {
|
||||
const ArcadeBackgroundState({required this.characterTheme});
|
||||
|
||||
const ArcadeBackgroundState.initial()
|
||||
: this(characterTheme: const DashTheme());
|
||||
|
||||
final CharacterTheme characterTheme;
|
||||
|
||||
@override
|
||||
List<Object> get props => [characterTheme];
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
// 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_components/pinball_components.dart';
|
||||
import 'package:pinball_theme/pinball_theme.dart' as theme;
|
||||
|
||||
import '../../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final assets = [
|
||||
theme.Assets.images.android.background.keyName,
|
||||
theme.Assets.images.dash.background.keyName,
|
||||
theme.Assets.images.dino.background.keyName,
|
||||
theme.Assets.images.sparky.background.keyName,
|
||||
];
|
||||
|
||||
final flameTester = FlameTester(() => TestGame(assets));
|
||||
|
||||
group('ArcadeBackground', () {
|
||||
test(
|
||||
'can be instantiated',
|
||||
() {
|
||||
expect(ArcadeBackground(), isA<ArcadeBackground>());
|
||||
expect(ArcadeBackground.test(), isA<ArcadeBackground>());
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'loads correctly',
|
||||
(game) async {
|
||||
final ball = ArcadeBackground();
|
||||
await game.ready();
|
||||
await game.ensureAdd(ball);
|
||||
|
||||
expect(game.contains(ball), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'has only one SpriteComponent',
|
||||
(game) async {
|
||||
final ball = ArcadeBackground();
|
||||
await game.ready();
|
||||
await game.ensureAdd(ball);
|
||||
|
||||
expect(
|
||||
ball.descendants().whereType<SpriteComponent>().length,
|
||||
equals(1),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'ArcadeBackgroundSpriteComponent changes sprite onNewState',
|
||||
(game) async {
|
||||
final ball = ArcadeBackground();
|
||||
await game.ready();
|
||||
await game.ensureAdd(ball);
|
||||
|
||||
final ballSprite = ball
|
||||
.descendants()
|
||||
.whereType<ArcadeBackgroundSpriteComponent>()
|
||||
.single;
|
||||
final originalSprite = ballSprite.sprite;
|
||||
|
||||
ballSprite.onNewState(
|
||||
const ArcadeBackgroundState(characterTheme: theme.DinoTheme()),
|
||||
);
|
||||
await game.ready();
|
||||
|
||||
final newSprite = ballSprite.sprite;
|
||||
expect(newSprite != originalSprite, isTrue);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_theme/pinball_theme.dart';
|
||||
|
||||
void main() {
|
||||
group(
|
||||
'ArcadeBackgroundCubit',
|
||||
() {
|
||||
blocTest<ArcadeBackgroundCubit, ArcadeBackgroundState>(
|
||||
'onCharacterSelected emits new theme',
|
||||
build: ArcadeBackgroundCubit.new,
|
||||
act: (bloc) => bloc.onCharacterSelected(const DinoTheme()),
|
||||
expect: () => [
|
||||
const ArcadeBackgroundState(
|
||||
characterTheme: DinoTheme(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_theme/pinball_theme.dart';
|
||||
|
||||
void main() {
|
||||
group('ArcadeBackgroundState', () {
|
||||
test('supports value equality', () {
|
||||
expect(
|
||||
ArcadeBackgroundState(characterTheme: DashTheme()),
|
||||
equals(ArcadeBackgroundState(characterTheme: DashTheme())),
|
||||
);
|
||||
});
|
||||
|
||||
group('constructor', () {
|
||||
test('can be instantiated', () {
|
||||
expect(
|
||||
ArcadeBackgroundState(characterTheme: DashTheme()),
|
||||
isNotNull,
|
||||
);
|
||||
});
|
||||
|
||||
test('initial contains DashTheme', () {
|
||||
expect(
|
||||
ArcadeBackgroundState.initial().characterTheme,
|
||||
DashTheme(),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
After Width: | Height: | Size: 638 KiB |
Before Width: | Height: | Size: 250 KiB |
After Width: | Height: | Size: 646 KiB |
Before Width: | Height: | Size: 342 KiB |
After Width: | Height: | Size: 654 KiB |
Before Width: | Height: | Size: 146 KiB |
After Width: | Height: | Size: 660 KiB |
Before Width: | Height: | Size: 372 KiB |