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_spawning_behavior.dart';
|
||||||
export 'ball_theming_behavior.dart';
|
|
||||||
export 'bonus_ball_spawning_behavior.dart';
|
export 'bonus_ball_spawning_behavior.dart';
|
||||||
export 'bonus_noise_behavior.dart';
|
export 'bonus_noise_behavior.dart';
|
||||||
export 'bumper_noise_behavior.dart';
|
export 'bumper_noise_behavior.dart';
|
||||||
export 'camera_focusing_behavior.dart';
|
export 'camera_focusing_behavior.dart';
|
||||||
|
export 'character_selection_behavior.dart';
|
||||||
export 'scoring_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 |