fix: fixed merge conflicts with main at launcher component

pull/200/head
RuiAlonso 3 years ago
commit 1db1e6a8dc

@ -0,0 +1,20 @@
name: pinball_flame
on:
push:
paths:
- "packages/pinball_flame/**"
- ".github/workflows/pinball_flame.yaml"
pull_request:
paths:
- "packages/pinball_flame/**"
- ".github/workflows/pinball_flame.yaml"
jobs:
build:
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1
with:
working_directory: packages/pinball_flame
coverage_excludes: "lib/gen/*.dart"
test_optimization: false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 MiB

After

Width:  |  Height:  |  Size: 3.2 MiB

@ -1 +0,0 @@
export 'component_controller.dart';

@ -11,14 +11,11 @@ class GameBloc extends Bloc<GameEvent, GameState> {
GameBloc() : super(const GameState.initial()) {
on<BallLost>(_onBallLost);
on<Scored>(_onScored);
on<BonusLetterActivated>(_onBonusLetterActivated);
on<BonusActivated>(_onBonusActivated);
on<DashNestActivated>(_onDashNestActivated);
on<SparkyTurboChargeActivated>(_onSparkyTurboChargeActivated);
}
static const bonusWord = 'GOOGLE';
static const bonusWordScore = 10000;
void _onBallLost(BallLost event, Emitter emit) {
emit(state.copyWith(balls: state.balls - 1));
}
@ -29,29 +26,12 @@ class GameBloc extends Bloc<GameEvent, GameState> {
}
}
void _onBonusLetterActivated(BonusLetterActivated event, Emitter emit) {
final newBonusLetters = [
...state.activatedBonusLetters,
event.letterIndex,
];
final achievedBonus = newBonusLetters.length == bonusWord.length;
if (achievedBonus) {
emit(
state.copyWith(
activatedBonusLetters: [],
bonusHistory: [
...state.bonusHistory,
GameBonus.word,
],
),
);
add(const Scored(points: bonusWordScore));
} else {
emit(
state.copyWith(activatedBonusLetters: newBonusLetters),
);
}
void _onBonusActivated(BonusActivated event, Emitter emit) {
emit(
state.copyWith(
bonusHistory: [...state.bonusHistory, event.bonus],
),
);
}
void _onDashNestActivated(DashNestActivated event, Emitter emit) {

@ -33,17 +33,13 @@ class Scored extends GameEvent {
List<Object?> get props => [points];
}
class BonusLetterActivated extends GameEvent {
const BonusLetterActivated(this.letterIndex)
: assert(
letterIndex < GameBloc.bonusWord.length,
'Index must be smaller than the length of the word',
);
class BonusActivated extends GameEvent {
const BonusActivated(this.bonus);
final int letterIndex;
final GameBonus bonus;
@override
List<Object?> get props => [letterIndex];
List<Object?> get props => [bonus];
}
class DashNestActivated extends GameEvent {

@ -4,9 +4,8 @@ part of 'game_bloc.dart';
/// Defines bonuses that a player can gain during a PinballGame.
enum GameBonus {
/// Bonus achieved when the user activate all of the bonus
/// letters on the board, forming the bonus word.
word,
/// Bonus achieved when the ball activates all Google letters.
googleWord,
/// Bonus achieved when the user activates all dash nest bumpers.
dashNest,
@ -23,7 +22,6 @@ class GameState extends Equatable {
const GameState({
required this.score,
required this.balls,
required this.activatedBonusLetters,
required this.bonusHistory,
required this.activatedDashNests,
}) : assert(score >= 0, "Score can't be negative"),
@ -32,7 +30,6 @@ class GameState extends Equatable {
const GameState.initial()
: score = 0,
balls = 3,
activatedBonusLetters = const [],
activatedDashNests = const {},
bonusHistory = const [];
@ -44,9 +41,6 @@ class GameState extends Equatable {
/// When the number of balls is 0, the game is over.
final int balls;
/// Active bonus letters.
final List<int> activatedBonusLetters;
/// Active dash nests.
final Set<String> activatedDashNests;
@ -57,14 +51,9 @@ class GameState extends Equatable {
/// Determines when the game is over.
bool get isGameOver => balls == 0;
/// Shortcut method to check if the given [i]
/// is activated.
bool isLetterActivated(int i) => activatedBonusLetters.contains(i);
GameState copyWith({
int? score,
int? balls,
List<int>? activatedBonusLetters,
Set<String>? activatedDashNests,
List<GameBonus>? bonusHistory,
}) {
@ -76,8 +65,6 @@ class GameState extends Equatable {
return GameState(
score: score ?? this.score,
balls: balls ?? this.balls,
activatedBonusLetters:
activatedBonusLetters ?? this.activatedBonusLetters,
activatedDashNests: activatedDashNests ?? this.activatedDashNests,
bonusHistory: bonusHistory ?? this.bonusHistory,
);
@ -87,7 +74,6 @@ class GameState extends Equatable {
List<Object?> get props => [
score,
balls,
activatedBonusLetters,
activatedDashNests,
bonusHistory,
];

@ -3,9 +3,9 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template alien_zone}
/// Area positioned below [Spaceship] where the [Ball]
@ -25,9 +25,9 @@ class AlienZone extends Component with HasGameRef<PinballGame> {
gameRef.addContactCallback(_ControlledAlienBumperBallContactCallback());
final lowerBumper = ControlledAlienBumper.a()
..initialPosition = Vector2(-32.52, 9.34);
..initialPosition = Vector2(-32.52, -9.34);
final upperBumper = ControlledAlienBumper.b()
..initialPosition = Vector2(-22.89, 17.43);
..initialPosition = Vector2(-22.89, -17.43);
await addAll([
lowerBumper,

@ -23,7 +23,7 @@ class Board extends Component {
final dino = ChromeDino()
..initialPosition = Vector2(
BoardDimensions.bounds.center.dx + 25,
BoardDimensions.bounds.center.dy + 10,
BoardDimensions.bounds.center.dy - 10,
);
await addAll([
@ -77,17 +77,17 @@ class _BottomGroupSide extends Component {
final flipper = ControlledFlipper(
side: _side,
)..initialPosition = Vector2((11.8 * direction) + centerXAdjustment, -43.6);
)..initialPosition = Vector2((11.8 * direction) + centerXAdjustment, 43.6);
final baseboard = Baseboard(side: _side)
..initialPosition = Vector2(
(25.58 * direction) + centerXAdjustment,
-28.69,
28.69,
);
final kicker = Kicker(
side: _side,
)..initialPosition = Vector2(
(22.4 * direction) + centerXAdjustment,
-25,
25,
);
await addAll([flipper, baseboard, kicker]);

@ -1,208 +0,0 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:async';
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template bonus_word}
/// Loads all [BonusLetter]s to compose a [BonusWord].
/// {@endtemplate}
class BonusWord extends Component
with BlocComponent<GameBloc, GameState>, HasGameRef<PinballGame> {
/// {@macro bonus_word}
BonusWord({required Vector2 position}) : _position = position;
final Vector2 _position;
@override
bool listenWhen(GameState? previousState, GameState newState) {
return (previousState?.bonusHistory.length ?? 0) <
newState.bonusHistory.length &&
newState.bonusHistory.last == GameBonus.word;
}
@override
void onNewState(GameState state) {
if (state.bonusHistory.last == GameBonus.word) {
gameRef.audio.googleBonus();
final letters = children.whereType<BonusLetter>().toList();
for (var i = 0; i < letters.length; i++) {
final letter = letters[i];
letter
..isEnabled = false
..add(
SequenceEffect(
[
ColorEffect(
i.isOdd
? BonusLetter._activeColor
: BonusLetter._disableColor,
const Offset(0, 1),
EffectController(duration: 0.25),
),
ColorEffect(
i.isOdd
? BonusLetter._disableColor
: BonusLetter._activeColor,
const Offset(0, 1),
EffectController(duration: 0.25),
),
],
repeatCount: 4,
)..onFinishCallback = () {
letter
..isEnabled = true
..add(
ColorEffect(
BonusLetter._disableColor,
const Offset(0, 1),
EffectController(duration: 0.25),
),
);
},
);
}
}
}
@override
Future<void> onLoad() async {
await super.onLoad();
final offsets = [
Vector2(-12.92, -1.82),
Vector2(-8.33, 0.65),
Vector2(-2.88, 1.75),
];
offsets.addAll(
offsets.reversed
.map(
(offset) => Vector2(-offset.x, offset.y),
)
.toList(),
);
assert(offsets.length == GameBloc.bonusWord.length, 'Invalid positions');
final letters = <BonusLetter>[];
for (var i = 0; i < GameBloc.bonusWord.length; i++) {
letters.add(
BonusLetter(
letter: GameBloc.bonusWord[i],
index: i,
)..initialPosition = _position + offsets[i],
);
}
await addAll(letters);
}
}
/// {@template bonus_letter}
/// [BodyType.static] sensor component, part of a word bonus,
/// which will activate its letter after contact with a [Ball].
/// {@endtemplate}
class BonusLetter extends BodyComponent<PinballGame>
with BlocComponent<GameBloc, GameState>, InitialPosition {
/// {@macro bonus_letter}
BonusLetter({
required String letter,
required int index,
}) : _letter = letter,
_index = index {
paint = Paint()..color = _disableColor;
}
/// The size of the [BonusLetter].
static final size = Vector2.all(3.7);
static const _activeColor = Colors.green;
static const _disableColor = Colors.red;
final String _letter;
final int _index;
/// Indicates if a [BonusLetter] can be activated on [Ball] contact.
///
/// It is disabled whilst animating and enabled again once the animation
/// completes. The animation is triggered when [GameBonus.word] is
/// awarded.
bool isEnabled = true;
@override
Future<void> onLoad() async {
await super.onLoad();
await add(
TextComponent(
position: Vector2(-1, -1),
text: _letter,
textRenderer: TextPaint(
style: const TextStyle(fontSize: 2, color: Colors.white),
),
),
);
}
@override
Body createBody() {
final shape = CircleShape()..radius = size.x / 2;
final fixtureDef = FixtureDef(shape)..isSensor = true;
final bodyDef = BodyDef()
..position = initialPosition
..userData = this
..type = BodyType.static;
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
@override
bool listenWhen(GameState? previousState, GameState newState) {
final wasActive = previousState?.isLetterActivated(_index) ?? false;
final isActive = newState.isLetterActivated(_index);
return wasActive != isActive;
}
@override
void onNewState(GameState state) {
final isActive = state.isLetterActivated(_index);
add(
ColorEffect(
isActive ? _activeColor : _disableColor,
const Offset(0, 1),
EffectController(duration: 0.25),
),
);
}
/// Activates this [BonusLetter], if it's not already activated.
void activate() {
final isActive = state?.isLetterActivated(_index) ?? false;
if (!isActive) {
gameRef.read<GameBloc>().add(BonusLetterActivated(_index));
}
}
}
/// Triggers [BonusLetter.activate] method when a [BonusLetter] and a [Ball]
/// come in contact.
class BonusLetterBallContactCallback
extends ContactCallback<Ball, BonusLetter> {
@override
void begin(Ball ball, BonusLetter bonusLetter, Contact contact) {
if (bonusLetter.isEnabled) {
bonusLetter.activate();
}
}
}

@ -1,7 +1,7 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// Adds helpers methods to Flame's [Camera]
extension CameraX on Camera {

@ -1,6 +1,5 @@
export 'alien_zone.dart';
export 'board.dart';
export 'bonus_word.dart';
export 'camera_controller.dart';
export 'controlled_ball.dart';
export 'controlled_flipper.dart';
@ -8,6 +7,7 @@ export 'controlled_plunger.dart';
export 'controlled_sparky_computer.dart';
export 'flutter_forest.dart';
export 'game_flow_controller.dart';
export 'google_word.dart';
export 'launcher.dart';
export 'score_effect_controller.dart';
export 'score_points.dart';

@ -1,9 +1,9 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/forge2d_game.dart';
import 'package:flutter/material.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:pinball_theme/pinball_theme.dart';
/// {@template controlled_ball}
@ -68,7 +68,7 @@ class BallController extends ComponentController<Ball>
await Future<void>.delayed(const Duration(seconds: 1));
component
..resume()
..boost(Vector2(200, -500));
..boost(Vector2(200, 500));
}
@override

@ -1,7 +1,9 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flutter/services.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template controlled_flipper}
/// A [Flipper] with a [FlipperController] attached.
@ -19,7 +21,7 @@ class ControlledFlipper extends Flipper with Controls<FlipperController> {
/// A [ComponentController] that controls a [Flipper]s movement.
/// {@endtemplate}
class FlipperController extends ComponentController<Flipper>
with KeyboardHandler {
with KeyboardHandler, BlocComponent<GameBloc, GameState> {
/// {@macro flipper_controller}
FlipperController(Flipper flipper)
: _keys = flipper.side.flipperKeys,
@ -35,6 +37,7 @@ class FlipperController extends ComponentController<Flipper>
RawKeyEvent event,
Set<LogicalKeyboardKey> keysPressed,
) {
if (state?.isGameOver ?? false) return true;
if (!_keys.contains(event.logicalKey)) return true;
if (event is RawKeyDownEvent) {

@ -1,7 +1,9 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flutter/services.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template controlled_plunger}
/// A [Plunger] with a [PlungerController] attached.
@ -18,7 +20,7 @@ class ControlledPlunger extends Plunger with Controls<PlungerController> {
/// A [ComponentController] that controls a [Plunger]s movement.
/// {@endtemplate}
class PlungerController extends ComponentController<Plunger>
with KeyboardHandler {
with KeyboardHandler, BlocComponent<GameBloc, GameState> {
/// {@macro plunger_controller}
PlungerController(Plunger plunger) : super(plunger);
@ -36,6 +38,7 @@ class PlungerController extends ComponentController<Plunger>
RawKeyEvent event,
Set<LogicalKeyboardKey> keysPressed,
) {
if (state?.isGameOver ?? false) return true;
if (!_keys.contains(event.logicalKey)) return true;
if (event is RawKeyDownEvent) {

@ -3,9 +3,9 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template controlled_sparky_computer}
/// [SparkyComputer] with a [SparkyComputerController] attached.
@ -21,7 +21,7 @@ class ControlledSparkyComputer extends SparkyComputer
void build(Forge2DGame _) {
addContactCallback(SparkyTurboChargeSensorBallContactCallback());
final sparkyTurboChargeSensor = SparkyTurboChargeSensor()
..initialPosition = Vector2(-13, 49.8);
..initialPosition = Vector2(-13, -49.8);
add(sparkyTurboChargeSensor);
super.build(_);
}

@ -4,9 +4,9 @@ import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template flutter_forest}
/// Area positioned at the top right of the [Board] where the [Ball]
@ -26,17 +26,17 @@ class FlutterForest extends Component with Controls<_FlutterForestController> {
@override
Future<void> onLoad() async {
await super.onLoad();
final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, 58.3);
final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, -58.3);
final bigNest = _ControlledBigDashNestBumper(
id: 'big_nest_bumper',
)..initialPosition = Vector2(18.55, 59.35);
)..initialPosition = Vector2(18.55, -59.35);
final smallLeftNest = _ControlledSmallDashNestBumper.a(
id: 'small_nest_bumper_a',
)..initialPosition = Vector2(8.95, 51.95);
)..initialPosition = Vector2(8.95, -51.95);
final smallRightNest = _ControlledSmallDashNestBumper.b(
id: 'small_nest_bumper_b',
)..initialPosition = Vector2(23.3, 46.75);
)..initialPosition = Vector2(23.3, -46.75);
final dashAnimatronic = DashAnimatronic()..position = Vector2(20, -66);
await addAll([
@ -78,7 +78,7 @@ class _FlutterForestController extends ComponentController<FlutterForest>
await Future<void>.delayed(const Duration(milliseconds: 700));
await gameRef.add(
ControlledBall.bonus(theme: gameRef.theme)
..initialPosition = Vector2(17.2, 52.7),
..initialPosition = Vector2(17.2, -52.7),
);
}
}

@ -1,8 +1,8 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template game_flow_controller}
/// A [Component] that controls the game over and game restart logic
@ -28,7 +28,11 @@ class GameFlowController extends ComponentController<PinballGame>
/// Puts the game on a game over state
void gameOver() {
component.firstChild<Backboard>()?.gameOverMode();
// TODO(erickzanardo): implement score submission and "navigate" to the
// next page
component.firstChild<Backboard>()?.gameOverMode(
score: state?.score ?? 0,
);
component.firstChild<CameraController>()?.focusOnBackboard();
}

@ -0,0 +1,83 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:async';
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template google_word}
/// Loads all [GoogleLetter]s to compose a [GoogleWord].
/// {@endtemplate}
class GoogleWord extends Component
with HasGameRef<PinballGame>, Controls<_GoogleWordController> {
/// {@macro google_word}
GoogleWord({
required Vector2 position,
}) : _position = position {
controller = _GoogleWordController(this);
}
final Vector2 _position;
@override
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(_GoogleLetterBallContactCallback());
final offsets = [
Vector2(-12.92, 1.82),
Vector2(-8.33, -0.65),
Vector2(-2.88, -1.75),
Vector2(2.88, -1.75),
Vector2(8.33, -0.65),
Vector2(12.92, 1.82),
];
final letters = <GoogleLetter>[];
for (var index = 0; index < offsets.length; index++) {
letters.add(
GoogleLetter(index)..initialPosition = _position + offsets[index],
);
}
await addAll(letters);
}
}
class _GoogleWordController extends ComponentController<GoogleWord>
with HasGameRef<PinballGame> {
_GoogleWordController(GoogleWord googleWord) : super(googleWord);
final _activatedLetters = <GoogleLetter>{};
void activate(GoogleLetter googleLetter) {
if (!_activatedLetters.add(googleLetter)) return;
googleLetter.activate();
final activatedBonus = _activatedLetters.length == 6;
if (activatedBonus) {
gameRef.audio.googleBonus();
gameRef.read<GameBloc>().add(const BonusActivated(GameBonus.googleWord));
component.children.whereType<GoogleLetter>().forEach(
(letter) => letter.deactivate(),
);
_activatedLetters.clear();
}
}
}
/// Activates a [GoogleLetter] when it contacts with a [Ball].
class _GoogleLetterBallContactCallback
extends ContactCallback<GoogleLetter, Ball> {
@override
void begin(GoogleLetter googleLetter, _, __) {
final parent = googleLetter.parent;
if (parent is GoogleWord) {
parent.controller.activate(googleLetter);
}
}
}

@ -1,6 +1,7 @@
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/game/components/components.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template launcher}
/// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and
@ -15,8 +16,13 @@ class Launcher extends Forge2DBlueprint {
@override
void build(Forge2DGame gameRef) {
<<<<<<< HEAD
plunger = ControlledPlunger(compressionDistance: 20) //12.3
..initialPosition = Vector2(38.5, -20);
=======
plunger = ControlledPlunger(compressionDistance: 12.3)
..initialPosition = Vector2(40.1, 38);
>>>>>>> main
final _rocket = RocketSpriteComponent()..position = Vector2(43, 62);

@ -2,9 +2,9 @@ import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template score_effect_controller}
/// A [ComponentController] responsible for adding [ScoreText]s
@ -37,7 +37,7 @@ class ScoreEffectController extends ComponentController<PinballGame>
text: newScore.toString(),
position: Vector2(
_noise(),
_noise() + (-BoardDimensions.bounds.topCenter.dy + 10),
_noise() + (BoardDimensions.bounds.topCenter.dy + 10),
),
),
);

@ -3,9 +3,9 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_fire_zone}
/// Area positioned at the top left of the [Board] where the [Ball]
@ -25,11 +25,11 @@ class SparkyFireZone extends Component with HasGameRef<PinballGame> {
gameRef.addContactCallback(_ControlledSparkyBumperBallContactCallback());
final lowerLeftBumper = ControlledSparkyBumper.a()
..initialPosition = Vector2(-23.15, 41.65);
..initialPosition = Vector2(-23.15, -41.65);
final upperLeftBumper = ControlledSparkyBumper.b()
..initialPosition = Vector2(-21.25, 58.15);
..initialPosition = Vector2(-21.25, -58.15);
final rightBumper = ControlledSparkyBumper.c()
..initialPosition = Vector2(-3.56, 53.051);
..initialPosition = Vector2(-3.56, -53.051);
await addAll([
lowerLeftBumper,

@ -15,10 +15,8 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.baseboard.right.keyName),
images.load(components.Assets.images.kicker.left.keyName),
images.load(components.Assets.images.kicker.right.keyName),
images.load(components.Assets.images.slingshot.leftUpper.keyName),
images.load(components.Assets.images.slingshot.leftLower.keyName),
images.load(components.Assets.images.slingshot.rightUpper.keyName),
images.load(components.Assets.images.slingshot.rightLower.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,
@ -60,6 +58,13 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.sparky.bumper.c.inactive.keyName),
images.load(components.Assets.images.backboard.backboardScores.keyName),
images.load(components.Assets.images.backboard.backboardGameOver.keyName),
images.load(components.Assets.images.googleWord.letter1.keyName),
images.load(components.Assets.images.googleWord.letter2.keyName),
images.load(components.Assets.images.googleWord.letter3.keyName),
images.load(components.Assets.images.googleWord.letter4.keyName),
images.load(components.Assets.images.googleWord.letter5.keyName),
images.load(components.Assets.images.googleWord.letter6.keyName),
images.load(components.Assets.images.backboard.display.keyName),
images.load(Assets.images.components.background.path),
];
}

@ -5,11 +5,11 @@ import 'package:flame/components.dart';
import 'package:flame/input.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/gen/assets.gen.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
import 'package:pinball_theme/pinball_theme.dart' hide Assets;
class PinballGame extends Forge2DGame
@ -41,7 +41,7 @@ class PinballGame extends Forge2DGame
unawaited(add(ScoreEffectController(this)));
unawaited(add(gameFlowController = GameFlowController(this)));
unawaited(add(CameraController(this)));
unawaited(add(Backboard(position: Vector2(0, -88))));
unawaited(add(Backboard.waiting(position: Vector2(0, -88))));
await _addGameBoundaries();
unawaited(addFromBlueprint(Boundaries()));
@ -59,15 +59,11 @@ class PinballGame extends Forge2DGame
unawaited(
addFromBlueprint(
Spaceship(
position: Vector2(-26.5, 28.5),
position: Vector2(-26.5, -28.5),
),
),
);
unawaited(
addFromBlueprint(
SpaceshipRail(),
),
);
unawaited(addFromBlueprint(SpaceshipRail()));
controller.attachTo(launcher.plunger);
await super.onLoad();
@ -76,7 +72,6 @@ class PinballGame extends Forge2DGame
void _addContactCallbacks() {
addContactCallback(BallScorePointsCallback(this));
addContactCallback(BottomWallBallContactCallback());
addContactCallback(BonusLetterBallContactCallback());
}
Future<void> _addGameBoundaries() async {
@ -86,10 +81,10 @@ class PinballGame extends Forge2DGame
Future<void> _addBonusWord() async {
await add(
BonusWord(
GoogleWord(
position: Vector2(
BoardDimensions.bounds.center.dx - 3.07,
BoardDimensions.bounds.center.dy - 2.4,
BoardDimensions.bounds.center.dx - 4.1,
BoardDimensions.bounds.center.dy + 1.8,
),
),
);
@ -127,7 +122,7 @@ class _GameBallsController extends ComponentController<PinballGame>
theme: gameRef.theme,
)..initialPosition = Vector2(
_plunger.body.position.x,
_plunger.body.position.y + Ball.size.y,
_plunger.body.position.y - Ball.size.y,
);
component.add(ball);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

@ -57,6 +57,10 @@ class $AssetsImagesBackboardGen {
/// File path: assets/images/backboard/backboard_scores.png
AssetGenImage get backboardScores =>
const AssetGenImage('assets/images/backboard/backboard_scores.png');
/// File path: assets/images/backboard/display.png
AssetGenImage get display =>
const AssetGenImage('assets/images/backboard/display.png');
}
class $AssetsImagesBaseboardGen {
@ -196,21 +200,13 @@ class $AssetsImagesPlungerGen {
class $AssetsImagesSlingshotGen {
const $AssetsImagesSlingshotGen();
/// File path: assets/images/slingshot/left_lower.png
AssetGenImage get leftLower =>
const AssetGenImage('assets/images/slingshot/left_lower.png');
/// File path: assets/images/slingshot/left_upper.png
AssetGenImage get leftUpper =>
const AssetGenImage('assets/images/slingshot/left_upper.png');
/// File path: assets/images/slingshot/right_lower.png
AssetGenImage get rightLower =>
const AssetGenImage('assets/images/slingshot/right_lower.png');
/// File path: assets/images/slingshot/lower.png
AssetGenImage get lower =>
const AssetGenImage('assets/images/slingshot/lower.png');
/// File path: assets/images/slingshot/right_upper.png
AssetGenImage get rightUpper =>
const AssetGenImage('assets/images/slingshot/right_upper.png');
/// File path: assets/images/slingshot/upper.png
AssetGenImage get upper =>
const AssetGenImage('assets/images/slingshot/upper.png');
}
class $AssetsImagesSpaceshipGen {

@ -1,5 +1,3 @@
import 'dart:math' as math;
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
@ -73,14 +71,15 @@ class AlienBumper extends BodyComponent with InitialPosition {
center: Vector2.zero(),
majorRadius: _majorRadius,
minorRadius: _minorRadius,
)..rotate(15.9 * math.pi / 180);
final fixtureDef = FixtureDef(shape)
..friction = 0
..restitution = 4;
final bodyDef = BodyDef()
..position = initialPosition
..userData = this;
)..rotate(1.29);
final fixtureDef = FixtureDef(
shape,
restitution: 4,
);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}

@ -1,40 +0,0 @@
import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template backboard}
/// The [Backboard] of the pinball machine.
/// {@endtemplate}
class Backboard extends SpriteComponent with HasGameRef {
/// {@macro backboard}
Backboard({
required Vector2 position,
}) : super(
// TODO(erickzanardo): remove multiply after
// https://github.com/flame-engine/flame/pull/1506 is merged
position: position..clone().multiply(Vector2(1, -1)),
anchor: Anchor.bottomCenter,
);
@override
Future<void> onLoad() async {
await waitingMode();
}
/// Puts the Backboard in waiting mode, where the scoreboard is shown.
Future<void> waitingMode() async {
final sprite = await gameRef.loadSprite(
Assets.images.backboard.backboardScores.keyName,
);
size = sprite.originalSize / 10;
this.sprite = sprite;
}
/// Puts the Backboard in game over mode, where the score input is shown.
Future<void> gameOverMode() async {
final sprite = await gameRef.loadSprite(
Assets.images.backboard.backboardGameOver.keyName,
);
size = sprite.originalSize / 10;
this.sprite = sprite;
}
}

@ -0,0 +1,75 @@
import 'dart:async';
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
export 'backboard_game_over.dart';
export 'backboard_letter_prompt.dart';
export 'backboard_waiting.dart';
/// {@template backboard}
/// The [Backboard] of the pinball machine.
/// {@endtemplate}
class Backboard extends PositionComponent with HasGameRef {
/// {@macro backboard}
Backboard({
required Vector2 position,
}) : super(
position: position,
anchor: Anchor.bottomCenter,
);
/// {@macro backboard}
///
/// Returns a [Backboard] initialized in the waiting mode
factory Backboard.waiting({
required Vector2 position,
}) {
return Backboard(position: position)..waitingMode();
}
/// {@macro backboard}
///
/// Returns a [Backboard] initialized in the game over mode
factory Backboard.gameOver({
required Vector2 position,
required int score,
required BackboardOnSubmit onSubmit,
}) {
return Backboard(position: position)
..gameOverMode(
score: score,
onSubmit: onSubmit,
);
}
/// [TextPaint] used on the [Backboard]
static final textPaint = TextPaint(
style: TextStyle(
fontSize: 6,
color: Colors.white,
fontFamily: PinballFonts.pixeloidSans,
),
);
/// Puts the Backboard in waiting mode, where the scoreboard is shown.
Future<void> waitingMode() async {
children.removeWhere((_) => true);
await add(BackboardWaiting());
}
/// Puts the Backboard in game over mode, where the score input is shown.
Future<void> gameOverMode({
required int score,
BackboardOnSubmit? onSubmit,
}) async {
children.removeWhere((_) => true);
await add(
BackboardGameOver(
score: score,
onSubmit: onSubmit,
),
);
}
}

@ -0,0 +1,120 @@
import 'dart:async';
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flutter/services.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// Signature for the callback called when the used has
/// submettied their initials on the [BackboardGameOver]
typedef BackboardOnSubmit = void Function(String);
/// {@template backboard_game_over}
/// [PositionComponent] that handles the user input on the
/// game over display view.
/// {@endtemplate}
class BackboardGameOver extends PositionComponent with HasGameRef {
/// {@macro backboard_game_over}
BackboardGameOver({
required int score,
BackboardOnSubmit? onSubmit,
}) : _score = score,
_onSubmit = onSubmit;
final int _score;
final BackboardOnSubmit? _onSubmit;
@override
Future<void> onLoad() async {
final backgroundSprite = await gameRef.loadSprite(
Assets.images.backboard.backboardGameOver.keyName,
);
unawaited(
add(
SpriteComponent(
sprite: backgroundSprite,
size: backgroundSprite.originalSize / 10,
anchor: Anchor.bottomCenter,
),
),
);
final displaySprite = await gameRef.loadSprite(
Assets.images.backboard.display.keyName,
);
unawaited(
add(
SpriteComponent(
sprite: displaySprite,
size: displaySprite.originalSize / 10,
anchor: Anchor.bottomCenter,
position: Vector2(0, -11.5),
),
),
);
unawaited(
add(
TextComponent(
text: _score.formatScore(),
position: Vector2(-22, -46.5),
anchor: Anchor.center,
textRenderer: Backboard.textPaint,
),
),
);
for (var i = 0; i < 3; i++) {
unawaited(
add(
BackboardLetterPrompt(
position: Vector2(
20 + (6 * i).toDouble(),
-46.5,
),
hasFocus: i == 0,
),
),
);
}
unawaited(
add(
KeyboardInputController(
keyUp: {
LogicalKeyboardKey.arrowLeft: () => _movePrompt(true),
LogicalKeyboardKey.arrowRight: () => _movePrompt(false),
LogicalKeyboardKey.enter: _submit,
},
),
),
);
}
/// Returns the current inputed initials
String get initials => children
.whereType<BackboardLetterPrompt>()
.map((prompt) => prompt.char)
.join();
bool _submit() {
_onSubmit?.call(initials);
return true;
}
bool _movePrompt(bool left) {
final prompts = children.whereType<BackboardLetterPrompt>().toList();
final current = prompts.firstWhere((prompt) => prompt.hasFocus)
..hasFocus = false;
var index = prompts.indexOf(current) + (left ? -1 : 1);
index = min(max(0, index), prompts.length - 1);
prompts[index].hasFocus = true;
return false;
}
}

@ -0,0 +1,107 @@
import 'dart:async';
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template backboard_letter_prompt}
/// A [PositionComponent] that renders a letter prompt used
/// on the [BackboardGameOver]
/// {@endtemplate}
class BackboardLetterPrompt extends PositionComponent {
/// {@macro backboard_letter_prompt}
BackboardLetterPrompt({
required Vector2 position,
bool hasFocus = false,
}) : _hasFocus = hasFocus,
super(
position: position,
);
static const _alphabetCode = 65;
static const _alphabetLength = 25;
var _charIndex = 0;
bool _hasFocus;
late RectangleComponent _underscore;
late TextComponent _input;
late TimerComponent _underscoreBlinker;
@override
Future<void> onLoad() async {
_underscore = RectangleComponent(
size: Vector2(
4,
1.2,
),
anchor: Anchor.center,
position: Vector2(0, 4),
);
unawaited(add(_underscore));
_input = TextComponent(
text: 'A',
textRenderer: Backboard.textPaint,
anchor: Anchor.center,
);
unawaited(add(_input));
_underscoreBlinker = TimerComponent(
period: 0.6,
repeat: true,
autoStart: _hasFocus,
onTick: () {
_underscore.paint.color = (_underscore.paint.color == Colors.white)
? Colors.transparent
: Colors.white;
},
);
unawaited(add(_underscoreBlinker));
unawaited(
add(
KeyboardInputController(
keyUp: {
LogicalKeyboardKey.arrowUp: () => _cycle(true),
LogicalKeyboardKey.arrowDown: () => _cycle(false),
},
),
),
);
}
/// Returns the current selected character
String get char => String.fromCharCode(_alphabetCode + _charIndex);
bool _cycle(bool up) {
if (_hasFocus) {
final newCharCode =
min(max(_charIndex + (up ? 1 : -1), 0), _alphabetLength);
_input.text = String.fromCharCode(_alphabetCode + newCharCode);
_charIndex = newCharCode;
return false;
}
return true;
}
/// Returns if this prompt has focus on it
bool get hasFocus => _hasFocus;
/// Updates this prompt focus
set hasFocus(bool hasFocus) {
if (hasFocus) {
_underscoreBlinker.timer.resume();
} else {
_underscoreBlinker.timer.pause();
}
_underscore.paint.color = Colors.white;
_hasFocus = hasFocus;
}
}

@ -0,0 +1,17 @@
import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
/// [PositionComponent] that shows the leaderboard while the player
/// has not started the game yet.
class BackboardWaiting extends SpriteComponent with HasGameRef {
@override
Future<void> onLoad() async {
final sprite = await gameRef.loadSprite(
Assets.images.backboard.backboardScores.keyName,
);
this.sprite = sprite;
size = sprite.originalSize / 10;
anchor = Anchor.bottomCenter;
}
}

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:math' as math;
import 'dart:ui';
import 'package:flame/components.dart';
@ -63,11 +64,15 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
@override
Body createBody() {
final shape = CircleShape()..radius = size.x / 2;
final fixtureDef = FixtureDef(shape)..density = 1;
final bodyDef = BodyDef()
..position = initialPosition
..userData = this
..type = BodyType.dynamic;
final fixtureDef = FixtureDef(
shape,
density: 1,
);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
type: BodyType.dynamic,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
@ -79,7 +84,7 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
// TODO(allisonryan0002): prevent motion from contact with other balls.
void stop() {
body
..gravityScale = 0
..gravityScale = Vector2.zero()
..linearVelocity = Vector2.zero()
..angularVelocity = 0;
}
@ -88,7 +93,7 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
///
/// If previously [stop]ped, the previous ball's velocity is not kept.
void resume() {
body.gravityScale = 1;
body.gravityScale = Vector2(0, 1);
}
@override
@ -99,15 +104,16 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
final direction = body.linearVelocity.normalized();
final effect = FireEffect(
burstPower: _boostTimer,
direction: -direction,
position: Vector2(body.position.x, -body.position.y),
direction: direction,
position: Vector2(body.position.x, body.position.y),
priority: priority - 1,
);
unawaited(gameRef.add(effect));
}
_rescale();
_rescaleSize();
_setPositionalGravity();
}
/// Applies a boost on this [Ball].
@ -116,19 +122,36 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
_boostTimer = _boostDuration;
}
void _rescale() {
final boardHeight = BoardDimensions.size.y;
const maxShrinkAmount = BoardDimensions.perspectiveShrinkFactor;
void _rescaleSize() {
final boardHeight = BoardDimensions.bounds.height;
const maxShrinkValue = BoardDimensions.perspectiveShrinkFactor;
final adjustedYPosition = body.position.y + (boardHeight / 2);
final standardizedYPosition = body.position.y + (boardHeight / 2);
final scaleFactor = ((boardHeight - adjustedYPosition) /
BoardDimensions.shrinkAdjustedHeight) +
maxShrinkAmount;
final scaleFactor = maxShrinkValue +
((standardizedYPosition / boardHeight) * (1 - maxShrinkValue));
body.fixtures.first.shape.radius = (size.x / 2) * scaleFactor;
_spriteComponent.scale = Vector2.all(scaleFactor);
}
void _setPositionalGravity() {
final defaultGravity = gameRef.world.gravity.y;
final maxXDeviationFromCenter = BoardDimensions.bounds.width / 2;
const maxXGravityPercentage =
(1 - BoardDimensions.perspectiveShrinkFactor) / 2;
final xDeviationFromCenter = body.position.x;
final positionalXForce = ((xDeviationFromCenter / maxXDeviationFromCenter) *
maxXGravityPercentage) *
defaultGravity;
final positionalYForce = math.sqrt(
math.pow(defaultGravity, 2) - math.pow(positionalXForce, 2),
);
body.gravityOverride = Vector2(positionalXForce, positionalYForce);
}
}
class _BallSpriteComponent extends SpriteComponent with HasGameRef {

@ -19,37 +19,37 @@ class Baseboard extends BodyComponent with InitialPosition {
List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
final direction = _side.direction;
final arcsAngle = -1.11 * direction;
const arcsRotation = math.pi / 2.08;
const arcsAngle = 1.11;
final arcsRotation = (_side.isLeft) ? -2.7 : -1.6;
final pegBumperShape = CircleShape()..radius = 0.7;
pegBumperShape.position.setValues(11.11 * direction, 7.15);
pegBumperShape.position.setValues(11.11 * direction, -7.15);
final pegBumperFixtureDef = FixtureDef(pegBumperShape);
fixturesDef.add(pegBumperFixtureDef);
final topCircleShape = CircleShape()..radius = 0.7;
topCircleShape.position.setValues(9.71 * direction, 4.95);
topCircleShape.position.setValues(9.71 * direction, -4.95);
final topCircleFixtureDef = FixtureDef(topCircleShape);
fixturesDef.add(topCircleFixtureDef);
final innerEdgeShape = EdgeShape()
..set(
Vector2(9.01 * direction, 5.35),
Vector2(5.29 * direction, -0.95),
Vector2(9.01 * direction, -5.35),
Vector2(5.29 * direction, 0.95),
);
final innerEdgeShapeFixtureDef = FixtureDef(innerEdgeShape);
fixturesDef.add(innerEdgeShapeFixtureDef);
final outerEdgeShape = EdgeShape()
..set(
Vector2(10.41 * direction, 4.75),
Vector2(3.79 * direction, -5.95),
Vector2(10.41 * direction, -4.75),
Vector2(3.79 * direction, 5.95),
);
final outerEdgeShapeFixtureDef = FixtureDef(outerEdgeShape);
fixturesDef.add(outerEdgeShapeFixtureDef);
final upperArcShape = ArcShape(
center: Vector2(0.09 * direction, 2.15),
center: Vector2(0.09 * direction, -2.15),
arcRadius: 6.1,
angle: arcsAngle,
rotation: arcsRotation,
@ -58,7 +58,7 @@ class Baseboard extends BodyComponent with InitialPosition {
fixturesDef.add(upperArcFixtureDef);
final lowerArcShape = ArcShape(
center: Vector2(0.09 * direction, -3.35),
center: Vector2(0.09 * direction, 3.35),
arcRadius: 4.5,
angle: arcsAngle,
rotation: arcsRotation,
@ -70,7 +70,7 @@ class Baseboard extends BodyComponent with InitialPosition {
..setAsBox(
6.8,
2,
Vector2(-6.3 * direction, -5.85),
Vector2(-6.3 * direction, 5.85),
0,
);
final bottomRectangleFixtureDef = FixtureDef(bottomRectangle);
@ -89,10 +89,10 @@ class Baseboard extends BodyComponent with InitialPosition {
@override
Body createBody() {
const angle = 37.1 * (math.pi / 180);
final bodyDef = BodyDef()
..position = initialPosition
..angle = _side.isLeft ? -angle : angle;
final bodyDef = BodyDef(
position: initialPosition,
angle: -angle * _side.direction,
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);

@ -14,7 +14,7 @@ class BoardDimensions {
static final bounds = Rect.fromCenter(
center: Offset.zero,
width: size.x,
height: -size.y,
height: size.y,
);
/// 3D perspective angle of the board in radians.
@ -22,8 +22,4 @@ class BoardDimensions {
/// Factor the board shrinks by from the closest point to the farthest.
static const perspectiveShrinkFactor = 0.63;
/// Board height based on the [perspectiveShrinkFactor].
static final shrinkAdjustedHeight =
(1 / (1 - perspectiveShrinkFactor)) * size.y;
}

@ -3,6 +3,7 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template boundaries}
/// A [Blueprint] which creates the [_BottomBoundary] and [_OuterBoundary].
@ -30,9 +31,9 @@ class _BottomBoundary extends BodyComponent with InitialPosition {
final bottomLeftCurve = BezierCurveShape(
controlPoints: [
Vector2(-43.9, -41.8),
Vector2(-35.7, -43),
Vector2(-19.9, -51),
Vector2(-43.9, 41.8),
Vector2(-35.7, 43),
Vector2(-19.9, 51),
],
);
final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurve);
@ -40,9 +41,9 @@ class _BottomBoundary extends BodyComponent with InitialPosition {
final bottomRightCurve = BezierCurveShape(
controlPoints: [
Vector2(31.8, -44.8),
Vector2(21.95, -47.7),
Vector2(12.3, -52.1),
Vector2(31.8, 44.8),
Vector2(21.95, 47.7),
Vector2(12.3, 52.1),
],
);
final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurve);
@ -95,17 +96,17 @@ class _OuterBoundary extends BodyComponent with InitialPosition {
final topWall = EdgeShape()
..set(
Vector2(3.6, 70.2),
Vector2(-14.1, 70.2),
Vector2(3.6, -70.2),
Vector2(-14.1, -70.2),
);
final topWallFixtureDef = FixtureDef(topWall);
fixturesDefs.add(topWallFixtureDef);
final topLeftCurve = BezierCurveShape(
controlPoints: [
Vector2(-32.3, 57.2),
Vector2(-31.5, 69.9),
Vector2(-14.1, 70.2),
Vector2(-32.3, -57.2),
Vector2(-31.5, -69.9),
Vector2(-14.1, -70.2),
],
);
final topLeftCurveFixtureDef = FixtureDef(topLeftCurve);
@ -113,8 +114,8 @@ class _OuterBoundary extends BodyComponent with InitialPosition {
final leftWall = EdgeShape()
..set(
Vector2(-32.3, 57.2),
Vector2(-43.9, -41.8),
Vector2(-32.3, -57.2),
Vector2(-43.9, 41.8),
);
final leftWallFixtureDef = FixtureDef(leftWall);
fixturesDefs.add(leftWallFixtureDef);

@ -54,12 +54,14 @@ class ChromeDino extends BodyComponent with InitialPosition {
// TODO(alestiago): Subject to change when sprites are added.
final box = PolygonShape()..setAsBoxXY(size.x / 2, size.y / 2);
final fixtureDef = FixtureDef(box)
..shape = box
..density = 999
..friction = 0.3
..restitution = 0.1
..isSensor = true;
final fixtureDef = FixtureDef(
box,
density: 999,
friction: 0.3,
restitution: 0.1,
isSensor: true,
);
fixtureDefs.add(fixtureDef);
// FIXME(alestiago): Investigate why adding these fixtures is considered as
@ -93,10 +95,11 @@ class ChromeDino extends BodyComponent with InitialPosition {
@override
Body createBody() {
final bodyDef = BodyDef()
..gravityScale = 0
..position = initialPosition
..type = BodyType.dynamic;
final bodyDef = BodyDef(
position: initialPosition,
type: BodyType.dynamic,
gravityScale: Vector2.zero(),
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
@ -111,10 +114,7 @@ class ChromeDino extends BodyComponent with InitialPosition {
class _ChromeDinoAnchor extends JointAnchor {
/// {@macro flipper_anchor}
_ChromeDinoAnchor() {
initialPosition = Vector2(
ChromeDino.size.x / 2,
0,
);
initialPosition = Vector2(ChromeDino.size.x / 2, 0);
}
}

@ -1,5 +1,5 @@
export 'alien_bumper.dart';
export 'backboard.dart';
export 'backboard/backboard.dart';
export 'ball.dart';
export 'baseboard.dart';
export 'board_dimensions.dart';

@ -77,12 +77,12 @@ class BigDashNestBumper extends DashNestBumper {
center: Vector2.zero(),
majorRadius: 5.1,
minorRadius: 3.75,
)..rotate(math.pi / 2.1);
)..rotate(math.pi / 1.9);
final fixtureDef = FixtureDef(shape);
final bodyDef = BodyDef()
..position = initialPosition
..userData = this;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
@ -130,13 +130,14 @@ class SmallDashNestBumper extends DashNestBumper {
majorRadius: 3,
minorRadius: 2.25,
)..rotate(math.pi / 2);
final fixtureDef = FixtureDef(shape)
..friction = 0
..restitution = 4;
final bodyDef = BodyDef()
..position = initialPosition
..userData = this;
final fixtureDef = FixtureDef(
shape,
restitution: 4,
);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}

@ -6,6 +6,7 @@ import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template dinowalls}
/// A [Blueprint] which creates walls for the [ChromeDino].
@ -35,8 +36,8 @@ class _DinoTopWall extends BodyComponent with InitialPosition {
final topStraightShape = EdgeShape()
..set(
Vector2(29.5, 35.1),
Vector2(28.4, 35.1),
Vector2(28.4, -35.1),
Vector2(29.5, -35.1),
);
final topStraightFixtureDef = FixtureDef(topStraightShape);
fixturesDef.add(topStraightFixtureDef);
@ -44,8 +45,8 @@ class _DinoTopWall extends BodyComponent with InitialPosition {
final topCurveShape = BezierCurveShape(
controlPoints: [
topStraightShape.vertex1,
Vector2(17.4, 26.38),
Vector2(25.5, 20.7),
Vector2(17.4, -26.38),
Vector2(25.5, -20.7),
],
);
fixturesDef.add(FixtureDef(topCurveShape));
@ -53,8 +54,8 @@ class _DinoTopWall extends BodyComponent with InitialPosition {
final middleCurveShape = BezierCurveShape(
controlPoints: [
topCurveShape.vertices.last,
Vector2(27.8, 20.1),
Vector2(26.8, 19.5),
Vector2(27.8, -20.1),
Vector2(26.8, -19.5),
],
);
fixturesDef.add(FixtureDef(middleCurveShape));
@ -62,8 +63,8 @@ class _DinoTopWall extends BodyComponent with InitialPosition {
final bottomCurveShape = BezierCurveShape(
controlPoints: [
middleCurveShape.vertices.last,
Vector2(21.15, 16),
Vector2(25.6, 15.2),
Vector2(21.15, -16),
Vector2(25.6, -15.2),
],
);
fixturesDef.add(FixtureDef(bottomCurveShape));
@ -71,7 +72,7 @@ class _DinoTopWall extends BodyComponent with InitialPosition {
final bottomStraightShape = EdgeShape()
..set(
bottomCurveShape.vertices.last,
Vector2(31, 14.5),
Vector2(31, -14.5),
);
final bottomStraightFixtureDef = FixtureDef(bottomStraightShape);
fixturesDef.add(bottomStraightFixtureDef);
@ -81,10 +82,11 @@ class _DinoTopWall extends BodyComponent with InitialPosition {
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition
..type = BodyType.static;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(
(fixture) => body.createFixture(
@ -128,51 +130,65 @@ class _DinoBottomWall extends BodyComponent with InitialPosition {
List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
const restitution = 1.0;
final topStraightControlPoints = [
Vector2(32.4, 8.3),
Vector2(25, 7.7),
Vector2(32.4, -8.3),
Vector2(25, -7.7),
];
final topStraightShape = EdgeShape()
..set(
topStraightControlPoints.first,
topStraightControlPoints.last,
);
final topStraightFixtureDef = FixtureDef(topStraightShape);
final topStraightFixtureDef = FixtureDef(
topStraightShape,
restitution: restitution,
);
fixturesDef.add(topStraightFixtureDef);
final topLeftCurveControlPoints = [
topStraightControlPoints.last,
Vector2(21.8, 7),
Vector2(29.5, -13.8),
Vector2(21.8, -7),
Vector2(29.5, 13.8),
];
final topLeftCurveShape = BezierCurveShape(
controlPoints: topLeftCurveControlPoints,
);
fixturesDef.add(FixtureDef(topLeftCurveShape));
final topLeftCurveFixtureDef = FixtureDef(
topLeftCurveShape,
restitution: restitution,
);
fixturesDef.add(topLeftCurveFixtureDef);
final bottomLeftStraightControlPoints = [
topLeftCurveControlPoints.last,
Vector2(31.8, -44.1),
Vector2(31.8, 44.1),
];
final bottomLeftStraightShape = EdgeShape()
..set(
bottomLeftStraightControlPoints.first,
bottomLeftStraightControlPoints.last,
);
final bottomLeftStraightFixtureDef = FixtureDef(bottomLeftStraightShape);
final bottomLeftStraightFixtureDef = FixtureDef(
bottomLeftStraightShape,
restitution: restitution,
);
fixturesDef.add(bottomLeftStraightFixtureDef);
final bottomStraightControlPoints = [
bottomLeftStraightControlPoints.last,
Vector2(37.8, -44.1),
Vector2(37.8, 44.1),
];
final bottomStraightShape = EdgeShape()
..set(
bottomStraightControlPoints.first,
bottomStraightControlPoints.last,
);
final bottomStraightFixtureDef = FixtureDef(bottomStraightShape);
final bottomStraightFixtureDef = FixtureDef(
bottomStraightShape,
restitution: restitution,
);
fixturesDef.add(bottomStraightFixtureDef);
return fixturesDef;
@ -180,19 +196,13 @@ class _DinoBottomWall extends BodyComponent with InitialPosition {
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition
..type = BodyType.static;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(
(fixture) => body.createFixture(
fixture
..restitution = 0.1
..friction = 0,
),
);
_createFixtureDefs().forEach(body.createFixture);
return body;
}

@ -73,7 +73,7 @@ class FireEffect extends ParticleSystemComponent {
spreadTween.transform(random.nextDouble()),
spreadTween.transform(random.nextDouble()),
);
final finalDirection = Vector2(direction.x, -direction.y) + spread;
final finalDirection = Vector2(direction.x, direction.y) + spread;
final speed = finalDirection * (burstPower * 20);
return AcceleratedParticle(

@ -32,13 +32,13 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
/// Applies downward linear velocity to the [Flipper], moving it to its
/// resting position.
void moveDown() {
body.linearVelocity = Vector2(0, -_speed);
body.linearVelocity = Vector2(0, _speed);
}
/// Applies upward linear velocity to the [Flipper], moving it to its highest
/// position.
void moveUp() {
body.linearVelocity = Vector2(0, _speed);
body.linearVelocity = Vector2(0, -_speed);
}
/// Anchors the [Flipper] to the [RevoluteJoint] that controls its arc motion.
@ -99,9 +99,11 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
Vector2(smallCircleShape.position.x, -smallCircleShape.radius),
];
final trapezium = PolygonShape()..set(trapeziumVertices);
final trapeziumFixtureDef = FixtureDef(trapezium)
..density = 50.0 // TODO(alestiago): Use a proper density.
..friction = .1; // TODO(alestiago): Use a proper friction.
final trapeziumFixtureDef = FixtureDef(
trapezium,
density: 50, // TODO(alestiago): Use a proper density.
friction: .1, // TODO(alestiago): Use a proper friction.
);
fixturesDef.add(trapeziumFixtureDef);
return fixturesDef;
@ -118,10 +120,12 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
@override
Body createBody() {
final bodyDef = BodyDef()
..position = initialPosition
..gravityScale = 0
..type = BodyType.dynamic;
final bodyDef = BodyDef(
position: initialPosition,
gravityScale: Vector2.zero(),
type: BodyType.dynamic,
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
@ -161,7 +165,7 @@ class _FlipperAnchor extends JointAnchor {
initialPosition = Vector2(
(Flipper.size.x * flipper.side.direction) / 2 -
(1.65 * flipper.side.direction),
0.15,
-0.15,
);
}
}
@ -209,8 +213,8 @@ class _FlipperJoint extends RevoluteJoint {
void lock() {
const angle = _halfSweepingAngle;
setLimits(
-angle * side.direction,
-angle * side.direction,
angle * side.direction,
angle * side.direction,
);
}

@ -18,7 +18,9 @@ class FlutterSignPost extends BodyComponent with InitialPosition {
Body createBody() {
final shape = CircleShape()..radius = 0.25;
final fixtureDef = FixtureDef(shape);
final bodyDef = BodyDef()..position = initialPosition;
final bodyDef = BodyDef(
position: initialPosition,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}

@ -33,12 +33,14 @@ class GoogleLetter extends BodyComponent with InitialPosition {
@override
Body createBody() {
final shape = CircleShape()..radius = 1.85;
final fixtureDef = FixtureDef(shape)..isSensor = true;
final bodyDef = BodyDef()
..position = initialPosition
..userData = this
..type = BodyType.static;
final fixtureDef = FixtureDef(
shape,
isSensor: true,
);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}

@ -22,7 +22,9 @@ class JointAnchor extends BodyComponent with InitialPosition {
@override
Body createBody() {
final bodyDef = BodyDef()..position = initialPosition;
final bodyDef = BodyDef(
position: initialPosition,
);
return world.createBody(bodyDef);
}
}

@ -34,16 +34,16 @@ class Kicker extends BodyComponent with InitialPosition {
const quarterPi = math.pi / 4;
final upperCircle = CircleShape()..radius = 1.6;
upperCircle.position.setValues(0, -upperCircle.radius / 2);
final upperCircleFixtureDef = FixtureDef(upperCircle)..friction = 0;
upperCircle.position.setValues(0, upperCircle.radius / 2);
final upperCircleFixtureDef = FixtureDef(upperCircle);
fixturesDefs.add(upperCircleFixtureDef);
final lowerCircle = CircleShape()..radius = 1.6;
lowerCircle.position.setValues(
size.x * -direction,
-size.y - 0.8,
size.y + 0.8,
);
final lowerCircleFixtureDef = FixtureDef(lowerCircle)..friction = 0;
final lowerCircleFixtureDef = FixtureDef(lowerCircle);
fixturesDefs.add(lowerCircleFixtureDef);
final wallFacingEdge = EdgeShape()
@ -53,9 +53,9 @@ class Kicker extends BodyComponent with InitialPosition {
upperCircle.radius * direction,
0,
),
Vector2(2.5 * direction, -size.y + 2),
Vector2(2.5 * direction, size.y - 2),
);
final wallFacingLineFixtureDef = FixtureDef(wallFacingEdge)..friction = 0;
final wallFacingLineFixtureDef = FixtureDef(wallFacingEdge);
fixturesDefs.add(wallFacingLineFixtureDef);
final bottomEdge = EdgeShape()
@ -64,10 +64,10 @@ class Kicker extends BodyComponent with InitialPosition {
lowerCircle.position +
Vector2(
lowerCircle.radius * math.cos(quarterPi) * direction,
-lowerCircle.radius * math.sin(quarterPi),
lowerCircle.radius * math.sin(quarterPi),
),
);
final bottomLineFixtureDef = FixtureDef(bottomEdge)..friction = 0;
final bottomLineFixtureDef = FixtureDef(bottomEdge);
fixturesDefs.add(bottomLineFixtureDef);
final bouncyEdge = EdgeShape()
@ -75,19 +75,20 @@ class Kicker extends BodyComponent with InitialPosition {
upperCircle.position +
Vector2(
upperCircle.radius * math.cos(quarterPi) * -direction,
upperCircle.radius * math.sin(quarterPi),
-upperCircle.radius * math.sin(quarterPi),
),
lowerCircle.position +
Vector2(
lowerCircle.radius * math.cos(quarterPi) * -direction,
lowerCircle.radius * math.sin(quarterPi),
-lowerCircle.radius * math.sin(quarterPi),
),
);
final bouncyFixtureDef = FixtureDef(bouncyEdge)
final bouncyFixtureDef = FixtureDef(
bouncyEdge,
// TODO(alestiago): Play with restitution value once game is bundled.
..restitution = 10.0
..friction = 0;
restitution: 10,
);
fixturesDefs.add(bouncyFixtureDef);
// TODO(alestiago): Evaluate if there is value on centering the fixtures.
@ -97,7 +98,7 @@ class Kicker extends BodyComponent with InitialPosition {
lowerCircle.position +
Vector2(
lowerCircle.radius * math.cos(quarterPi) * -direction,
-lowerCircle.radius * math.sin(quarterPi),
lowerCircle.radius * math.sin(quarterPi),
),
wallFacingEdge.vertex2,
],
@ -111,7 +112,9 @@ class Kicker extends BodyComponent with InitialPosition {
@override
Body createBody() {
final bodyDef = BodyDef()..position = initialPosition;
final bodyDef = BodyDef(
position: initialPosition,
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);

@ -5,6 +5,7 @@ import 'dart:math' as math;
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template launch_ramp}
/// A [Blueprint] which creates the [_LaunchRampBase] and
@ -22,10 +23,10 @@ class LaunchRamp extends Forge2DBlueprint {
final launchRampForegroundRailing = _LaunchRampForegroundRailing();
final launchRampExit = _LaunchRampExit(rotation: math.pi / 2)
..initialPosition = Vector2(0.6, 34);
..initialPosition = Vector2(0.6, -34);
final launchRampCloseWall = _LaunchRampCloseWall()
..initialPosition = Vector2(4, 66.5);
..initialPosition = Vector2(4, -69.5);
addAll([
launchRampBase,
@ -50,50 +51,50 @@ class _LaunchRampBase extends BodyComponent with InitialPosition, Layered {
final rightStraightShape = EdgeShape()
..set(
Vector2(31.4, 61.4),
Vector2(46.5, -68.4),
Vector2(31.4, -61.4),
Vector2(46.5, 68.4),
);
final rightStraightFixtureDef = FixtureDef(rightStraightShape);
fixturesDef.add(rightStraightFixtureDef);
final leftStraightShape = EdgeShape()
..set(
Vector2(27.8, 61.4),
Vector2(41.5, -68.4),
Vector2(27.8, -61.4),
Vector2(41.5, 68.4),
);
final leftStraightFixtureDef = FixtureDef(leftStraightShape);
fixturesDef.add(leftStraightFixtureDef);
final topCurveShape = ArcShape(
center: Vector2(20.5, 61.1),
center: Vector2(20.5, -61.1),
arcRadius: 11,
angle: 1.6,
rotation: -1.65,
rotation: 0.1,
);
final topCurveFixtureDef = FixtureDef(topCurveShape);
fixturesDef.add(topCurveFixtureDef);
final bottomCurveShape = ArcShape(
center: Vector2(19.3, 60.3),
center: Vector2(19.3, -60.3),
arcRadius: 8.5,
angle: 1.48,
rotation: -1.58,
rotation: 0.1,
);
final bottomCurveFixtureDef = FixtureDef(bottomCurveShape);
fixturesDef.add(bottomCurveFixtureDef);
final topStraightShape = EdgeShape()
..set(
Vector2(3.7, 70.1),
Vector2(19.1, 72.1),
Vector2(3.7, -70.1),
Vector2(19.1, -72.1),
);
final topStraightFixtureDef = FixtureDef(topStraightShape);
fixturesDef.add(topStraightFixtureDef);
final bottomStraightShape = EdgeShape()
..set(
Vector2(3.7, 66.9),
Vector2(19.1, 68.8),
Vector2(3.7, -66.9),
Vector2(19.1, -68.8),
);
final bottomStraightFixtureDef = FixtureDef(bottomStraightShape);
fixturesDef.add(bottomStraightFixtureDef);
@ -103,9 +104,10 @@ class _LaunchRampBase extends BodyComponent with InitialPosition, Layered {
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
@ -150,25 +152,25 @@ class _LaunchRampForegroundRailing extends BodyComponent with InitialPosition {
final rightStraightShape = EdgeShape()
..set(
Vector2(27.6, 57.9),
Vector2(30, 35.1),
Vector2(27.6, -57.9),
Vector2(30, -35.1),
);
final rightStraightFixtureDef = FixtureDef(rightStraightShape);
fixturesDef.add(rightStraightFixtureDef);
final curveShape = ArcShape(
center: Vector2(20.1, 59.3),
center: Vector2(20.1, -59.3),
arcRadius: 7.5,
angle: 1.8,
rotation: -1.63,
rotation: -0.13,
);
final curveFixtureDef = FixtureDef(curveShape);
fixturesDef.add(curveFixtureDef);
final topStraightShape = EdgeShape()
..set(
Vector2(3.7, 66.8),
Vector2(19.7, 66.8),
Vector2(3.7, -66.8),
Vector2(19.7, -66.8),
);
final topStraightFixtureDef = FixtureDef(topStraightShape);
fixturesDef.add(topStraightFixtureDef);
@ -221,7 +223,7 @@ class _LaunchRampCloseWall extends BodyComponent with InitialPosition, Layered {
@override
Body createBody() {
final shape = EdgeShape()..set(Vector2.zero(), Vector2(0, 4));
final shape = EdgeShape()..set(Vector2.zero(), Vector2(0, 3));
final fixtureDef = FixtureDef(shape);

@ -33,18 +33,19 @@ class Plunger extends BodyComponent with InitialPosition, Layered {
final fixtureDef = FixtureDef(shape)..density = 80;
final bodyDef = BodyDef()
..position = initialPosition
..userData = this
..type = BodyType.dynamic
..gravityScale = 0;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
type: BodyType.dynamic,
gravityScale: Vector2.zero(),
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
/// Set a constant downward velocity on the [Plunger].
void pull() {
body.linearVelocity = Vector2(0, -7);
body.linearVelocity = Vector2(0, 7);
}
/// Set an upward velocity on the [Plunger].
@ -107,7 +108,7 @@ class PlungerAnchor extends JointAnchor {
}) {
initialPosition = Vector2(
0,
-plunger.compressionDistance,
plunger.compressionDistance,
);
}
}

@ -2,6 +2,7 @@
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template ramp_orientation}
/// Determines if a ramp is facing [up] or [down] on the Board.
@ -65,12 +66,14 @@ abstract class RampOpening extends BodyComponent with InitialPosition, Layered {
@override
Body createBody() {
final fixtureDef = FixtureDef(shape)..isSensor = true;
final bodyDef = BodyDef()
..userData = this
..position = initialPosition
..type = BodyType.static;
final fixtureDef = FixtureDef(
shape,
isSensor: true,
);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
@ -112,9 +115,9 @@ class RampOpeningBallContactCallback<Opening extends RampOpening>
// now doesn't work position.y comparison
final isBallOutsideOpening =
(opening.orientation == RampOrientation.down &&
ball.body.linearVelocity.y < 0) ||
ball.body.linearVelocity.y > 0) ||
(opening.orientation == RampOrientation.up &&
ball.body.linearVelocity.y > 0);
ball.body.linearVelocity.y < 0);
if (isBallOutsideOpening) {
ball

@ -1,47 +1,32 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:math' as math;
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template slingshots}
/// A [Blueprint] which creates the left and right pairs of [Slingshot]s.
/// A [Blueprint] which creates the pair of [Slingshot]s on the right side of
/// the board.
/// {@endtemplate}
class Slingshots extends Forge2DBlueprint {
@override
void build(_) {
// TODO(allisonryan0002): use radians values instead of converting degrees.
final leftUpperSlingshot = Slingshot(
length: 5.66,
angle: -1.5 * (math.pi / 180),
spritePath: Assets.images.slingshot.leftUpper.keyName,
)..initialPosition = Vector2(-29, 1.5);
final leftLowerSlingshot = Slingshot(
length: 3.54,
angle: -29.1 * (math.pi / 180),
spritePath: Assets.images.slingshot.leftLower.keyName,
)..initialPosition = Vector2(-31, -6.2);
final rightUpperSlingshot = Slingshot(
final upperSlingshot = Slingshot(
length: 5.64,
angle: 1 * (math.pi / 180),
spritePath: Assets.images.slingshot.rightUpper.keyName,
)..initialPosition = Vector2(22.3, 1.58);
angle: -0.017,
spritePath: Assets.images.slingshot.upper.keyName,
)..initialPosition = Vector2(22.3, -1.58);
final rightLowerSlingshot = Slingshot(
final lowerSlingshot = Slingshot(
length: 3.46,
angle: 26.8 * (math.pi / 180),
spritePath: Assets.images.slingshot.rightLower.keyName,
)..initialPosition = Vector2(24.7, -6.2);
angle: -0.468,
spritePath: Assets.images.slingshot.lower.keyName,
)..initialPosition = Vector2(24.7, 6.2);
addAll([
leftUpperSlingshot,
leftLowerSlingshot,
rightUpperSlingshot,
rightLowerSlingshot,
upperSlingshot,
lowerSlingshot,
]);
}
}
@ -71,13 +56,13 @@ class Slingshot extends BodyComponent with InitialPosition {
const circleRadius = 1.55;
final topCircleShape = CircleShape()..radius = circleRadius;
topCircleShape.position.setValues(0, _length / 2);
final topCircleFixtureDef = FixtureDef(topCircleShape)..friction = 0;
topCircleShape.position.setValues(0, -_length / 2);
final topCircleFixtureDef = FixtureDef(topCircleShape);
fixturesDef.add(topCircleFixtureDef);
final bottomCircleShape = CircleShape()..radius = circleRadius;
bottomCircleShape.position.setValues(0, -_length / 2);
final bottomCircleFixtureDef = FixtureDef(bottomCircleShape)..friction = 0;
bottomCircleShape.position.setValues(0, _length / 2);
final bottomCircleFixtureDef = FixtureDef(bottomCircleShape);
fixturesDef.add(bottomCircleFixtureDef);
final leftEdgeShape = EdgeShape()
@ -85,9 +70,11 @@ class Slingshot extends BodyComponent with InitialPosition {
Vector2(circleRadius, _length / 2),
Vector2(circleRadius, -_length / 2),
);
final leftEdgeShapeFixtureDef = FixtureDef(leftEdgeShape)
..friction = 0
..restitution = 5;
final leftEdgeShapeFixtureDef = FixtureDef(
leftEdgeShape,
restitution: 5,
);
fixturesDef.add(leftEdgeShapeFixtureDef);
final rightEdgeShape = EdgeShape()
@ -95,9 +82,10 @@ class Slingshot extends BodyComponent with InitialPosition {
Vector2(-circleRadius, _length / 2),
Vector2(-circleRadius, -_length / 2),
);
final rightEdgeShapeFixtureDef = FixtureDef(rightEdgeShape)
..friction = 0
..restitution = 5;
final rightEdgeShapeFixtureDef = FixtureDef(
rightEdgeShape,
restitution: 5,
);
fixturesDef.add(rightEdgeShapeFixtureDef);
return fixturesDef;
@ -105,10 +93,11 @@ class Slingshot extends BodyComponent with InitialPosition {
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition
..angle = _angle;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
angle: _angle,
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
@ -131,7 +120,7 @@ class Slingshot extends BodyComponent with InitialPosition {
sprite: sprite,
size: sprite.originalSize / 10,
anchor: Anchor.center,
angle: _angle,
angle: -_angle,
),
);
}

@ -7,6 +7,7 @@ import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship}
/// A [Blueprint] which creates the spaceship feature.
@ -35,8 +36,8 @@ class Spaceship extends Forge2DBlueprint {
SpaceshipHole(
outsideLayer: Layer.spaceshipExitRail,
outsidePriority: Ball.spaceshipRailPriority,
)..initialPosition = position - Vector2(5.2, 4.8),
SpaceshipHole()..initialPosition = position - Vector2(-7.2, 0.8),
)..initialPosition = position - Vector2(5.2, -4.8),
SpaceshipHole()..initialPosition = position - Vector2(-7.2, -0.8),
SpaceshipWall()..initialPosition = position,
]);
}
@ -71,17 +72,17 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
@override
Body createBody() {
final circleShape = CircleShape()..radius = 3;
final bodyDef = BodyDef()
..userData = this
..position = initialPosition
..type = BodyType.static;
final shape = CircleShape()..radius = 3;
final fixtureDef = FixtureDef(
shape,
isSensor: true,
);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)
..createFixture(
FixtureDef(circleShape)..isSensor = true,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
@ -107,10 +108,10 @@ class AndroidHead extends BodyComponent with InitialPosition, Layered {
Body createBody() {
final circleShape = CircleShape()..radius = 2;
final bodyDef = BodyDef()
..userData = this
..position = initialPosition
..type = BodyType.static;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)
..createFixture(
@ -197,10 +198,10 @@ class SpaceshipHole extends RampOpening {
@override
Shape get shape {
return ArcShape(
center: Vector2(0, 3.2),
center: Vector2(0, -3.2),
arcRadius: 5,
angle: 1,
rotation: 60 * pi / 180,
rotation: -2,
);
}
}
@ -246,18 +247,16 @@ class SpaceshipWall extends BodyComponent with InitialPosition, Layered {
Body createBody() {
renderBody = false;
final wallShape = _SpaceshipWallShape();
final shape = _SpaceshipWallShape();
final fixtureDef = FixtureDef(shape);
final bodyDef = BodyDef()
..userData = this
..position = initialPosition
..angle = 90 * pi / 172
..type = BodyType.static;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
angle: -1.7,
);
return world.createBody(bodyDef)
..createFixture(
FixtureDef(wallShape)..restitution = 1,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}

@ -6,6 +6,7 @@ import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_rail}
/// A [Blueprint] for the spaceship drop tube.
@ -23,9 +24,9 @@ class SpaceshipRail extends Forge2DBlueprint {
final railRamp = _SpaceshipRailRamp();
final railEnd = SpaceshipRailExit();
final topBase = _SpaceshipRailBase(radius: 0.55)
..initialPosition = Vector2(-26.15, 18.65);
..initialPosition = Vector2(-26.15, -18.65);
final bottomBase = _SpaceshipRailBase(radius: 0.8)
..initialPosition = Vector2(-25.5, -12.9);
..initialPosition = Vector2(-25.5, 12.9);
final railForeground = _SpaceshipRailForeground();
addAll([
@ -51,19 +52,19 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
final fixturesDefs = <FixtureDef>[];
final topArcShape = ArcShape(
center: Vector2(-35.5, 30.9),
center: Vector2(-35.5, -30.9),
arcRadius: 2.5,
angle: math.pi,
rotation: 2.9,
rotation: 0.2,
);
final topArcFixtureDef = FixtureDef(topArcShape);
fixturesDefs.add(topArcFixtureDef);
final topLeftCurveShape = BezierCurveShape(
controlPoints: [
Vector2(-37.9, 30.4),
Vector2(-38, 23.9),
Vector2(-30.93, 18.2),
Vector2(-37.9, -30.4),
Vector2(-38, -23.9),
Vector2(-30.93, -18.2),
],
);
final topLeftCurveFixtureDef = FixtureDef(topLeftCurveShape);
@ -72,8 +73,8 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
final middleLeftCurveShape = BezierCurveShape(
controlPoints: [
topLeftCurveShape.vertices.last,
Vector2(-22.6, 10.3),
Vector2(-30, 0.2),
Vector2(-22.6, -10.3),
Vector2(-30, -0.2),
],
);
final middleLeftCurveFixtureDef = FixtureDef(middleLeftCurveShape);
@ -82,8 +83,8 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
final bottomLeftCurveShape = BezierCurveShape(
controlPoints: [
middleLeftCurveShape.vertices.last,
Vector2(-36, -8.6),
Vector2(-32.04, -18.3),
Vector2(-36, 8.6),
Vector2(-32.04, 18.3),
],
);
final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurveShape);
@ -91,8 +92,8 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
final topRightStraightShape = EdgeShape()
..set(
Vector2(-33, 31.3),
Vector2(-27.2, 21.3),
Vector2(-33, -31.3),
Vector2(-27.2, -21.3),
);
final topRightStraightFixtureDef = FixtureDef(topRightStraightShape);
fixturesDefs.add(topRightStraightFixtureDef);
@ -100,8 +101,8 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
final middleRightCurveShape = BezierCurveShape(
controlPoints: [
topRightStraightShape.vertex1,
Vector2(-16.5, 11.4),
Vector2(-25.29, -1.7),
Vector2(-16.5, -11.4),
Vector2(-25.29, 1.7),
],
);
final middleRightCurveFixtureDef = FixtureDef(middleRightCurveShape);
@ -110,8 +111,8 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
final bottomRightCurveShape = BezierCurveShape(
controlPoints: [
middleRightCurveShape.vertices.last,
Vector2(-29.91, -8.5),
Vector2(-26.8, -15.7),
Vector2(-29.91, 8.5),
Vector2(-26.8, 15.7),
],
);
final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurveShape);
@ -122,9 +123,10 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
@ -189,12 +191,11 @@ class _SpaceshipRailBase extends BodyComponent with InitialPosition, Layered {
@override
Body createBody() {
final shape = CircleShape()..radius = radius;
final fixtureDef = FixtureDef(shape);
final bodyDef = BodyDef()
..position = initialPosition
..userData = this;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
@ -219,10 +220,10 @@ class SpaceshipRailExit extends RampOpening {
@override
Shape get shape {
return ArcShape(
center: Vector2(-29, -19),
center: Vector2(-29, 19),
arcRadius: 2.5,
angle: math.pi * 0.4,
rotation: 0.26,
rotation: -1.4,
);
}
}

@ -6,6 +6,7 @@ import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_ramp}
/// A [Blueprint] which creates the ramp leading into the [Spaceship].
@ -25,14 +26,14 @@ class SpaceshipRamp extends Forge2DBlueprint {
outsidePriority: 1,
rotation: math.pi,
)
..initialPosition = Vector2(1.7, 19.8)
..initialPosition = Vector2(1.7, -19.8)
..layer = Layer.opening;
final leftOpening = _SpaceshipRampOpening(
outsideLayer: Layer.spaceship,
outsidePriority: Ball.spaceshipPriority,
rotation: math.pi,
)
..initialPosition = Vector2(-13.7, 18.6)
..initialPosition = Vector2(-13.7, -18.6)
..layer = Layer.spaceshipEntranceRamp;
final spaceshipRamp = _SpaceshipRampBackground();
@ -43,7 +44,7 @@ class SpaceshipRamp extends Forge2DBlueprint {
final spaceshipRampForegroundRailing = _SpaceshipRampForegroundRailing();
final baseRight = _SpaceshipRampBase()..initialPosition = Vector2(1.7, 20);
final baseRight = _SpaceshipRampBase()..initialPosition = Vector2(1.7, -20);
addAll([
spaceshipRampBoardOpeningSprite,
@ -70,30 +71,28 @@ class _SpaceshipRampBackground extends BodyComponent
final outerLeftCurveShape = BezierCurveShape(
controlPoints: [
Vector2(-30.75, 37.3),
Vector2(-32.5, 71.25),
Vector2(-14.2, 71.25),
Vector2(-30.75, -37.3),
Vector2(-32.5, -71.25),
Vector2(-14.2, -71.25),
],
);
final outerLeftCurveFixtureDef = FixtureDef(outerLeftCurveShape);
fixturesDef.add(outerLeftCurveFixtureDef);
final outerRightCurveShape = BezierCurveShape(
controlPoints: [
outerLeftCurveShape.vertices.last,
Vector2(2.5, 71.9),
Vector2(6.1, 44.9),
Vector2(2.5, -71.9),
Vector2(6.1, -44.9),
],
);
final outerRightCurveFixtureDef = FixtureDef(outerRightCurveShape);
fixturesDef.add(outerRightCurveFixtureDef);
final boardOpeningEdgeShape = EdgeShape()
..set(
outerRightCurveShape.vertices.last,
Vector2(7.3, 41.1),
Vector2(7.3, -41.1),
);
final boardOpeningEdgeShapeFixtureDef = FixtureDef(boardOpeningEdgeShape);
fixturesDef.add(boardOpeningEdgeShapeFixtureDef);
@ -103,9 +102,10 @@ class _SpaceshipRampBackground extends BodyComponent
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
@ -179,9 +179,9 @@ class _SpaceshipRampForegroundRailing extends BodyComponent
final innerLeftCurveShape = BezierCurveShape(
controlPoints: [
Vector2(-24.5, 38),
Vector2(-26.3, 64),
Vector2(-13.8, 64.5),
Vector2(-24.5, -38),
Vector2(-26.3, -64),
Vector2(-13.8, -64.5),
],
);
@ -191,8 +191,8 @@ class _SpaceshipRampForegroundRailing extends BodyComponent
final innerRightCurveShape = BezierCurveShape(
controlPoints: [
innerLeftCurveShape.vertices.last,
Vector2(-2.5, 66.2),
Vector2(0, 44.5),
Vector2(-2.5, -66.2),
Vector2(0, -44.5),
],
);
@ -202,7 +202,7 @@ class _SpaceshipRampForegroundRailing extends BodyComponent
final boardOpeningEdgeShape = EdgeShape()
..set(
innerRightCurveShape.vertices.last,
Vector2(-0.85, 40.8),
Vector2(-0.85, -40.8),
);
final boardOpeningEdgeShapeFixtureDef = FixtureDef(boardOpeningEdgeShape);
fixturesDef.add(boardOpeningEdgeShapeFixtureDef);
@ -212,9 +212,10 @@ class _SpaceshipRampForegroundRailing extends BodyComponent
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
@ -259,17 +260,17 @@ class _SpaceshipRampBase extends BodyComponent with InitialPosition, Layered {
controlPoints: [
Vector2(initialPosition.x - baseWidth / 2, initialPosition.y),
Vector2(initialPosition.x - baseWidth / 2, initialPosition.y) +
Vector2(2, 5),
Vector2(2, -5),
Vector2(initialPosition.x + baseWidth / 2, initialPosition.y) +
Vector2(-2, 5),
Vector2(-2, -5),
Vector2(initialPosition.x + baseWidth / 2, initialPosition.y)
],
);
final fixtureDef = FixtureDef(baseShape);
final bodyDef = BodyDef()
..userData = this
..position = initialPosition;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}

@ -89,11 +89,11 @@ class SparkyBumper extends BodyComponent with InitialPosition {
center: Vector2.zero(),
majorRadius: _majorRadius,
minorRadius: _minorRadius,
)..rotate(math.pi / 1.9);
final fixtureDef = FixtureDef(shape)
..friction = 0
..restitution = 4;
)..rotate(math.pi / 2.1);
final fixtureDef = FixtureDef(
shape,
restitution: 4,
);
final bodyDef = BodyDef()
..position = initialPosition
..userData = this;

@ -3,6 +3,7 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_computer}
/// A [Blueprint] which creates the [_ComputerBase] and
@ -29,24 +30,24 @@ class _ComputerBase extends BodyComponent with InitialPosition {
final leftEdge = EdgeShape()
..set(
Vector2(-14.9, 46),
Vector2(-15.3, 49.6),
Vector2(-14.9, -46),
Vector2(-15.3, -49.6),
);
final leftEdgeFixtureDef = FixtureDef(leftEdge);
fixturesDef.add(leftEdgeFixtureDef);
final topEdge = EdgeShape()
..set(
Vector2(-15.3, 49.6),
Vector2(-10.7, 50.6),
Vector2(-15.3, -49.6),
Vector2(-10.7, -50.6),
);
final topEdgeFixtureDef = FixtureDef(topEdge);
fixturesDef.add(topEdgeFixtureDef);
final rightEdge = EdgeShape()
..set(
Vector2(-10.7, 50.6),
Vector2(-9, 47.2),
Vector2(-10.7, -50.6),
Vector2(-9, -47.2),
);
final rightEdgeFixtureDef = FixtureDef(rightEdge);
fixturesDef.add(rightEdgeFixtureDef);
@ -56,9 +57,10 @@ class _ComputerBase extends BodyComponent with InitialPosition {
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);

@ -0,0 +1,11 @@
import 'package:intl/intl.dart';
final _numberFormat = NumberFormat('#,###');
/// Adds score related extensions to int
extension ScoreX on int {
/// Formats this number as a score value
String formatScore() {
return _numberFormat.format(this);
}
}

@ -1,2 +0,0 @@
export 'blueprint.dart';
export 'priority.dart';

@ -1,2 +1,2 @@
export 'components/components.dart';
export 'flame/flame.dart';
export 'extensions/extensions.dart';

@ -7,12 +7,15 @@ environment:
sdk: ">=2.16.0 <3.0.0"
dependencies:
flame: ^1.1.0
flame_forge2d: ^0.10.0
flame: ^1.1.1
flame_forge2d: ^0.11.0
flutter:
sdk: flutter
geometry:
path: ../geometry
intl: ^0.17.0
pinball_flame:
path: ../pinball_flame
dev_dependencies:

@ -11,6 +11,9 @@ abstract class BasicGame extends Forge2DGame {
}
}
abstract class BasicKeyboardGame extends BasicGame
with HasKeyboardHandlerComponents {}
abstract class LineGame extends BasicGame with PanDetector {
Vector2? _lineEnd;

@ -32,6 +32,7 @@ void main() {
addGoogleWordStories(dashbook);
addLaunchRampStories(dashbook);
addScoreTextStories(dashbook);
addBackboardStories(dashbook);
runApp(dashbook);
}

@ -0,0 +1,37 @@
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart';
class BackboardGameOverGame extends BasicKeyboardGame {
BackboardGameOverGame(this.score);
static const info = '''
Simple example showing the waiting mode of the backboard.
''';
final int score;
@override
Future<void> onLoad() async {
camera
..followVector2(Vector2.zero())
..zoom = 5;
await add(
Backboard.gameOver(
position: Vector2(0, 20),
score: score,
onSubmit: (initials) {
add(
ScoreText(
text: 'User $initials made $score',
position: Vector2(0, 50),
color: Colors.pink,
),
);
},
),
);
}
}

@ -0,0 +1,27 @@
import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/backboard/game_over.dart';
import 'package:sandbox/stories/backboard/waiting.dart';
void addBackboardStories(Dashbook dashbook) {
dashbook.storiesOf('Backboard')
..add(
'Waiting mode',
(context) => GameWidget(
game: BackboardWaitingGame(),
),
codeLink: buildSourceLink('backboard/waiting.dart'),
info: BackboardWaitingGame.info,
)
..add(
'Game over',
(context) => GameWidget(
game: BackboardGameOverGame(
context.numberProperty('score', 9000000000).toInt(),
),
),
codeLink: buildSourceLink('backboard/game_over.dart'),
info: BackboardGameOverGame.info,
);
}

@ -0,0 +1,19 @@
import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart';
class BackboardWaitingGame extends BasicGame {
static const info = '''
Simple example showing the waiting mode of the backboard.
''';
@override
Future<void> onLoad() async {
camera
..followVector2(Vector2.zero())
..zoom = 5;
final backboard = Backboard.waiting(position: Vector2(0, 20));
await add(backboard);
}
}

@ -1,5 +1,6 @@
import 'package:flame/extensions.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class LaunchRampGame extends BasicBallGame {

@ -1,5 +1,6 @@
import 'package:flame/extensions.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/common/common.dart';
class BasicSpaceshipGame extends BasicGame with TapDetector {

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SpaceshipRailGame extends BasicBallGame {

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SpaceshipRampGame extends BasicBallGame {

@ -1,4 +1,5 @@
export 'alien_zone/stories.dart';
export 'backboard/stories.dart';
export 'ball/stories.dart';
export 'baseboard/stories.dart';
export 'boundaries/stories.dart';

@ -91,14 +91,14 @@ packages:
name: flame
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.1.1"
flame_forge2d:
dependency: "direct main"
description:
name: flame_forge2d
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.0"
version: "0.11.0"
flutter:
dependency: "direct main"
description: flutter
@ -134,7 +134,7 @@ packages:
name: forge2d
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.0"
version: "0.11.0"
freezed_annotation:
dependency: transitive
description:
@ -149,6 +149,13 @@ packages:
relative: true
source: path
version: "1.0.0+1"
intl:
dependency: transitive
description:
name: intl
url: "https://pub.dartlang.org"
source: hosted
version: "0.17.0"
js:
dependency: transitive
description:
@ -233,6 +240,13 @@ packages:
relative: true
source: path
version: "1.0.0+1"
pinball_flame:
dependency: "direct main"
description:
path: "../../pinball_flame"
relative: true
source: path
version: "1.0.0+1"
platform:
dependency: transitive
description:

@ -8,12 +8,14 @@ environment:
dependencies:
dashbook: ^0.1.7
flame: ^1.1.0
flame_forge2d: ^0.10.0
flame: ^1.1.1
flame_forge2d: ^0.11.0
flutter:
sdk: flutter
pinball_components:
path: ../
pinball_flame:
path: ../../pinball_flame
dev_dependencies:
flutter_test:

@ -1,3 +1,4 @@
import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
class TestGame extends Forge2DGame {
@ -5,3 +6,5 @@ class TestGame extends Forge2DGame {
images.prefix = '';
}
}
class KeyboardTestGame extends TestGame with HasKeyboardHandlerComponents {}

@ -1,7 +1,9 @@
// ignore_for_file: unawaited_futures
// ignore_for_file: unawaited_futures, cascade_invocations
import 'package:flame/components.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
@ -9,7 +11,7 @@ import '../../helpers/helpers.dart';
void main() {
group('Backboard', () {
final tester = FlameTester(TestGame.new);
final tester = FlameTester(KeyboardTestGame.new);
group('on waitingMode', () {
tester.testGameWidget(
@ -17,7 +19,7 @@ void main() {
setUp: (game, tester) async {
game.camera.zoom = 2;
game.camera.followVector2(Vector2.zero());
await game.ensureAdd(Backboard(position: Vector2(0, 15)));
await game.ensureAdd(Backboard.waiting(position: Vector2(0, 15)));
},
verify: (game, tester) async {
await expectLater(
@ -34,20 +36,145 @@ void main() {
setUp: (game, tester) async {
game.camera.zoom = 2;
game.camera.followVector2(Vector2.zero());
final backboard = Backboard(position: Vector2(0, 15));
final backboard = Backboard.gameOver(
position: Vector2(0, 15),
score: 1000,
onSubmit: (_) {},
);
await game.ensureAdd(backboard);
},
verify: (game, tester) async {
final prompts =
game.descendants().whereType<BackboardLetterPrompt>().length;
expect(prompts, equals(3));
final score = game.descendants().firstWhere(
(component) =>
component is TextComponent && component.text == '1,000',
);
expect(score, isNotNull);
},
);
tester.testGameWidget(
'can change the initials',
setUp: (game, tester) async {
final backboard = Backboard.gameOver(
position: Vector2(0, 15),
score: 1000,
onSubmit: (_) {},
);
await game.ensureAdd(backboard);
await backboard.gameOverMode();
await game.ready();
// Focus is already on the first letter
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
await tester.pump();
// Move to the next an press up again
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
await tester.pump();
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
await tester.pump();
// One more time
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
await tester.pump();
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
await tester.pump();
// Back to the previous and increase one more
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
await tester.pump();
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
await tester.pump();
},
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/backboard/game_over.png'),
final backboard = game
.descendants()
.firstWhere((component) => component is BackboardGameOver)
as BackboardGameOver;
expect(backboard.initials, equals('BCB'));
},
);
String? submitedInitials;
tester.testGameWidget(
'submits the initials',
setUp: (game, tester) async {
final backboard = Backboard.gameOver(
position: Vector2(0, 15),
score: 1000,
onSubmit: (value) {
submitedInitials = value;
},
);
await game.ensureAdd(backboard);
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
await tester.pump();
},
verify: (game, tester) async {
expect(submitedInitials, equals('AAA'));
},
);
});
});
group('BackboardLetterPrompt', () {
final tester = FlameTester(KeyboardTestGame.new);
tester.testGameWidget(
'cycles the char up and down when it has focus',
setUp: (game, tester) async {
await game.ensureAdd(
BackboardLetterPrompt(hasFocus: true, position: Vector2.zero()),
);
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
await tester.pump();
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
await tester.pump();
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
await tester.pump();
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.pump();
},
verify: (game, tester) async {
final prompt = game.firstChild<BackboardLetterPrompt>();
expect(prompt?.char, equals('C'));
},
);
tester.testGameWidget(
"does nothing when it doesn't have focus",
setUp: (game, tester) async {
await game.ensureAdd(
BackboardLetterPrompt(position: Vector2.zero()),
);
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
await tester.pump();
},
verify: (game, tester) async {
final prompt = game.firstChild<BackboardLetterPrompt>();
expect(prompt?.char, equals('A'));
},
);
tester.testGameWidget(
'blinks the prompt when it has the focus',
setUp: (game, tester) async {
await game.ensureAdd(
BackboardLetterPrompt(position: Vector2.zero(), hasFocus: true),
);
},
verify: (game, tester) async {
final underscore = game.descendants().whereType<ShapeComponent>().first;
expect(underscore.paint.color, Colors.white);
game.update(2);
expect(underscore.paint.color, Colors.transparent);
},
);
});
}

@ -48,7 +48,7 @@ void main() {
final ball = Ball(baseColor: Colors.blue);
await game.ensureAdd(ball);
ball.body.gravityScale = 0;
ball.body.gravityScale = Vector2.zero();
ball.body.linearVelocity.setValues(10, 10);
game.update(1);
expect(ball.body.position, isNot(equals(ball.initialPosition)));
@ -153,7 +153,7 @@ void main() {
ball.stop();
ball.resume();
ball.body.gravityScale = 0;
ball.body.gravityScale = Vector2.zero();
ball.body.linearVelocity.setValues(10, 10);
game.update(1);
expect(ball.body.position, isNot(equals(ball.initialPosition)));

@ -9,11 +9,30 @@ import '../../helpers/helpers.dart';
void main() {
group('Baseboard', () {
// TODO(allisonryan0002): Add golden tests.
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
final leftBaseboard = Baseboard(
side: BoardSide.left,
)..initialPosition = Vector2(-20, 0);
final rightBaseboard = Baseboard(
side: BoardSide.right,
)..initialPosition = Vector2(20, 0);
await game.ensureAddAll([leftBaseboard, rightBaseboard]);
game.camera.followVector2(Vector2.zero());
},
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/baseboard.png'),
);
},
);
flameTester.test(
'loads correctly',
(game) async {
@ -57,8 +76,8 @@ void main() {
);
await game.ensureAddAll([leftBaseboard, rightBaseboard]);
expect(leftBaseboard.body.angle, isNegative);
expect(rightBaseboard.body.angle, isPositive);
expect(leftBaseboard.body.angle, isPositive);
expect(rightBaseboard.body.angle, isNegative);
},
);
});

@ -19,9 +19,5 @@ void main() {
test('has perspectiveShrinkFactor', () {
expect(BoardDimensions.perspectiveShrinkFactor, equals(0.63));
});
test('has shrinkAdjustedHeight', () {
expect(BoardDimensions.shrinkAdjustedHeight, isNotNull);
});
});
}

@ -4,6 +4,7 @@ import 'package:flame_forge2d/flame_forge2d.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_flame/pinball_flame.dart';
import '../../helpers/helpers.dart';
@ -15,17 +16,15 @@ void main() {
'render correctly',
setUp: (game, tester) async {
await game.addFromBlueprint(Boundaries());
await game.ready();
game.camera.followVector2(Vector2.zero());
game.camera.zoom = 3.9;
},
// TODO(allisonryan0002): enable test when workflows are fixed.
// verify: (game, tester) async {
// await expectLater(
// find.byGame<Forge2DGame>(),
// matchesGoldenFile('golden/boundaries.png'),
// );
// },
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/boundaries.png'),
);
},
);
});
}

@ -1,5 +1,6 @@
// ignore_for_file: cascade_invocations
import 'package:flame/extensions.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
@ -11,6 +12,33 @@ void main() {
final flameTester = FlameTester(TestGame.new);
group('DashAnimatronic', () {
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
await game.ensureAdd(DashAnimatronic()..playing = true);
game.camera.followVector2(Vector2.zero());
},
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/dash_animatronic/start.png'),
);
game.update(1);
await tester.pump();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/dash_animatronic/middle.png'),
);
game.update(4);
await tester.pump();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/dash_animatronic/end.png'),
);
},
);
flameTester.test(
'loads correctly',
(game) async {

@ -1,8 +1,10 @@
// ignore_for_file: cascade_invocations
import 'package:flame_forge2d/flame_forge2d.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_flame/pinball_flame.dart';
import '../../helpers/helpers.dart';
@ -11,6 +13,21 @@ void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
await game.addFromBlueprint(DinoWalls());
game.camera.followVector2(Vector2.zero());
game.camera.zoom = 6.5;
},
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/dino-walls.png'),
);
},
);
flameTester.test(
'loads correctly',
(game) async {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 913 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 894 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 844 KiB

@ -13,9 +13,29 @@ void main() {
final flameTester = FlameTester(TestGame.new);
group('Flipper', () {
// TODO(alestiago): Add golden tests.
// TODO(alestiago): Consider testing always both left and right Flipper.
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
final leftFlipper = Flipper(
side: BoardSide.left,
)..initialPosition = Vector2(-10, 0);
final rightFlipper = Flipper(
side: BoardSide.right,
)..initialPosition = Vector2(10, 0);
await game.ensureAddAll([leftFlipper, rightFlipper]);
game.camera.followVector2(Vector2.zero());
},
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/flipper.png'),
);
},
);
flameTester.test(
'loads correctly',
(game) async {
@ -55,7 +75,7 @@ void main() {
final flipper = Flipper(side: BoardSide.left);
await game.ensureAdd(flipper);
expect(flipper.body.gravityScale, isZero);
expect(flipper.body.gravityScale, equals(Vector2.zero()));
},
);
@ -113,7 +133,7 @@ void main() {
expect(flipper.body.linearVelocity, equals(Vector2.zero()));
flipper.moveDown();
expect(flipper.body.linearVelocity.y, lessThan(0));
expect(flipper.body.linearVelocity.y, isPositive);
},
);
@ -126,7 +146,7 @@ void main() {
expect(flipper.body.linearVelocity, equals(Vector2.zero()));
flipper.moveUp();
expect(flipper.body.linearVelocity.y, greaterThan(0));
expect(flipper.body.linearVelocity.y, isNegative);
},
);
});

@ -1,5 +1,6 @@
// ignore_for_file: cascade_invocations
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
@ -11,6 +12,20 @@ void main() {
final flameTester = FlameTester(TestGame.new);
group('FlutterSignPost', () {
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
await game.ensureAdd(FlutterSignPost());
game.camera.followVector2(Vector2.zero());
},
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/flutter-sign-post.png'),
);
},
);
flameTester.test(
'loads correctly',
(game) async {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save