mirror of https://github.com/flutter/pinball.git
commit
408bbb5db2
Before Width: | Height: | Size: 11 KiB |
@ -0,0 +1,26 @@
|
|||||||
|
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||||
|
import 'package:pinball/game/components/components.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart' hide Assets;
|
||||||
|
|
||||||
|
/// {@template launcher}
|
||||||
|
/// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and
|
||||||
|
/// [LaunchRamp].
|
||||||
|
/// {@endtemplate}
|
||||||
|
class Launcher extends Forge2DBlueprint {
|
||||||
|
/// {@macro launcher}
|
||||||
|
Launcher();
|
||||||
|
|
||||||
|
/// [Plunger] to launch the [Ball] onto the board.
|
||||||
|
late final Plunger plunger;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void build(Forge2DGame gameRef) {
|
||||||
|
plunger = ControlledPlunger(compressionDistance: 12.3)
|
||||||
|
..initialPosition = Vector2(40.1, -38);
|
||||||
|
|
||||||
|
final _rocket = RocketSpriteComponent()..position = Vector2(43, 62);
|
||||||
|
|
||||||
|
addAll([_rocket, plunger]);
|
||||||
|
addBlueprint(LaunchRamp());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:pinball/game/game.dart';
|
||||||
|
|
||||||
|
part 'start_game_event.dart';
|
||||||
|
part 'start_game_state.dart';
|
||||||
|
|
||||||
|
/// {@template start_game_bloc}
|
||||||
|
/// Bloc that manages the app flow before the game starts.
|
||||||
|
/// {@endtemplate}
|
||||||
|
class StartGameBloc extends Bloc<StartGameEvent, StartGameState> {
|
||||||
|
/// {@macro start_game_bloc}
|
||||||
|
StartGameBloc({
|
||||||
|
required PinballGame game,
|
||||||
|
}) : _game = game,
|
||||||
|
super(const StartGameState.initial()) {
|
||||||
|
on<PlayTapped>(_onPlayTapped);
|
||||||
|
on<CharacterSelected>(_onCharacterSelected);
|
||||||
|
on<HowToPlayFinished>(_onHowToPlayFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
final PinballGame _game;
|
||||||
|
|
||||||
|
void _onPlayTapped(
|
||||||
|
PlayTapped event,
|
||||||
|
Emitter<StartGameState> emit,
|
||||||
|
) {
|
||||||
|
_game.gameFlowController.start();
|
||||||
|
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
status: StartGameStatus.selectCharacter,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onCharacterSelected(
|
||||||
|
CharacterSelected event,
|
||||||
|
Emitter<StartGameState> emit,
|
||||||
|
) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
status: StartGameStatus.howToPlay,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onHowToPlayFinished(
|
||||||
|
HowToPlayFinished event,
|
||||||
|
Emitter<StartGameState> emit,
|
||||||
|
) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
status: StartGameStatus.play,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
part of 'start_game_bloc.dart';
|
||||||
|
|
||||||
|
/// {@template start_game_event}
|
||||||
|
/// Event added during the start game flow.
|
||||||
|
/// {@endtemplate}
|
||||||
|
abstract class StartGameEvent extends Equatable {
|
||||||
|
/// {@macro start_game_event}
|
||||||
|
const StartGameEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// {@template play_tapped}
|
||||||
|
/// Play tapped event.
|
||||||
|
/// {@endtemplate}
|
||||||
|
class PlayTapped extends StartGameEvent {
|
||||||
|
/// {@macro play_tapped}
|
||||||
|
const PlayTapped();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// {@template character_selected}
|
||||||
|
/// Character selected event.
|
||||||
|
/// {@endtemplate}
|
||||||
|
class CharacterSelected extends StartGameEvent {
|
||||||
|
/// {@macro character_selected}
|
||||||
|
const CharacterSelected();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// {@template how_to_play_finished}
|
||||||
|
/// How to play finished event.
|
||||||
|
/// {@endtemplate}
|
||||||
|
class HowToPlayFinished extends StartGameEvent {
|
||||||
|
/// {@macro how_to_play_finished}
|
||||||
|
const HowToPlayFinished();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
part of 'start_game_bloc.dart';
|
||||||
|
|
||||||
|
/// Defines status of start game flow.
|
||||||
|
enum StartGameStatus {
|
||||||
|
/// Initial status.
|
||||||
|
initial,
|
||||||
|
|
||||||
|
/// Selection characters status.
|
||||||
|
selectCharacter,
|
||||||
|
|
||||||
|
/// How to play status.
|
||||||
|
howToPlay,
|
||||||
|
|
||||||
|
/// Play status.
|
||||||
|
play,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// {@template start_game_state}
|
||||||
|
/// Represents the state of flow before the game starts.
|
||||||
|
/// {@endtemplate}
|
||||||
|
class StartGameState extends Equatable {
|
||||||
|
/// {@macro start_game_state}
|
||||||
|
const StartGameState({
|
||||||
|
required this.status,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Initial [StartGameState].
|
||||||
|
const StartGameState.initial() : this(status: StartGameStatus.initial);
|
||||||
|
|
||||||
|
/// Status of [StartGameState].
|
||||||
|
final StartGameStatus status;
|
||||||
|
|
||||||
|
/// Creates a copy of [StartGameState].
|
||||||
|
StartGameState copyWith({
|
||||||
|
StartGameStatus? status,
|
||||||
|
}) {
|
||||||
|
return StartGameState(
|
||||||
|
status: status ?? this.status,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [status];
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export 'bloc/start_game_bloc.dart';
|
After Width: | Height: | Size: 26 KiB |
@ -0,0 +1,24 @@
|
|||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:pinball_components/gen/assets.gen.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart' hide Assets;
|
||||||
|
|
||||||
|
/// {@template rocket_sprite_component}
|
||||||
|
/// A [SpriteComponent] for the rocket over [Plunger].
|
||||||
|
/// {@endtemplate}
|
||||||
|
class RocketSpriteComponent extends SpriteComponent with HasGameRef {
|
||||||
|
// TODO(ruimiguel): change this priority to be over launcher ramp and bottom
|
||||||
|
// wall.
|
||||||
|
/// {@macro rocket_sprite_component}
|
||||||
|
RocketSpriteComponent() : super(priority: 5);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onLoad() async {
|
||||||
|
await super.onLoad();
|
||||||
|
final sprite = await gameRef.loadSprite(
|
||||||
|
Assets.images.plunger.rocket.keyName,
|
||||||
|
);
|
||||||
|
this.sprite = sprite;
|
||||||
|
size = sprite.originalSize / 10;
|
||||||
|
anchor = Anchor.center;
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 57 KiB |
@ -0,0 +1,28 @@
|
|||||||
|
// 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 '../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('RocketSpriteComponent', () {
|
||||||
|
final tester = FlameTester(TestGame.new);
|
||||||
|
|
||||||
|
tester.testGameWidget(
|
||||||
|
'renders correctly',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
game.camera.followVector2(Vector2.zero());
|
||||||
|
await game.ensureAdd(RocketSpriteComponent());
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
await expectLater(
|
||||||
|
find.byGame<TestGame>(),
|
||||||
|
matchesGoldenFile('golden/plunger/rocket.png'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:pinball/game/game.dart';
|
||||||
|
import 'package:pinball/start_game/bloc/start_game_bloc.dart';
|
||||||
|
|
||||||
|
import '../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late PinballGame pinballGame;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
pinballGame = MockPinballGame();
|
||||||
|
|
||||||
|
when(
|
||||||
|
() => pinballGame.gameFlowController,
|
||||||
|
).thenReturn(
|
||||||
|
MockGameFlowController(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('StartGameBloc', () {
|
||||||
|
blocTest<StartGameBloc, StartGameState>(
|
||||||
|
'on PlayTapped changes status to selectCharacter',
|
||||||
|
build: () => StartGameBloc(
|
||||||
|
game: pinballGame,
|
||||||
|
),
|
||||||
|
act: (bloc) => bloc.add(const PlayTapped()),
|
||||||
|
expect: () => [
|
||||||
|
const StartGameState(
|
||||||
|
status: StartGameStatus.selectCharacter,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<StartGameBloc, StartGameState>(
|
||||||
|
'on CharacterSelected changes status to howToPlay',
|
||||||
|
build: () => StartGameBloc(
|
||||||
|
game: pinballGame,
|
||||||
|
),
|
||||||
|
act: (bloc) => bloc.add(const CharacterSelected()),
|
||||||
|
expect: () => [
|
||||||
|
const StartGameState(
|
||||||
|
status: StartGameStatus.howToPlay,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<StartGameBloc, StartGameState>(
|
||||||
|
'on HowToPlayFinished changes status to play',
|
||||||
|
build: () => StartGameBloc(
|
||||||
|
game: pinballGame,
|
||||||
|
),
|
||||||
|
act: (bloc) => bloc.add(const HowToPlayFinished()),
|
||||||
|
expect: () => [
|
||||||
|
const StartGameState(
|
||||||
|
status: StartGameStatus.play,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
// ignore_for_file: prefer_const_constructors
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball/start_game/bloc/start_game_bloc.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('StartGameEvent', () {
|
||||||
|
test('PlayTapped supports value equality', () {
|
||||||
|
expect(
|
||||||
|
PlayTapped(),
|
||||||
|
equals(PlayTapped()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('CharacterSelected supports value equality', () {
|
||||||
|
expect(
|
||||||
|
CharacterSelected(),
|
||||||
|
equals(CharacterSelected()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('HowToPlayFinished supports value equality', () {
|
||||||
|
expect(
|
||||||
|
HowToPlayFinished(),
|
||||||
|
equals(HowToPlayFinished()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
// ignore_for_file: prefer_const_constructors
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball/start_game/bloc/start_game_bloc.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('StartGameState', () {
|
||||||
|
final testState = StartGameState(
|
||||||
|
status: StartGameStatus.selectCharacter,
|
||||||
|
);
|
||||||
|
|
||||||
|
test('initial state has correct values', () {
|
||||||
|
final state = StartGameState(
|
||||||
|
status: StartGameStatus.initial,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(state, StartGameState.initial());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('supports value equality', () {
|
||||||
|
final secondState = StartGameState(
|
||||||
|
status: StartGameStatus.selectCharacter,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(testState, secondState);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('supports copyWith', () {
|
||||||
|
final secondState = testState.copyWith();
|
||||||
|
|
||||||
|
expect(testState, secondState);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('has correct props', () {
|
||||||
|
expect(
|
||||||
|
testState.props,
|
||||||
|
equals([
|
||||||
|
StartGameStatus.selectCharacter,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in new issue