fix: fixed merge conflicts on spaceshipramp

feat/spaceship-ramp-logic
RuiAlonso 2 years ago
commit 6439b54c18

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

@ -37,8 +37,7 @@ class ControlledBall extends Ball with Controls<BallController> {
/// [Ball] used in [DebugPinballGame].
ControlledBall.debug() : super(baseColor: const Color(0xFFFF0000)) {
controller = DebugBallController(this);
priority = RenderPriority.ballOnSpaceshipRamp;
layer = Layer.spaceshipEntranceRamp;
priority = RenderPriority.ballOnBoard;
}
}

@ -11,19 +11,16 @@ import 'package:pinball_flame/pinball_flame.dart';
/// [SparkyComputer] with a [SparkyComputerController] attached.
/// {@endtemplate}
class ControlledSparkyComputer extends SparkyComputer
with Controls<SparkyComputerController>, HasGameRef<PinballGame> {
with Controls<SparkyComputerController>, HasGameRef<Forge2DGame> {
/// {@macro controlled_sparky_computer}
ControlledSparkyComputer() {
ControlledSparkyComputer() : super() {
controller = SparkyComputerController(this);
}
@override
void build(Forge2DGame _) {
addContactCallback(SparkyTurboChargeSensorBallContactCallback());
final sparkyTurboChargeSensor = SparkyTurboChargeSensor()
..initialPosition = Vector2(-13, -49.8);
add(sparkyTurboChargeSensor);
super.build(_);
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(SparkyComputerSensorBallContactCallback());
}
}
@ -39,46 +36,17 @@ class SparkyComputerController
: super(controlledComputer);
}
/// {@template sparky_turbo_charge_sensor}
/// Small sensor body used to detect when a ball has entered the
/// [SparkyComputer] with the [SparkyTurboChargeSensorBallContactCallback].
/// {@template sparky_computer_sensor_ball_contact_callback}
/// Turbo charges the [Ball] when it enters the [SparkyComputer]
/// {@endtemplate}
@visibleForTesting
class SparkyTurboChargeSensor extends BodyComponent with InitialPosition {
/// {@macro sparky_turbo_charge_sensor}
SparkyTurboChargeSensor() {
renderBody = false;
}
@override
Body createBody() {
final shape = CircleShape()..radius = 0.1;
final fixtureDef = FixtureDef(shape)..isSensor = true;
final bodyDef = BodyDef()
..position = initialPosition
..userData = this;
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
/// {@template sparky_turbo_charge_sensor_ball_contact_callback}
/// Turbo charges the [Ball] on contact with [SparkyTurboChargeSensor].
/// {@endtemplate}
@visibleForTesting
class SparkyTurboChargeSensorBallContactCallback
extends ContactCallback<SparkyTurboChargeSensor, ControlledBall> {
/// {@macro sparky_turbo_charge_sensor_ball_contact_callback}
SparkyTurboChargeSensorBallContactCallback();
class SparkyComputerSensorBallContactCallback
extends ContactCallback<SparkyComputerSensor, ControlledBall> {
/// {@macro sparky_computer_sensor_ball_contact_callback}
SparkyComputerSensorBallContactCallback();
@override
void begin(
SparkyTurboChargeSensor sparkyTurboChargeSensor,
ControlledBall ball,
_,
) {
void begin(_, ControlledBall ball, __) {
ball.controller.turboCharge();
}
}

@ -32,6 +32,8 @@ class GameFlowController extends ComponentController<PinballGame>
// next page
component.firstChild<Backboard>()?.gameOverMode(
score: state?.score ?? 0,
characterIconPath:
component.theme.characterTheme.leaderboardIcon.keyName,
);
component.firstChild<CameraController>()?.focusOnBackboard();
}

@ -7,21 +7,15 @@ import 'package:pinball_flame/pinball_flame.dart';
/// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and
/// [LaunchRamp].
/// {@endtemplate}
class Launcher extends Forge2DBlueprint {
class Launcher extends Blueprint {
/// {@macro launcher}
Launcher();
/// [Plunger] to launch the [Ball] onto the board.
late final Plunger plunger;
@override
void build(Forge2DGame gameRef) {
plunger = ControlledPlunger(compressionDistance: 14)
..initialPosition = Vector2(40.7, 38);
final _rocket = RocketSpriteComponent()..position = Vector2(43, 62);
addAll([_rocket, plunger]);
addBlueprint(LaunchRamp());
}
Launcher()
: super(
components: [
ControlledPlunger(compressionDistance: 14)
..initialPosition = Vector2(40.7, 38),
RocketSpriteComponent()..position = Vector2(43, 62),
],
blueprints: [LaunchRamp()],
);
}

@ -39,22 +39,6 @@ class Wall extends BodyComponent {
}
}
/// Create top, left, and right [Wall]s for the game board.
List<Wall> createBoundaries(Forge2DGame game) {
final topLeft = BoardDimensions.bounds.topLeft.toVector2() + Vector2(18.6, 0);
final bottomRight = BoardDimensions.bounds.bottomRight.toVector2();
final topRight =
BoardDimensions.bounds.topRight.toVector2() - Vector2(18.6, 0);
final bottomLeft = BoardDimensions.bounds.bottomLeft.toVector2();
return [
Wall(start: topLeft, end: topRight),
Wall(start: topRight, end: bottomRight),
Wall(start: topLeft, end: bottomLeft),
];
}
/// {@template bottom_wall}
/// [Wall] located at the bottom of the board.
///

@ -1,11 +1,17 @@
import 'package:pinball/game/game.dart';
import 'package:pinball/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' as components;
import 'package:pinball_theme/pinball_theme.dart' hide Assets;
/// Add methods to help loading and caching game assets.
extension PinballGameAssetsX on PinballGame {
/// Returns a list of assets to be loaded
List<Future> preLoadAssets() {
const dashTheme = DashTheme();
const sparkyTheme = SparkyTheme();
const androidTheme = AndroidTheme();
const dinoTheme = DinoTheme();
return [
images.load(components.Assets.images.ball.ball.keyName),
images.load(components.Assets.images.ball.flameEffect.keyName),
@ -93,6 +99,10 @@ extension PinballGameAssetsX on PinballGame {
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(dashTheme.leaderboardIcon.keyName),
images.load(sparkyTheme.leaderboardIcon.keyName),
images.load(androidTheme.leaderboardIcon.keyName),
images.load(dinoTheme.leaderboardIcon.keyName),
images.load(Assets.images.components.background.path),
];
}

@ -46,7 +46,8 @@ class PinballGame extends Forge2DGame
unawaited(add(CameraController(this)));
unawaited(add(Backboard.waiting(position: Vector2(0, -88))));
await _addGameBoundaries();
// TODO(allisonryan0002): banish Wall and Board classes in later PR.
await add(BottomWall());
unawaited(addFromBlueprint(Boundaries()));
unawaited(addFromBlueprint(ControlledSparkyComputer()));
@ -68,7 +69,7 @@ class PinballGame extends Forge2DGame
);
unawaited(addFromBlueprint(SpaceshipRail()));
controller.attachTo(launcher.plunger);
controller.attachTo(launcher.components.whereType<Plunger>().first);
await super.onLoad();
}
@ -77,11 +78,6 @@ class PinballGame extends Forge2DGame
addContactCallback(BottomWallBallContactCallback());
}
Future<void> _addGameBoundaries() async {
await add(BottomWall());
createBoundaries(this).forEach(add);
}
Future<void> _addBonusWord() async {
await add(
GoogleWord(

@ -0,0 +1,106 @@
// ignore_for_file: public_member_api_docs
import 'package:flame/flame.dart';
import 'package:flame/sprite.dart';
import 'package:flame/widgets.dart';
import 'package:flutter/material.dart' hide Image;
import 'package:pinball/gen/assets.gen.dart';
class BonusAnimation extends StatelessWidget {
const BonusAnimation._(
this.imagePath, {
VoidCallback? onCompleted,
Key? key,
}) : _onCompleted = onCompleted,
super(key: key);
BonusAnimation.dashNest({
Key? key,
VoidCallback? onCompleted,
}) : this._(
Assets.images.bonusAnimation.dashNest.keyName,
onCompleted: onCompleted,
key: key,
);
BonusAnimation.sparkyTurboCharge({
Key? key,
VoidCallback? onCompleted,
}) : this._(
Assets.images.bonusAnimation.sparkyTurboCharge.keyName,
onCompleted: onCompleted,
key: key,
);
BonusAnimation.dino({
Key? key,
VoidCallback? onCompleted,
}) : this._(
Assets.images.bonusAnimation.dino.keyName,
onCompleted: onCompleted,
key: key,
);
BonusAnimation.android({
Key? key,
VoidCallback? onCompleted,
}) : this._(
Assets.images.bonusAnimation.android.keyName,
onCompleted: onCompleted,
key: key,
);
BonusAnimation.google({
Key? key,
VoidCallback? onCompleted,
}) : this._(
Assets.images.bonusAnimation.google.keyName,
onCompleted: onCompleted,
key: key,
);
final String imagePath;
final VoidCallback? _onCompleted;
static Future<void> loadAssets() {
Flame.images.prefix = '';
return Flame.images.loadAll([
Assets.images.bonusAnimation.dashNest.keyName,
Assets.images.bonusAnimation.sparkyTurboCharge.keyName,
Assets.images.bonusAnimation.dino.keyName,
Assets.images.bonusAnimation.android.keyName,
Assets.images.bonusAnimation.google.keyName,
]);
}
@override
Widget build(BuildContext context) {
final spriteSheet = SpriteSheet.fromColumnsAndRows(
image: Flame.images.fromCache(imagePath),
columns: 8,
rows: 9,
);
final animation = spriteSheet.createAnimation(
row: 0,
stepTime: 1 / 24,
to: spriteSheet.rows * spriteSheet.columns,
loop: false,
);
Future<void>.delayed(
Duration(seconds: animation.totalDuration().ceil()),
() {
_onCompleted?.call();
},
);
return SizedBox(
width: double.infinity,
height: double.infinity,
child: SpriteAnimationWidget(
animation: animation,
),
);
}
}

@ -1,2 +1,3 @@
export 'bonus_animation.dart';
export 'game_hud.dart';
export 'play_button_overlay.dart';

@ -10,10 +10,36 @@ import 'package:flutter/widgets.dart';
class $AssetsImagesGen {
const $AssetsImagesGen();
$AssetsImagesBonusAnimationGen get bonusAnimation =>
const $AssetsImagesBonusAnimationGen();
$AssetsImagesComponentsGen get components =>
const $AssetsImagesComponentsGen();
}
class $AssetsImagesBonusAnimationGen {
const $AssetsImagesBonusAnimationGen();
/// File path: assets/images/bonus_animation/android.png
AssetGenImage get android =>
const AssetGenImage('assets/images/bonus_animation/android.png');
/// File path: assets/images/bonus_animation/dash_nest.png
AssetGenImage get dashNest =>
const AssetGenImage('assets/images/bonus_animation/dash_nest.png');
/// File path: assets/images/bonus_animation/dino.png
AssetGenImage get dino =>
const AssetGenImage('assets/images/bonus_animation/dino.png');
/// File path: assets/images/bonus_animation/google.png
AssetGenImage get google =>
const AssetGenImage('assets/images/bonus_animation/google.png');
/// File path: assets/images/bonus_animation/sparky_turbo_charge.png
AssetGenImage get sparkyTurboCharge => const AssetGenImage(
'assets/images/bonus_animation/sparky_turbo_charge.png');
}
class $AssetsImagesComponentsGen {
const $AssetsImagesComponentsGen();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 825 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

@ -1,6 +1,6 @@
import 'package:pinball_components/gen/fonts.gen.dart';
const String _fontPath = 'packages/pinball_components/';
const String _fontPath = 'packages/pinball_components';
/// Class with the fonts available on the pinball game
class PinballFonts {

@ -34,12 +34,14 @@ class Backboard extends PositionComponent with HasGameRef {
/// Returns a [Backboard] initialized in the game over mode
factory Backboard.gameOver({
required Vector2 position,
required String characterIconPath,
required int score,
required BackboardOnSubmit onSubmit,
}) {
return Backboard(position: position)
..gameOverMode(
score: score,
characterIconPath: characterIconPath,
onSubmit: onSubmit,
);
}
@ -62,12 +64,14 @@ class Backboard extends PositionComponent with HasGameRef {
/// Puts the Backboard in game over mode, where the score input is shown.
Future<void> gameOverMode({
required int score,
required String characterIconPath,
BackboardOnSubmit? onSubmit,
}) async {
children.removeWhere((_) => true);
await add(
BackboardGameOver(
score: score,
characterIconPath: characterIconPath,
onSubmit: onSubmit,
),
);

@ -18,78 +18,41 @@ class BackboardGameOver extends PositionComponent with HasGameRef {
/// {@macro backboard_game_over}
BackboardGameOver({
required int score,
required String characterIconPath,
BackboardOnSubmit? onSubmit,
}) : _score = score,
_onSubmit = onSubmit;
}) : _onSubmit = onSubmit,
super(
children: [
_BackboardSpriteComponent(),
_BackboardDisplaySpriteComponent(),
_ScoreTextComponent(score.formatScore()),
_CharacterIconSpriteComponent(characterIconPath),
],
);
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,
await add(
BackboardLetterPrompt(
position: Vector2(
24.3 + (4.5 * i),
-45,
),
hasFocus: i == 0,
),
);
}
unawaited(
add(
KeyboardInputController(
keyUp: {
LogicalKeyboardKey.arrowLeft: () => _movePrompt(true),
LogicalKeyboardKey.arrowRight: () => _movePrompt(false),
LogicalKeyboardKey.enter: _submit,
},
),
await add(
KeyboardInputController(
keyUp: {
LogicalKeyboardKey.arrowLeft: () => _movePrompt(true),
LogicalKeyboardKey.arrowRight: () => _movePrompt(false),
LogicalKeyboardKey.enter: _submit,
},
),
);
}
@ -118,3 +81,64 @@ class BackboardGameOver extends PositionComponent with HasGameRef {
return false;
}
}
class _BackboardSpriteComponent extends SpriteComponent with HasGameRef {
_BackboardSpriteComponent() : super(anchor: Anchor.bottomCenter);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.backboard.backboardGameOver.keyName,
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}
class _BackboardDisplaySpriteComponent extends SpriteComponent with HasGameRef {
_BackboardDisplaySpriteComponent()
: super(
anchor: Anchor.bottomCenter,
position: Vector2(0, -11.5),
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.backboard.display.keyName,
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}
class _ScoreTextComponent extends TextComponent {
_ScoreTextComponent(String score)
: super(
text: score,
anchor: Anchor.centerLeft,
position: Vector2(-34, -45),
textRenderer: Backboard.textPaint,
);
}
class _CharacterIconSpriteComponent extends SpriteComponent with HasGameRef {
_CharacterIconSpriteComponent(String characterIconPath)
: _characterIconPath = characterIconPath,
super(
anchor: Anchor.center,
position: Vector2(18.4, -45),
);
final String _characterIconPath;
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = Sprite(gameRef.images.fromCache(_characterIconPath));
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}

@ -34,22 +34,19 @@ class BackboardLetterPrompt extends PositionComponent {
@override
Future<void> onLoad() async {
_underscore = RectangleComponent(
size: Vector2(
4,
1.2,
),
size: Vector2(3.8, 0.8),
anchor: Anchor.center,
position: Vector2(0, 4),
position: Vector2(-0.3, 4),
);
unawaited(add(_underscore));
await add(_underscore);
_input = TextComponent(
text: 'A',
textRenderer: Backboard.textPaint,
anchor: Anchor.center,
);
unawaited(add(_input));
await add(_input);
_underscoreBlinker = TimerComponent(
period: 0.6,
@ -62,16 +59,14 @@ class BackboardLetterPrompt extends PositionComponent {
},
);
unawaited(add(_underscoreBlinker));
await add(_underscoreBlinker);
unawaited(
add(
KeyboardInputController(
keyUp: {
LogicalKeyboardKey.arrowUp: () => _cycle(true),
LogicalKeyboardKey.arrowDown: () => _cycle(false),
},
),
await add(
KeyboardInputController(
keyUp: {
LogicalKeyboardKey.arrowUp: () => _cycle(true),
LogicalKeyboardKey.arrowDown: () => _cycle(false),
},
),
);
}

@ -22,7 +22,6 @@ class Baseboard extends BodyComponent with InitialPosition {
final BoardSide _side;
List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
final direction = _side.direction;
const arcsAngle = 1.11;
final arcsRotation = (_side.isLeft) ? -2.7 : -1.6;
@ -30,12 +29,10 @@ class Baseboard extends BodyComponent with InitialPosition {
final pegBumperShape = CircleShape()..radius = 0.7;
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);
final topCircleFixtureDef = FixtureDef(topCircleShape);
fixturesDef.add(topCircleFixtureDef);
final innerEdgeShape = EdgeShape()
..set(
@ -43,7 +40,6 @@ class Baseboard extends BodyComponent with InitialPosition {
Vector2(5.29 * direction, 0.95),
);
final innerEdgeShapeFixtureDef = FixtureDef(innerEdgeShape);
fixturesDef.add(innerEdgeShapeFixtureDef);
final outerEdgeShape = EdgeShape()
..set(
@ -51,7 +47,6 @@ class Baseboard extends BodyComponent with InitialPosition {
Vector2(3.79 * direction, 5.95),
);
final outerEdgeShapeFixtureDef = FixtureDef(outerEdgeShape);
fixturesDef.add(outerEdgeShapeFixtureDef);
final upperArcShape = ArcShape(
center: Vector2(0.09 * direction, -2.15),
@ -60,7 +55,6 @@ class Baseboard extends BodyComponent with InitialPosition {
rotation: arcsRotation,
);
final upperArcFixtureDef = FixtureDef(upperArcShape);
fixturesDef.add(upperArcFixtureDef);
final lowerArcShape = ArcShape(
center: Vector2(0.09 * direction, 3.35),
@ -69,7 +63,6 @@ class Baseboard extends BodyComponent with InitialPosition {
rotation: arcsRotation,
);
final lowerArcFixtureDef = FixtureDef(lowerArcShape);
fixturesDef.add(lowerArcFixtureDef);
final bottomRectangle = PolygonShape()
..setAsBox(
@ -79,9 +72,16 @@ class Baseboard extends BodyComponent with InitialPosition {
0,
);
final bottomRectangleFixtureDef = FixtureDef(bottomRectangle);
fixturesDef.add(bottomRectangleFixtureDef);
return fixturesDef;
return [
pegBumperFixtureDef,
topCircleFixtureDef,
innerEdgeShapeFixtureDef,
outerEdgeShapeFixtureDef,
upperArcFixtureDef,
lowerArcFixtureDef,
bottomRectangleFixtureDef,
];
}
@override
@ -100,21 +100,26 @@ class Baseboard extends BodyComponent with InitialPosition {
}
class _BaseboardSpriteComponent extends SpriteComponent with HasGameRef {
_BaseboardSpriteComponent({required BoardSide side}) : _side = side;
_BaseboardSpriteComponent({required BoardSide side})
: _side = side,
super(
anchor: Anchor.center,
position: Vector2(0.4 * -side.direction, 0),
);
final BoardSide _side;
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
(_side.isLeft)
? Assets.images.baseboard.left.keyName
: Assets.images.baseboard.right.keyName,
final sprite = Sprite(
gameRef.images.fromCache(
(_side.isLeft)
? Assets.images.baseboard.left.keyName
: Assets.images.baseboard.right.keyName,
),
);
this.sprite = sprite;
size = sprite.originalSize / 10;
position = Vector2(0.4 * -_side.direction, 0);
anchor = Anchor.center;
}
}

@ -1,5 +1,3 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
@ -8,14 +6,15 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template boundaries}
/// A [Blueprint] which creates the [_BottomBoundary] and [_OuterBoundary].
///{@endtemplate boundaries}
class Boundaries extends Forge2DBlueprint {
@override
void build(_) {
final bottomBoundary = _BottomBoundary();
final outerBoundary = _OuterBoundary();
addAll([outerBoundary, bottomBoundary]);
}
class Boundaries extends Blueprint {
/// {@macro boundaries}
Boundaries()
: super(
components: [
_BottomBoundary(),
_OuterBoundary(),
],
);
}
/// {@template bottom_boundary}
@ -33,8 +32,6 @@ class _BottomBoundary extends BodyComponent with InitialPosition {
}
List<FixtureDef> _createFixtureDefs() {
final fixturesDefs = <FixtureDef>[];
final bottomLeftCurve = BezierCurveShape(
controlPoints: [
Vector2(-43.9, 41.8),
@ -43,7 +40,6 @@ class _BottomBoundary extends BodyComponent with InitialPosition {
],
);
final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurve);
fixturesDefs.add(bottomLeftCurveFixtureDef);
final bottomRightCurve = BezierCurveShape(
controlPoints: [
@ -53,9 +49,8 @@ class _BottomBoundary extends BodyComponent with InitialPosition {
],
);
final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurve);
fixturesDefs.add(bottomRightCurveFixtureDef);
return fixturesDefs;
return [bottomLeftCurveFixtureDef, bottomRightCurveFixtureDef];
}
@override
@ -69,16 +64,22 @@ class _BottomBoundary extends BodyComponent with InitialPosition {
}
class _BottomBoundarySpriteComponent extends SpriteComponent with HasGameRef {
_BottomBoundarySpriteComponent()
: super(
anchor: Anchor.center,
position: Vector2(-5.4, 55.6),
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.boundary.bottom.keyName,
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.boundary.bottom.keyName,
),
);
this.sprite = sprite;
size = sprite.originalSize / 10;
anchor = Anchor.center;
position = Vector2(-5.4, 55.6);
}
}
@ -97,15 +98,12 @@ class _OuterBoundary extends BodyComponent with InitialPosition {
}
List<FixtureDef> _createFixtureDefs() {
final fixturesDefs = <FixtureDef>[];
final topWall = EdgeShape()
..set(
Vector2(3.6, -70.2),
Vector2(-14.1, -70.2),
);
final topWallFixtureDef = FixtureDef(topWall);
fixturesDefs.add(topWallFixtureDef);
final topLeftCurve = BezierCurveShape(
controlPoints: [
@ -115,7 +113,6 @@ class _OuterBoundary extends BodyComponent with InitialPosition {
],
);
final topLeftCurveFixtureDef = FixtureDef(topLeftCurve);
fixturesDefs.add(topLeftCurveFixtureDef);
final leftWall = EdgeShape()
..set(
@ -123,9 +120,12 @@ class _OuterBoundary extends BodyComponent with InitialPosition {
Vector2(-43.9, 41.8),
);
final leftWallFixtureDef = FixtureDef(leftWall);
fixturesDefs.add(leftWallFixtureDef);
return fixturesDefs;
return [
topWallFixtureDef,
topLeftCurveFixtureDef,
leftWallFixtureDef,
];
}
@override
@ -139,15 +139,21 @@ class _OuterBoundary extends BodyComponent with InitialPosition {
}
class _OuterBoundarySpriteComponent extends SpriteComponent with HasGameRef {
_OuterBoundarySpriteComponent()
: super(
anchor: Anchor.center,
position: Vector2(0, -7.8),
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.boundary.outer.keyName,
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.boundary.outer.keyName,
),
);
this.sprite = sprite;
size = sprite.originalSize / 10;
anchor = Anchor.center;
position = Vector2(-0.2, -1.4);
}
}

@ -17,12 +17,12 @@ class DashAnimatronic extends SpriteAnimationComponent with HasGameRef {
Future<void> onLoad() async {
await super.onLoad();
final spriteSheet = await gameRef.images.load(
final spriteSheet = gameRef.images.fromCache(
Assets.images.dash.animatronic.keyName,
);
const amountPerRow = 12;
const amountPerColumn = 8;
const amountPerRow = 13;
const amountPerColumn = 6;
final textureSize = Vector2(
spriteSheet.width / amountPerRow,
spriteSheet.height / amountPerColumn,

@ -1,5 +1,3 @@
// ignore_for_file: comment_references, avoid_renaming_method_parameters
import 'dart:async';
import 'package:flame/components.dart';
@ -11,17 +9,15 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template dinowalls}
/// A [Blueprint] which creates walls for the [ChromeDino].
/// {@endtemplate}
class DinoWalls extends Forge2DBlueprint {
class DinoWalls extends Blueprint {
/// {@macro dinowalls}
DinoWalls();
@override
void build(_) {
addAll([
_DinoTopWall(),
_DinoBottomWall(),
]);
}
DinoWalls()
: super(
components: [
_DinoTopWall(),
_DinoBottomWall(),
],
);
}
/// {@template dino_top_wall}

@ -60,7 +60,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
}
List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
final direction = side.direction;
final assetShadow = Flipper.size.x * 0.012 * -direction;
@ -77,7 +76,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
0,
);
final bigCircleFixtureDef = FixtureDef(bigCircleShape);
fixturesDef.add(bigCircleFixtureDef);
final smallCircleShape = CircleShape()..radius = size.y * 0.23;
smallCircleShape.position.setValues(
@ -87,7 +85,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
0,
);
final smallCircleFixtureDef = FixtureDef(smallCircleShape);
fixturesDef.add(smallCircleFixtureDef);
final trapeziumVertices = side.isLeft
? [
@ -108,9 +105,12 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
density: 50, // TODO(alestiago): Use a proper density.
friction: .1, // TODO(alestiago): Use a proper friction.
);
fixturesDef.add(trapeziumFixtureDef);
return fixturesDef;
return [
bigCircleFixtureDef,
smallCircleFixtureDef,
trapeziumFixtureDef,
];
}
@override
@ -136,21 +136,24 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
}
class _FlipperSpriteComponent extends SpriteComponent with HasGameRef {
_FlipperSpriteComponent({required BoardSide side}) : _side = side;
_FlipperSpriteComponent({required BoardSide side})
: _side = side,
super(anchor: Anchor.center);
final BoardSide _side;
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
(_side.isLeft)
? Assets.images.flipper.left.keyName
: Assets.images.flipper.right.keyName,
final sprite = Sprite(
gameRef.images.fromCache(
(_side.isLeft)
? Assets.images.flipper.left.keyName
: Assets.images.flipper.right.keyName,
),
);
this.sprite = sprite;
size = sprite.originalSize / 10;
anchor = Anchor.center;
}
}

@ -11,36 +11,23 @@ import 'package:pinball_flame/pinball_flame.dart';
/// A [Blueprint] which creates the [_LaunchRampBase] and
/// [_LaunchRampForegroundRailing].
/// {@endtemplate}
class LaunchRamp extends Forge2DBlueprint {
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_LaunchRampExit>(),
]);
final launchRampBase = _LaunchRampBase();
final launchRampForegroundRailing = _LaunchRampForegroundRailing();
final launchRampExit = _LaunchRampExit(rotation: math.pi / 2)
..initialPosition = Vector2(0.6, -34);
final launchRampCloseWall = _LaunchRampCloseWall()
..initialPosition = Vector2(4, -69.5);
addAll([
launchRampBase,
launchRampForegroundRailing,
launchRampExit,
launchRampCloseWall,
]);
}
class LaunchRamp extends Blueprint {
/// {@macro launch_ramp}
LaunchRamp()
: super(
components: [
_LaunchRampBase(),
_LaunchRampForegroundRailing(),
_LaunchRampExit()..initialPosition = Vector2(0.6, -34),
_LaunchRampCloseWall()..initialPosition = Vector2(4, -69.5),
],
);
}
/// {@template launch_ramp_base}
/// Ramp the [Ball] is launched from at the beginning of each ball life.
/// {@endtemplate}
class _LaunchRampBase extends BodyComponent with InitialPosition, Layered {
class _LaunchRampBase extends BodyComponent with Layered {
/// {@macro launch_ramp_base}
_LaunchRampBase()
: super(
@ -115,16 +102,18 @@ class _LaunchRampBase extends BodyComponent with InitialPosition, Layered {
@override
Body createBody() {
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final body = world.createBody(bodyDef);
final body = world.createBody(BodyDef());
_createFixtureDefs().forEach(body.createFixture);
return body;
}
@override
Future<void> onLoad() async {
await super.onLoad();
gameRef
.addContactCallback(LayerSensorBallContactCallback<_LaunchRampExit>());
}
}
class _LaunchRampBaseSpriteComponent extends SpriteComponent with HasGameRef {
@ -162,7 +151,7 @@ class _LaunchRampBackgroundRailingSpriteComponent extends SpriteComponent
/// Foreground railing for the [_LaunchRampBase] to render in front of the
/// [Ball].
/// {@endtemplate}
class _LaunchRampForegroundRailing extends BodyComponent with InitialPosition {
class _LaunchRampForegroundRailing extends BodyComponent {
/// {@macro launch_ramp_foreground_railing}
_LaunchRampForegroundRailing()
: super(
@ -205,11 +194,7 @@ class _LaunchRampForegroundRailing extends BodyComponent with InitialPosition {
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition;
final body = world.createBody(bodyDef);
final body = world.createBody(BodyDef());
_createFixtureDefs().forEach(body.createFixture);
return body;
@ -258,10 +243,8 @@ class _LaunchRampCloseWall extends BodyComponent with InitialPosition, Layered {
/// {@endtemplate}
class _LaunchRampExit extends LayerSensor {
/// {@macro launch_ramp_exit}
_LaunchRampExit({
required double rotation,
}) : _rotation = rotation,
super(
_LaunchRampExit()
: super(
insideLayer: Layer.launcher,
outsideLayer: Layer.board,
orientation: LayerEntranceOrientation.down,
@ -272,8 +255,6 @@ class _LaunchRampExit extends LayerSensor {
renderBody = false;
}
final double _rotation;
static final Vector2 _size = Vector2(1.6, 0.1);
@override
@ -282,6 +263,6 @@ class _LaunchRampExit extends LayerSensor {
_size.x,
_size.y,
initialPosition,
_rotation,
math.pi / 2,
);
}

@ -49,7 +49,7 @@ abstract class RenderPriority {
static const int launchRamp = _above + outerBoudary;
static const int launchRampForegroundRailing = _above + ballOnLaunchRamp;
static const int launchRampForegroundRailing = _below + ballOnBoard;
static const int plunger = _above + launchRamp;
@ -63,7 +63,7 @@ abstract class RenderPriority {
static const int dinoBottomWall = _above + dino;
static const int slingshot = _above + ballOnBoard;
static const int slingshot = _above + dinoBottomWall;
// Flutter Forest
@ -71,7 +71,7 @@ abstract class RenderPriority {
static const int dashBumper = _above + ballOnBoard;
static const int dashAnimatronic = _above + launchRampForegroundRailing;
static const int dashAnimatronic = 2 * _above + launchRamp;
// Sparky Fire Zone

@ -1,5 +1,3 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
@ -9,26 +7,23 @@ import 'package:pinball_flame/pinball_flame.dart';
/// A [Blueprint] which creates the pair of [Slingshot]s on the right side of
/// the board.
/// {@endtemplate}
class Slingshots extends Forge2DBlueprint {
@override
void build(_) {
final upperSlingshot = Slingshot(
length: 5.64,
angle: -0.017,
spritePath: Assets.images.slingshot.upper.keyName,
)..initialPosition = Vector2(22.3, -1.58);
final lowerSlingshot = Slingshot(
length: 3.46,
angle: -0.468,
spritePath: Assets.images.slingshot.lower.keyName,
)..initialPosition = Vector2(24.7, 6.2);
addAll([
upperSlingshot,
lowerSlingshot,
]);
}
class Slingshots extends Blueprint {
/// {@macro slingshots}
Slingshots()
: super(
components: [
Slingshot(
length: 5.64,
angle: -0.017,
spritePath: Assets.images.slingshot.upper.keyName,
)..initialPosition = Vector2(22.3, -1.58),
Slingshot(
length: 3.46,
angle: -0.468,
spritePath: Assets.images.slingshot.lower.keyName,
)..initialPosition = Vector2(24.7, 6.2),
],
);
}
/// {@template slingshot}
@ -54,18 +49,15 @@ class Slingshot extends BodyComponent with InitialPosition {
final double _angle;
List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
const circleRadius = 1.55;
final topCircleShape = CircleShape()..radius = circleRadius;
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);
fixturesDef.add(bottomCircleFixtureDef);
final leftEdgeShape = EdgeShape()
..set(
@ -77,8 +69,6 @@ class Slingshot extends BodyComponent with InitialPosition {
restitution: 5,
);
fixturesDef.add(leftEdgeShapeFixtureDef);
final rightEdgeShape = EdgeShape()
..set(
Vector2(-circleRadius, _length / 2),
@ -88,9 +78,13 @@ class Slingshot extends BodyComponent with InitialPosition {
rightEdgeShape,
restitution: 5,
);
fixturesDef.add(rightEdgeShapeFixtureDef);
return fixturesDef;
return [
topCircleFixtureDef,
bottomCircleFixtureDef,
leftEdgeShapeFixtureDef,
rightEdgeShapeFixtureDef,
];
}
@override
@ -123,7 +117,7 @@ class _SlinghsotSpriteComponent extends SpriteComponent with HasGameRef {
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(_path);
final sprite = Sprite(gameRef.images.fromCache(_path));
this.sprite = sprite;
size = sprite.originalSize / 10;
}

@ -1,5 +1,3 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:async';
import 'dart:math';
@ -12,38 +10,28 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship}
/// A [Blueprint] which creates the spaceship feature.
/// {@endtemplate}
class Spaceship extends Forge2DBlueprint {
class Spaceship extends Blueprint {
/// {@macro spaceship}
Spaceship({required this.position});
Spaceship({required Vector2 position})
: super(
components: [
SpaceshipSaucer()..initialPosition = position,
_SpaceshipEntrance()..initialPosition = position,
AndroidHead()..initialPosition = position,
_SpaceshipHole(
outsideLayer: Layer.spaceshipExitRail,
outsidePriority: RenderPriority.ballOnSpaceshipRail,
)..initialPosition = position - Vector2(5.2, -4.8),
_SpaceshipHole(
outsideLayer: Layer.board,
outsidePriority: RenderPriority.ballOnBoard,
)..initialPosition = position - Vector2(-7.2, -0.8),
SpaceshipWall()..initialPosition = position,
],
);
/// Total size of the spaceship.
static final size = Vector2(25, 19);
/// The [position] where the elements will be created
final Vector2 position;
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_SpaceshipEntrance>(),
LayerSensorBallContactCallback<_SpaceshipHole>(),
]);
addAll([
SpaceshipSaucer()..initialPosition = position,
_SpaceshipEntrance()..initialPosition = position,
AndroidHead()..initialPosition = position,
_SpaceshipHole(
outsideLayer: Layer.spaceshipExitRail,
outsidePriority: RenderPriority.ballOnSpaceshipRail,
)..initialPosition = position - Vector2(5.2, -4.8),
_SpaceshipHole(
outsideLayer: Layer.board,
outsidePriority: RenderPriority.ballOnBoard,
)..initialPosition = position - Vector2(-7.2, -0.8),
SpaceshipWall()..initialPosition = position,
]);
}
}
/// {@template spaceship_saucer}
@ -51,26 +39,28 @@ class Spaceship extends Forge2DBlueprint {
/// {@endtemplate}
class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_saucer}
SpaceshipSaucer() : super(priority: RenderPriority.spaceshipSaucer) {
SpaceshipSaucer()
: super(
priority: RenderPriority.spaceshipSaucer,
children: [
_SpaceshipSaucerSpriteComponent(),
],
) {
layer = Layer.spaceship;
renderBody = false;
}
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.spaceship.saucer.keyName,
);
await add(
SpriteComponent(
sprite: sprite,
size: Spaceship.size,
anchor: Anchor.center,
),
);
renderBody = false;
gameRef
..addContactCallback(
LayerSensorBallContactCallback<_SpaceshipEntrance>(),
)
..addContactCallback(
LayerSensorBallContactCallback<_SpaceshipHole>(),
);
}
@override
@ -89,6 +79,25 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
}
}
class _SpaceshipSaucerSpriteComponent extends SpriteComponent with HasGameRef {
_SpaceshipSaucerSpriteComponent()
: super(
anchor: Anchor.center,
// TODO(alestiago): Refactor to use sprite orignial size instead.
size: Spaceship.size,
);
@override
Future<void> onLoad() async {
await super.onLoad();
// TODO(alestiago): Use cached sprite.
sprite = await gameRef.loadSprite(
Assets.images.spaceship.saucer.keyName,
);
}
}
/// {@template spaceship_bridge}
/// A [BodyComponent] that provides both the collision and the rotation
/// animation for the bridge.

@ -1,5 +1,3 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:math' as math;
import 'package:flame/components.dart';
@ -11,38 +9,30 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_rail}
/// A [Blueprint] for the spaceship drop tube.
/// {@endtemplate}
class SpaceshipRail extends Forge2DBlueprint {
class SpaceshipRail extends Blueprint {
/// {@macro spaceship_rail}
SpaceshipRail();
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_SpaceshipRailExit>(),
]);
final railRamp = _SpaceshipRailRamp();
final railEnd = _SpaceshipRailExit();
final topBase = _SpaceshipRailBase(radius: 0.55)
..initialPosition = Vector2(-26.15, -18.65);
final bottomBase = _SpaceshipRailBase(radius: 0.8)
..initialPosition = Vector2(-25.5, 12.9);
final railForeground = _SpaceshipRailForeground();
addAll([
railRamp,
railEnd,
topBase,
bottomBase,
railForeground,
]);
}
SpaceshipRail()
: super(
components: [
_SpaceshipRailRamp(),
_SpaceshipRailExit(),
_SpaceshipRailBase(radius: 0.55)
..initialPosition = Vector2(-26.15, -18.65),
_SpaceshipRailBase(radius: 0.8)
..initialPosition = Vector2(-25.5, 12.9),
_SpaceshipRailForeground()
],
);
}
/// Represents the spaceship drop rail from the [Spaceship].
class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
_SpaceshipRailRamp() : super(priority: RenderPriority.spaceshipRail) {
class _SpaceshipRailRamp extends BodyComponent with Layered {
_SpaceshipRailRamp()
: super(
priority: RenderPriority.spaceshipRail,
children: [_SpaceshipRailRampSpriteComponent()],
) {
layer = Layer.spaceshipExitRail;
renderBody = false;
}
List<FixtureDef> _createFixtureDefs() {
@ -120,23 +110,17 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
@override
Body createBody() {
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final body = world.createBody(bodyDef);
final body = world.createBody(BodyDef());
_createFixtureDefs().forEach(body.createFixture);
return body;
}
@override
Future<void> onLoad() async {
await super.onLoad();
renderBody = false;
await add(_SpaceshipRailRampSpriteComponent());
gameRef.addContactCallback(
LayerSensorBallContactCallback<_SpaceshipRailExit>(),
);
}
}
@ -188,7 +172,6 @@ class _SpaceshipRailBase extends BodyComponent with InitialPosition {
final fixtureDef = FixtureDef(shape);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);

@ -1,5 +1,3 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:math' as math;
import 'package:flame/components.dart';
@ -12,62 +10,41 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_ramp}
/// A [Blueprint] which creates the ramp leading into the [Spaceship].
/// {@endtemplate}
class SpaceshipRamp extends Forge2DBlueprint {
class SpaceshipRamp extends Blueprint {
/// {@macro spaceship_ramp}
SpaceshipRamp();
/// [SpriteGroupComponent] representing the arrow that lights up.
@visibleForTesting
late final SpaceshipRampArrowSpriteComponent spaceshipRampArrow;
SpaceshipRamp()
: super(
components: [
_SpaceshipRampOpening(
outsidePriority: RenderPriority.ballOnBoard,
rotation: math.pi,
)
..initialPosition = Vector2(1.7, -19.8)
..layer = Layer.opening,
_SpaceshipRampOpening(
outsideLayer: Layer.spaceship,
outsidePriority: RenderPriority.ballOnSpaceship,
rotation: math.pi,
)
..initialPosition = Vector2(-13.7, -18.6)
..layer = Layer.spaceshipEntranceRamp,
_SpaceshipRampBackground(),
_SpaceshipRampBoardOpeningSpriteComponent()
..position = Vector2(3.4, -39.5),
_SpaceshipRampForegroundRailing(),
_SpaceshipRampBase()..initialPosition = Vector2(1.7, -20),
_SpaceshipRampBackgroundRailingSpriteComponent(),
_SpaceshipRampArrowSpriteComponent(),
],
);
/// Forwards the sprite to the next [SpaceshipRampArrowSpriteState].
///
/// If the current state is the last one it cycles back to the initial state.
void progress() => spaceshipRampArrow.progress();
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_SpaceshipRampOpening>(),
]);
final rightOpening = _SpaceshipRampOpening(
outsidePriority: RenderPriority.ballOnBoard,
rotation: -5 * math.pi / 180,
)
..initialPosition = Vector2(1.7, -19.12)
..layer = Layer.opening;
final leftOpening = _SpaceshipRampOpening(
outsideLayer: Layer.spaceship,
outsidePriority: RenderPriority.ballOnSpaceship,
rotation: -5 * math.pi / 180,
)
..initialPosition = Vector2(-13.7, -19)
..layer = Layer.spaceshipEntranceRamp;
final spaceshipRamp = _SpaceshipRampBackground();
spaceshipRampArrow = SpaceshipRampArrowSpriteComponent();
final spaceshipRampBoardOpeningSprite =
_SpaceshipRampBoardOpeningSpriteComponent()
..position = Vector2(3.4, -39.5);
final spaceshipRampForegroundRailing = _SpaceshipRampForegroundRailing();
final baseRight = _SpaceshipRampBase()..initialPosition = Vector2(1.7, -20);
addAll([
spaceshipRampBoardOpeningSprite,
rightOpening,
leftOpening,
baseRight,
_SpaceshipRampBackgroundRailingSpriteComponent(),
spaceshipRamp,
spaceshipRampArrow,
spaceshipRampForegroundRailing,
]);
}
void progress() => components
.whereType<_SpaceshipRampArrowSpriteComponent>()
.first
.progress();
}
/// Indicates the state of the arrow on the [SpaceshipRamp].
@ -133,8 +110,6 @@ class _SpaceshipRampBackground extends BodyComponent
static const width = 5.0;
List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
final outerLeftCurveShape = BezierCurveShape(
controlPoints: [
Vector2(-30.75, -37.3),
@ -142,9 +117,6 @@ class _SpaceshipRampBackground extends BodyComponent
Vector2(-14.2, -71.25),
],
);
final outerLeftCurveFixtureDef = FixtureDef(outerLeftCurveShape);
fixturesDef.add(outerLeftCurveFixtureDef);
final outerRightCurveShape = BezierCurveShape(
controlPoints: [
outerLeftCurveShape.vertices.last,
@ -152,32 +124,35 @@ class _SpaceshipRampBackground extends BodyComponent
Vector2(6.1, -44.9),
],
);
final outerRightCurveFixtureDef = FixtureDef(outerRightCurveShape);
fixturesDef.add(outerRightCurveFixtureDef);
final boardOpeningEdgeShape = EdgeShape()
..set(
outerRightCurveShape.vertices.last,
Vector2(7.3, -41.1),
);
final boardOpeningEdgeShapeFixtureDef = FixtureDef(boardOpeningEdgeShape);
fixturesDef.add(boardOpeningEdgeShapeFixtureDef);
return fixturesDef;
return [
FixtureDef(outerRightCurveShape),
FixtureDef(outerLeftCurveShape),
FixtureDef(boardOpeningEdgeShape),
];
}
@override
Body createBody() {
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final bodyDef = BodyDef(position: initialPosition);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
return body;
}
@override
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(
LayerSensorBallContactCallback<_SpaceshipRampOpening>(),
);
}
}
class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent
@ -188,6 +163,7 @@ class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent
position: Vector2(-11.7, -54.3),
priority: RenderPriority.spaceshipRampBackgroundRailing,
);
@override
Future<void> onLoad() async {
await super.onLoad();
@ -203,6 +179,12 @@ class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent
class _SpaceshipRampBackgroundRampSpriteComponent extends SpriteComponent
with HasGameRef {
_SpaceshipRampBackgroundRampSpriteComponent()
: super(
anchor: Anchor.center,
position: Vector2(-10.7, -53.6),
);
@override
Future<void> onLoad() async {
await super.onLoad();
@ -213,22 +195,19 @@ class _SpaceshipRampBackgroundRampSpriteComponent extends SpriteComponent
);
this.sprite = sprite;
size = sprite.originalSize / 10;
anchor = Anchor.center;
position = Vector2(-10.7, -53.6);
}
}
/// {@template spaceship_ramp_arrow_sprite_component}
/// An arrow inside [SpaceshipRamp].
///
/// Lights up a each dash whenever a [Ball] gets into [SpaceshipRamp].
/// Lights progressively whenever a [Ball] gets into [SpaceshipRamp].
/// {@endtemplate}
@visibleForTesting
class SpaceshipRampArrowSpriteComponent
class _SpaceshipRampArrowSpriteComponent
extends SpriteGroupComponent<SpaceshipRampArrowSpriteState>
with HasGameRef {
/// {@macro spaceship_ramp_arrow_sprite_component}
SpaceshipRampArrowSpriteComponent()
_SpaceshipRampArrowSpriteComponent()
: super(
anchor: Anchor.center,
position: Vector2(-3.9, -56.5),
@ -256,6 +235,8 @@ class SpaceshipRampArrowSpriteComponent
class _SpaceshipRampBoardOpeningSpriteComponent extends SpriteComponent
with HasGameRef {
_SpaceshipRampBoardOpeningSpriteComponent() : super(anchor: Anchor.center);
@override
Future<void> onLoad() async {
await super.onLoad();
@ -266,7 +247,6 @@ class _SpaceshipRampBoardOpeningSpriteComponent extends SpriteComponent
);
this.sprite = sprite;
size = sprite.originalSize / 10;
anchor = Anchor.center;
}
}
@ -282,8 +262,6 @@ class _SpaceshipRampForegroundRailing extends BodyComponent
}
List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
final innerLeftCurveShape = BezierCurveShape(
controlPoints: [
Vector2(-24.5, -38),
@ -291,10 +269,6 @@ class _SpaceshipRampForegroundRailing extends BodyComponent
Vector2(-13.8, -64.5),
],
);
final innerLeftCurveFixtureDef = FixtureDef(innerLeftCurveShape);
fixturesDef.add(innerLeftCurveFixtureDef);
final innerRightCurveShape = BezierCurveShape(
controlPoints: [
innerLeftCurveShape.vertices.last,
@ -302,28 +276,22 @@ class _SpaceshipRampForegroundRailing extends BodyComponent
Vector2(0, -44.5),
],
);
final innerRightCurveFixtureDef = FixtureDef(innerRightCurveShape);
fixturesDef.add(innerRightCurveFixtureDef);
final boardOpeningEdgeShape = EdgeShape()
..set(
innerRightCurveShape.vertices.last,
Vector2(-0.85, -40.8),
);
final boardOpeningEdgeShapeFixtureDef = FixtureDef(boardOpeningEdgeShape);
fixturesDef.add(boardOpeningEdgeShapeFixtureDef);
return fixturesDef;
return [
FixtureDef(innerLeftCurveShape),
FixtureDef(innerRightCurveShape),
FixtureDef(boardOpeningEdgeShape),
];
}
@override
Body createBody() {
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final bodyDef = BodyDef(position: initialPosition);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
@ -372,10 +340,7 @@ class _SpaceshipRampBase extends BodyComponent with InitialPosition, Layered {
],
);
final fixtureDef = FixtureDef(baseShape);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final bodyDef = BodyDef(position: initialPosition);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}

@ -6,75 +6,64 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_computer}
/// A [Blueprint] which creates the [_ComputerBase] and
/// [_ComputerTopSpriteComponent].
/// A computer owned by Sparky.
///
/// Register a [ContactCallback] for [SparkyComputerSensor] to listen when
/// something enters the [SparkyComputer].
/// {@endtemplate}
class SparkyComputer extends Forge2DBlueprint {
@override
void build(_) {
final computerBase = _ComputerBase();
final computerTop = _ComputerTopSpriteComponent();
addAll([
computerBase,
computerTop,
]);
}
class SparkyComputer extends Blueprint {
/// {@macro sparky_computer}
SparkyComputer()
: super(
components: [
_ComputerBase(),
_ComputerTopSpriteComponent(),
SparkyComputerSensor(),
],
);
}
class _ComputerBase extends BodyComponent with InitialPosition {
_ComputerBase() : super(priority: RenderPriority.computerBase);
_ComputerBase()
: super(
priority: RenderPriority.computerBase,
children: [_ComputerBaseSpriteComponent()],
) {
renderBody = false;
}
List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
final leftEdge = EdgeShape()
..set(
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),
);
final topEdgeFixtureDef = FixtureDef(topEdge);
fixturesDef.add(topEdgeFixtureDef);
final rightEdge = EdgeShape()
..set(
Vector2(-10.7, -50.6),
Vector2(-9, -47.2),
);
final rightEdgeFixtureDef = FixtureDef(rightEdge);
fixturesDef.add(rightEdgeFixtureDef);
return fixturesDef;
return [
FixtureDef(leftEdge),
FixtureDef(topEdge),
FixtureDef(rightEdge),
];
}
@override
Body createBody() {
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
final bodyDef = BodyDef(position: initialPosition);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
return body;
}
@override
Future<void> onLoad() async {
await super.onLoad();
renderBody = false;
await add(_ComputerBaseSpriteComponent());
}
}
class _ComputerBaseSpriteComponent extends SpriteComponent with HasGameRef {
@ -115,3 +104,24 @@ class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef {
size = sprite.originalSize / 10;
}
}
/// {@template sparky_computer_sensor}
/// Small sensor body used to detect when a ball has entered the
/// [SparkyComputer].
/// {@endtemplate}
class SparkyComputerSensor extends BodyComponent with InitialPosition {
/// {@macro sparky_computer_sensor}
SparkyComputerSensor() {
renderBody = false;
}
@override
Body createBody() {
final shape = CircleShape()..radius = 0.1;
final fixtureDef = FixtureDef(shape, isSensor: true);
final bodyDef = BodyDef()
..position = initialPosition
..userData = this;
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}

@ -16,6 +16,8 @@ dependencies:
intl: ^0.17.0
pinball_flame:
path: ../pinball_flame
pinball_theme:
path: ../pinball_theme
dev_dependencies:

@ -0,0 +1,51 @@
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_theme/pinball_theme.dart';
import 'package:sandbox/common/common.dart';
class BackboardGameOverGame extends BasicKeyboardGame {
BackboardGameOverGame(this.score, this.character);
static const info = '''
Simple example showing the game over mode of the backboard.
- Select a character to update the character icon.
''';
final int score;
final String character;
static final characterIconPaths = <String, String>{
'Dash': Assets.images.dash.leaderboardIcon.keyName,
'Sparky': Assets.images.sparky.leaderboardIcon.keyName,
'Android': Assets.images.android.leaderboardIcon.keyName,
'Dino': Assets.images.dino.leaderboardIcon.keyName,
};
@override
Future<void> onLoad() async {
camera
..followVector2(Vector2.zero())
..zoom = 5;
await images.loadAll(characterIconPaths.values.toList());
await add(
Backboard.gameOver(
position: Vector2(0, 20),
score: score,
characterIconPath: characterIconPaths[character]!,
onSubmit: (initials) {
add(
ScoreText(
text: 'User $initials made $score',
position: Vector2(0, 50),
color: Colors.pink,
),
);
},
),
);
}
}

@ -1,37 +0,0 @@
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,
),
);
},
),
);
}
}

@ -1,8 +1,8 @@
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';
import 'package:sandbox/stories/backboard/backboard_game_over_game.dart';
import 'package:sandbox/stories/backboard/backboard_waiting_game.dart';
void addBackboardStories(Dashbook dashbook) {
dashbook.storiesOf('Backboard')
@ -18,7 +18,12 @@ void addBackboardStories(Dashbook dashbook) {
'Game over',
(context) => GameWidget(
game: BackboardGameOverGame(
context.numberProperty('score', 9000000000).toInt(),
context.numberProperty('Score', 9000000000).toInt(),
context.listProperty(
'Character',
BackboardGameOverGame.characterIconPaths.keys.first,
BackboardGameOverGame.characterIconPaths.keys.toList(),
),
),
),
codeLink: buildSourceLink('backboard/game_over.dart'),

@ -0,0 +1,38 @@
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class BaseboardGame extends BasicBallGame with Traceable {
static const info = '''
Shows how the Baseboards are rendered.
- Activate the "trace" parameter to overlay the body.
- Tap anywhere on the screen to spawn a ball into the game.
''';
@override
Future<void> onLoad() async {
await super.onLoad();
await images.loadAll([
Assets.images.baseboard.left.keyName,
Assets.images.baseboard.right.keyName,
]);
final center = screenToWorld(camera.viewport.canvasSize! / 2);
final leftBaseboard = Baseboard(side: BoardSide.left)
..initialPosition = center - Vector2(25, 0)
..priority = 1;
final rightBaseboard = Baseboard(side: BoardSide.right)
..initialPosition = center + Vector2(25, 0)
..priority = 1;
await addAll([
leftBaseboard,
rightBaseboard,
]);
await traceAllBodies();
}
}

@ -1,24 +0,0 @@
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart';
class BasicBaseboardGame extends BasicGame {
static const info = 'Shows how a Baseboard works.';
@override
Future<void> onLoad() async {
await super.onLoad();
final center = screenToWorld(camera.viewport.canvasSize! / 2);
final leftBaseboard = Baseboard(side: BoardSide.left)
..initialPosition = center - Vector2(25, 0);
final rightBaseboard = Baseboard(side: BoardSide.right)
..initialPosition = center + Vector2(25, 0);
await addAll([
leftBaseboard,
rightBaseboard,
]);
}
}

@ -1,15 +1,15 @@
import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/baseboard/basic_baseboard_game.dart';
import 'package:sandbox/stories/baseboard/baseboard_game.dart';
void addBaseboardStories(Dashbook dashbook) {
dashbook.storiesOf('Baseboard').add(
'Basic',
(context) => GameWidget(
game: BasicBaseboardGame(),
game: BaseboardGame()..trace = context.boolProperty('Trace', true),
),
codeLink: buildSourceLink('baseboard/basic.dart'),
info: BasicBaseboardGame.info,
codeLink: buildSourceLink('baseboard_game/basic.dart'),
info: BaseboardGame.info,
);
}

@ -16,6 +16,11 @@ class BoundariesGame extends BasicBallGame with Traceable {
Future<void> onLoad() async {
await super.onLoad();
await images.loadAll([
Assets.images.boundary.outer.keyName,
Assets.images.boundary.bottom.keyName,
]);
await addFromBlueprint(Boundaries());
await ready();

@ -33,6 +33,11 @@ class FlipperGame extends BasicBallGame with KeyboardEvents, Traceable {
Future<void> onLoad() async {
await super.onLoad();
await images.loadAll([
Assets.images.flipper.left.keyName,
Assets.images.flipper.right.keyName,
]);
final center = screenToWorld(camera.viewport.canvasSize! / 2);
leftFlipper = Flipper(side: BoardSide.left)

@ -15,6 +15,12 @@ class SlingshotGame extends BasicBallGame with Traceable {
@override
Future<void> onLoad() async {
await super.onLoad();
await images.loadAll([
Assets.images.slingshot.upper.keyName,
Assets.images.slingshot.lower.keyName,
]);
await addFromBlueprint(Slingshots());
camera.followVector2(Vector2.zero());
await traceAllBodies();

@ -64,6 +64,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
equatable:
dependency: transitive
description:
name: equatable
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
fake_async:
dependency: transitive
description:
@ -247,6 +254,13 @@ packages:
relative: true
source: path
version: "1.0.0+1"
pinball_theme:
dependency: "direct main"
description:
path: "../../pinball_theme"
relative: true
source: path
version: "1.0.0+1"
platform:
dependency: transitive
description:

@ -16,6 +16,8 @@ dependencies:
path: ../
pinball_flame:
path: ../../pinball_flame
pinball_theme:
path: ../../pinball_theme
dev_dependencies:
flutter_test:

@ -17,4 +17,6 @@ class TestGame extends Forge2DGame {
}
}
class KeyboardTestGame extends TestGame with HasKeyboardHandlerComponents {}
class KeyboardTestGame extends TestGame with HasKeyboardHandlerComponents {
KeyboardTestGame([List<String>? assets]) : super(assets);
}

@ -5,13 +5,15 @@ 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';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_theme/pinball_theme.dart';
import '../../helpers/helpers.dart';
void main() {
group('Backboard', () {
final tester = FlameTester(KeyboardTestGame.new);
final characterIconPath = Assets.images.dash.leaderboardIcon.keyName;
final tester = FlameTester(() => KeyboardTestGame([characterIconPath]));
group('on waitingMode', () {
tester.testGameWidget(
@ -20,6 +22,7 @@ void main() {
game.camera.zoom = 2;
game.camera.followVector2(Vector2.zero());
await game.ensureAdd(Backboard.waiting(position: Vector2(0, 15)));
await tester.pump();
},
verify: (game, tester) async {
await expectLater(
@ -39,6 +42,7 @@ void main() {
final backboard = Backboard.gameOver(
position: Vector2(0, 15),
score: 1000,
characterIconPath: characterIconPath,
onSubmit: (_) {},
);
await game.ensureAdd(backboard);
@ -52,7 +56,6 @@ void main() {
(component) =>
component is TextComponent && component.text == '1,000',
);
expect(score, isNotNull);
},
);
@ -63,6 +66,7 @@ void main() {
final backboard = Backboard.gameOver(
position: Vector2(0, 15),
score: 1000,
characterIconPath: characterIconPath,
onSubmit: (_) {},
);
await game.ensureAdd(backboard);
@ -106,6 +110,7 @@ void main() {
final backboard = Backboard.gameOver(
position: Vector2(0, 15),
score: 1000,
characterIconPath: characterIconPath,
onSubmit: (value) {
submitedInitials = value;
},

@ -10,11 +10,16 @@ import '../../helpers/helpers.dart';
void main() {
group('Baseboard', () {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
final assets = [
Assets.images.baseboard.left.keyName,
Assets.images.baseboard.right.keyName,
];
final flameTester = FlameTester(() => TestGame(assets));
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
await game.images.loadAll(assets);
final leftBaseboard = Baseboard(
side: BoardSide.left,
)..initialPosition = Vector2(-20, 0);
@ -24,6 +29,7 @@ void main() {
await game.ensureAddAll([leftBaseboard, rightBaseboard]);
game.camera.followVector2(Vector2.zero());
await tester.pump();
},
verify: (game, tester) async {
await expectLater(

@ -10,15 +10,20 @@ import '../../helpers/helpers.dart';
void main() {
group('Boundaries', () {
final assets = [
Assets.images.boundary.outer.keyName,
Assets.images.boundary.bottom.keyName,
];
final tester = FlameTester(TestGame.new);
tester.testGameWidget(
'render correctly',
setUp: (game, tester) async {
await game.images.loadAll(assets);
await game.addFromBlueprint(Boundaries());
game.camera.followVector2(Vector2.zero());
game.camera.zoom = 3.9;
await game.ready();
game.camera.zoom = 3.2;
await tester.pump();
},
verify: (game, tester) async {
await expectLater(

@ -9,29 +9,33 @@ import '../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
final asset = Assets.images.dash.animatronic.keyName;
final flameTester = FlameTester(() => TestGame([asset]));
group('DashAnimatronic', () {
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
await game.images.load(asset);
await game.ensureAdd(DashAnimatronic()..playing = true);
game.camera.followVector2(Vector2.zero());
await tester.pump();
},
verify: (game, tester) async {
const animationDuration = 3.25;
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/dash_animatronic/start.png'),
);
game.update(1);
game.update(animationDuration * 0.25);
await tester.pump();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/dash_animatronic/middle.png'),
);
game.update(4);
game.update(animationDuration * 0.75);
await tester.pump();
await expectLater(
find.byGame<TestGame>(),

@ -10,7 +10,11 @@ import '../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
final assets = [
Assets.images.flipper.left.keyName,
Assets.images.flipper.right.keyName,
];
final flameTester = FlameTester(() => TestGame(assets));
group('Flipper', () {
// TODO(alestiago): Consider testing always both left and right Flipper.
@ -18,6 +22,7 @@ void main() {
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
await game.images.loadAll(assets);
final leftFlipper = Flipper(
side: BoardSide.left,
)..initialPosition = Vector2(-10, 0);
@ -27,6 +32,7 @@ void main() {
await game.ensureAddAll([leftFlipper, rightFlipper]);
game.camera.followVector2(Vector2.zero());
await tester.pump();
},
verify: (game, tester) async {
await expectLater(

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 38 KiB

@ -10,17 +10,22 @@ import '../../helpers/helpers.dart';
void main() {
group('Slingshot', () {
final flameTester = FlameTester(TestGame.new);
final assets = [
Assets.images.slingshot.upper.keyName,
Assets.images.slingshot.lower.keyName,
];
final flameTester = FlameTester(() => TestGame(assets));
const length = 2.0;
const angle = 0.0;
final spritePath = Assets.images.slingshot.upper.keyName;
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
await game.images.loadAll(assets);
await game.addFromBlueprint(Slingshots());
game.camera.followVector2(Vector2.zero());
await game.ready();
await tester.pump();
},
verify: (game, tester) async {
await expectLater(
@ -36,7 +41,7 @@ void main() {
final slingshot = Slingshot(
length: length,
angle: angle,
spritePath: spritePath,
spritePath: assets.first,
);
await game.ensureAdd(slingshot);
@ -50,7 +55,7 @@ void main() {
final slingshot = Slingshot(
length: length,
angle: angle,
spritePath: spritePath,
spritePath: assets.first,
);
await game.ensureAdd(slingshot);
@ -64,7 +69,7 @@ void main() {
final slingshot = Slingshot(
length: length,
angle: angle,
spritePath: spritePath,
spritePath: assets.first,
);
await game.ensureAdd(slingshot);
@ -82,7 +87,7 @@ void main() {
final slingshot = Slingshot(
length: length,
angle: angle,
spritePath: spritePath,
spritePath: assets.first,
);
await game.ensureAdd(slingshot);

@ -17,6 +17,8 @@ void main() {
'renders correctly',
setUp: (game, tester) async {
await game.addFromBlueprint(SpaceshipRail());
await game.ready();
game.camera.followVector2(Vector2.zero());
game.camera.zoom = 8;
},

@ -1,7 +1,6 @@
// ignore_for_file: cascade_invocations
import 'package:flame/components.dart';
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';
@ -37,6 +36,7 @@ void main() {
);
group('renders correctly', () {
const goldenFilePath = 'golden/spaceship_ramp/';
final centerForSpaceshipRamp = Vector2(-13, -55);
flameTester.testGameWidget(
@ -49,7 +49,10 @@ void main() {
await tester.pump();
expect(
spaceshipRamp.spaceshipRampArrow.current,
spaceshipRamp.components
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.inactive,
);
@ -58,7 +61,7 @@ void main() {
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/spaceship_ramp/inactive.png'),
matchesGoldenFile('${goldenFilePath}inactive.png'),
);
},
);
@ -74,7 +77,10 @@ void main() {
await tester.pump();
expect(
spaceshipRamp.spaceshipRampArrow.current,
spaceshipRamp.components
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.active1,
);
@ -83,7 +89,7 @@ void main() {
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/spaceship_ramp/active1.png'),
matchesGoldenFile('${goldenFilePath}active1.png'),
);
},
);
@ -101,7 +107,10 @@ void main() {
await tester.pump();
expect(
spaceshipRamp.spaceshipRampArrow.current,
spaceshipRamp.components
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.active2,
);
@ -110,7 +119,7 @@ void main() {
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/spaceship_ramp/active2.png'),
matchesGoldenFile('${goldenFilePath}active2.png'),
);
},
);
@ -129,7 +138,10 @@ void main() {
await tester.pump();
expect(
spaceshipRamp.spaceshipRampArrow.current,
spaceshipRamp.components
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.active3,
);
@ -138,7 +150,7 @@ void main() {
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/spaceship_ramp/active3.png'),
matchesGoldenFile('${goldenFilePath}active3.png'),
);
},
);
@ -158,7 +170,10 @@ void main() {
await tester.pump();
expect(
spaceshipRamp.spaceshipRampArrow.current,
spaceshipRamp.components
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.active4,
);
@ -167,7 +182,7 @@ void main() {
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/spaceship_ramp/active4.png'),
matchesGoldenFile('${goldenFilePath}active4.png'),
);
},
);
@ -188,7 +203,10 @@ void main() {
await tester.pump();
expect(
spaceshipRamp.spaceshipRampArrow.current,
spaceshipRamp.components
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.active5,
);
@ -197,7 +215,7 @@ void main() {
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/spaceship_ramp/active5.png'),
matchesGoldenFile('${goldenFilePath}active5.png'),
);
},
);

@ -16,6 +16,8 @@ void main() {
'renders correctly',
setUp: (game, tester) async {
await game.addFromBlueprint(SparkyComputer());
await game.ready();
game.camera.followVector2(Vector2(-15, -50));
},
verify: (game, tester) async {

@ -1,101 +1,49 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/foundation.dart';
const _attachedErrorMessage = "Can't add to attached Blueprints";
// TODO(erickzanardo): Keeping this inside our code base
// so we can experiment with the idea, but this is a
// potential upstream change on Flame.
/// A [Blueprint] is a virtual way of grouping [Component]s
/// that are related, but they need to be added directly on
/// the [FlameGame] level.
/// {@template blueprint}
/// A [Blueprint] is a virtual way of grouping [Component]s that are related,
/// but they need to be added directly on the [FlameGame] level.
/// {@endtemplate blueprint}
// TODO(alestiago): refactor with feat/make-blueprint-extend-component.
abstract class Blueprint<T extends FlameGame> extends Component {
final List<Component> _components = [];
final List<Blueprint> _blueprints = [];
bool _isAttached = false;
/// Called before the the [Component]s managed
/// by this blueprint is added to the [FlameGame]
void build(T gameRef);
/// Attach the [Component]s built on [build] to the [game]
/// instance
@mustCallSuper
Future<void> attach(T game) async {
build(game);
await Future.wait([
game.addAll(_components),
..._blueprints.map(game.addFromBlueprint).toList(),
]);
_isAttached = true;
class Blueprint extends Component {
/// {@macro blueprint}
Blueprint({
Iterable<Component>? components,
Iterable<Blueprint>? blueprints,
}) {
if (components != null) _components.addAll(components);
if (blueprints != null) {
_blueprints.addAll(blueprints);
for (final blueprint in blueprints) {
_components.addAll(blueprint.components);
}
}
}
/// Adds a single [Component] to this blueprint.
@override
Future<void> add(Component component) async {
assert(!_isAttached, _attachedErrorMessage);
_components.add(component);
}
final List<Component> _components = [];
/// Adds a list of [Blueprint]s to this blueprint.
void addAllBlueprints(List<Blueprint> blueprints) {
assert(!_isAttached, _attachedErrorMessage);
_blueprints.addAll(blueprints);
}
final List<Component> _blueprints = [];
/// Adds a single [Blueprint] to this blueprint.
void addBlueprint(Blueprint blueprint) {
assert(!_isAttached, _attachedErrorMessage);
_blueprints.add(blueprint);
Future<void> _addToParent(Component parent) async {
await parent.addAll(_components);
}
/// Returns a copy of the components built by this blueprint
/// Returns a copy of the components built by this blueprint.
List<Component> get components => List.unmodifiable(_components);
/// Returns a copy of the children blueprints
List<Blueprint> get blueprints => List.unmodifiable(_blueprints);
}
/// A [Blueprint] that provides additional
/// structures specific to flame_forge2d
abstract class Forge2DBlueprint extends Blueprint<Forge2DGame> {
final List<ContactCallback> _callbacks = [];
/// Adds a single [ContactCallback] to this blueprint
void addContactCallback(ContactCallback callback) {
assert(!_isAttached, _attachedErrorMessage);
_callbacks.add(callback);
}
/// Adds a collection of [ContactCallback]s to this blueprint
void addAllContactCallback(List<ContactCallback> callbacks) {
assert(!_isAttached, _attachedErrorMessage);
_callbacks.addAll(callbacks);
}
@override
Future<void> attach(Forge2DGame game) async {
await super.attach(game);
for (final callback in _callbacks) {
game.addContactCallback(callback);
}
}
/// Returns a copy of the callbacks built by this blueprint
List<ContactCallback> get callbacks => List.unmodifiable(_callbacks);
/// Returns a copy of the blueprints built by this blueprint.
List<Component> get blueprints => List.unmodifiable(_blueprints);
}
/// Adds helper methods regardin [Blueprint]s to [FlameGame]
extension FlameGameBlueprint on FlameGame {
/// Shortcut to attach a [Blueprint] instance to this game
/// equivalent to `MyBluepinrt().attach(game)`
/// Adds helper methods regarding [Blueprint]s to [FlameGame].
extension FlameGameBlueprint on Component {
/// Shortcut to add a [Blueprint]s components to its parent.
Future<void> addFromBlueprint(Blueprint blueprint) async {
await blueprint.attach(this);
await blueprint._addToParent(this);
}
}

@ -1,138 +1,86 @@
// ignore_for_file: cascade_invocations
import 'package:flame/components.dart';
import 'package:flame_forge2d/contact_callbacks.dart';
import 'package:flame/game.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../helpers/helpers.dart';
class TestContactCallback extends ContactCallback<dynamic, dynamic> {}
class MyBlueprint extends Blueprint {
@override
void build(_) {
add(Component());
addAll([Component(), Component()]);
}
}
class MyOtherBlueprint extends Blueprint {
@override
void build(_) {
add(Component());
}
}
class YetMyOtherBlueprint extends Blueprint {
@override
void build(_) {
add(Component());
}
}
class MyComposedBlueprint extends Blueprint {
@override
void build(_) {
addBlueprint(MyBlueprint());
addAllBlueprints([MyOtherBlueprint(), YetMyOtherBlueprint()]);
}
}
class MyForge2dBlueprint extends Forge2DBlueprint {
@override
void build(_) {
addContactCallback(MockContactCallback());
addAllContactCallback([MockContactCallback(), MockContactCallback()]);
}
}
void main() {
group('Blueprint', () {
setUpAll(() {
registerFallbackValue(MyBlueprint());
registerFallbackValue(Component());
});
test('components can be added to it', () {
final blueprint = MyBlueprint()..build(MockForge2DGame());
TestWidgetsFlutterBinding.ensureInitialized();
expect(blueprint.components.length, equals(3));
});
test('blueprints can be added to it', () {
final blueprint = MyComposedBlueprint()..build(MockForge2DGame());
expect(blueprint.blueprints.length, equals(3));
});
test('adds the components to a game on attach', () {
final mockGame = MockForge2DGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
MyBlueprint().attach(mockGame);
verify(() => mockGame.addAll(any())).called(1);
});
test('adds components from a child Blueprint the to a game on attach', () {
final mockGame = MockForge2DGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
MyComposedBlueprint().attach(mockGame);
verify(() => mockGame.addAll(any())).called(4);
group('Blueprint', () {
final flameTester = FlameTester(FlameGame.new);
test('correctly sets and gets components', () {
final component1 = Component();
final component2 = Component();
final blueprint = Blueprint(
components: [
component1,
component2,
],
);
expect(blueprint.components.length, 2);
expect(blueprint.components, contains(component1));
expect(blueprint.components, contains(component2));
});
test(
'throws assertion error when adding to an already attached blueprint',
() async {
final mockGame = MockForge2DGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
final blueprint = MyBlueprint();
await blueprint.attach(mockGame);
expect(() => blueprint.add(Component()), throwsAssertionError);
expect(() => blueprint.addAll([Component()]), throwsAssertionError);
},
);
});
test('correctly sets and gets blueprints', () {
final blueprint2 = Blueprint(
components: [Component()],
);
final blueprint1 = Blueprint(
components: [Component()],
blueprints: [blueprint2],
);
group('Forge2DBlueprint', () {
setUpAll(() {
registerFallbackValue(TestContactCallback());
expect(blueprint1.blueprints, contains(blueprint2));
});
test('callbacks can be added to it', () {
final blueprint = MyForge2dBlueprint()..build(MockForge2DGame());
expect(blueprint.callbacks.length, equals(3));
flameTester.test('adds the components to parent on attach', (game) async {
final blueprint = Blueprint(
components: [
Component(),
Component(),
],
);
await game.addFromBlueprint(blueprint);
await game.ready();
for (final component in blueprint.components) {
expect(game.children.contains(component), isTrue);
}
});
test('adds the callbacks to a game on attach', () async {
final mockGame = MockForge2DGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {});
await MyForge2dBlueprint().attach(mockGame);
verify(() => mockGame.addContactCallback(any())).called(3);
flameTester.test('adds components from a child Blueprint', (game) async {
final childBlueprint = Blueprint(
components: [
Component(),
Component(),
],
);
final parentBlueprint = Blueprint(
components: [
Component(),
Component(),
],
blueprints: [
childBlueprint,
],
);
await game.addFromBlueprint(parentBlueprint);
await game.ready();
for (final component in childBlueprint.components) {
expect(game.children, contains(component));
expect(parentBlueprint.components, contains(component));
}
for (final component in parentBlueprint.components) {
expect(game.children, contains(component));
}
});
test(
'throws assertion error when adding to an already attached blueprint',
() async {
final mockGame = MockForge2DGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {});
final blueprint = MyForge2dBlueprint();
await blueprint.attach(mockGame);
expect(
() => blueprint.addContactCallback(MockContactCallback()),
throwsAssertionError,
);
expect(
() => blueprint.addAllContactCallback([MockContactCallback()]),
throwsAssertionError,
);
},
);
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

@ -39,6 +39,10 @@ class $AssetsImagesAndroidGen {
/// File path: assets/images/android/icon.png
AssetGenImage get icon =>
const AssetGenImage('assets/images/android/icon.png');
/// File path: assets/images/android/leaderboard_icon.png
AssetGenImage get leaderboardIcon =>
const AssetGenImage('assets/images/android/leaderboard_icon.png');
}
class $AssetsImagesDashGen {
@ -54,6 +58,10 @@ class $AssetsImagesDashGen {
/// File path: assets/images/dash/icon.png
AssetGenImage get icon => const AssetGenImage('assets/images/dash/icon.png');
/// File path: assets/images/dash/leaderboard_icon.png
AssetGenImage get leaderboardIcon =>
const AssetGenImage('assets/images/dash/leaderboard_icon.png');
}
class $AssetsImagesDinoGen {
@ -69,6 +77,10 @@ class $AssetsImagesDinoGen {
/// File path: assets/images/dino/icon.png
AssetGenImage get icon => const AssetGenImage('assets/images/dino/icon.png');
/// File path: assets/images/dino/leaderboard_icon.png
AssetGenImage get leaderboardIcon =>
const AssetGenImage('assets/images/dino/leaderboard_icon.png');
}
class $AssetsImagesSparkyGen {
@ -85,6 +97,10 @@ class $AssetsImagesSparkyGen {
/// File path: assets/images/sparky/icon.png
AssetGenImage get icon =>
const AssetGenImage('assets/images/sparky/icon.png');
/// File path: assets/images/sparky/leaderboard_icon.png
AssetGenImage get leaderboardIcon =>
const AssetGenImage('assets/images/sparky/leaderboard_icon.png');
}
class Assets {

@ -22,4 +22,7 @@ class AndroidTheme extends CharacterTheme {
@override
AssetGenImage get icon => Assets.images.android.icon;
@override
AssetGenImage get leaderboardIcon => Assets.images.android.leaderboardIcon;
}

@ -27,6 +27,9 @@ abstract class CharacterTheme extends Equatable {
/// Icon asset.
AssetGenImage get icon;
/// Icon asset for the leaderboard.
AssetGenImage get leaderboardIcon;
@override
List<Object?> get props => [
name,
@ -34,5 +37,6 @@ abstract class CharacterTheme extends Equatable {
character,
background,
icon,
leaderboardIcon,
];
}

@ -22,4 +22,7 @@ class DashTheme extends CharacterTheme {
@override
AssetGenImage get icon => Assets.images.dash.icon;
@override
AssetGenImage get leaderboardIcon => Assets.images.dash.leaderboardIcon;
}

@ -22,4 +22,7 @@ class DinoTheme extends CharacterTheme {
@override
AssetGenImage get icon => Assets.images.dino.icon;
@override
AssetGenImage get leaderboardIcon => Assets.images.dino.leaderboardIcon;
}

@ -22,4 +22,7 @@ class SparkyTheme extends CharacterTheme {
@override
AssetGenImage get icon => Assets.images.sparky.icon;
@override
AssetGenImage get leaderboardIcon => Assets.images.sparky.leaderboardIcon;
}

@ -17,7 +17,6 @@ dev_dependencies:
very_good_analysis: ^2.4.0
flutter:
uses-material-design: true
generate: true
assets:
- assets/images/

@ -1,6 +1,5 @@
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_theme/pinball_theme.dart';
@ -13,27 +12,5 @@ void main() {
test('supports value equality', () {
expect(AndroidTheme(), equals(AndroidTheme()));
});
test('ballColor is correct', () {
expect(AndroidTheme().ballColor, equals(Colors.green));
});
test('character asset is correct', () {
expect(
AndroidTheme().character,
equals(Assets.images.android.character),
);
});
test('background asset is correct', () {
expect(
AndroidTheme().background,
equals(Assets.images.android.background),
);
});
test('icon asset is correct', () {
expect(AndroidTheme().icon, equals(Assets.images.android.icon));
});
});
}

@ -1,6 +1,5 @@
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_theme/pinball_theme.dart';
@ -13,27 +12,5 @@ void main() {
test('supports value equality', () {
expect(DashTheme(), equals(DashTheme()));
});
test('ballColor is correct', () {
expect(DashTheme().ballColor, equals(Colors.blue));
});
test('character asset is correct', () {
expect(
DashTheme().character,
equals(Assets.images.dash.character),
);
});
test('background asset is correct', () {
expect(
DashTheme().background,
equals(Assets.images.dash.background),
);
});
test('icon asset is correct', () {
expect(DashTheme().icon, equals(Assets.images.dash.icon));
});
});
}

@ -1,6 +1,5 @@
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_theme/pinball_theme.dart';
@ -13,27 +12,5 @@ void main() {
test('supports value equality', () {
expect(DinoTheme(), equals(DinoTheme()));
});
test('ballColor is correct', () {
expect(DinoTheme().ballColor, equals(Colors.grey));
});
test('character asset is correct', () {
expect(
DinoTheme().character,
equals(Assets.images.dino.character),
);
});
test('background asset is correct', () {
expect(
DinoTheme().background,
equals(Assets.images.dino.background),
);
});
test('icon asset is correct', () {
expect(DinoTheme().icon, equals(Assets.images.dino.icon));
});
});
}

@ -1,6 +1,5 @@
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_theme/pinball_theme.dart';
@ -13,27 +12,5 @@ void main() {
test('supports value equality', () {
expect(SparkyTheme(), equals(SparkyTheme()));
});
test('ballColor is correct', () {
expect(SparkyTheme().ballColor, equals(Colors.orange));
});
test('character asset is correct', () {
expect(
SparkyTheme().character,
equals(Assets.images.sparky.character),
);
});
test('background asset is correct', () {
expect(
SparkyTheme().background,
equals(Assets.images.sparky.background),
);
});
test('icon asset is correct', () {
expect(SparkyTheme().icon, equals(Assets.images.sparky.icon));
});
});
}

@ -7,14 +7,14 @@ packages:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "31.0.0"
version: "39.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.0"
version: "4.0.0"
args:
dependency: transitive
description:
@ -71,13 +71,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.5"
clock:
dependency: transitive
description:
@ -321,7 +314,7 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
version: "0.6.4"
json_annotation:
dependency: transitive
description:
@ -356,7 +349,7 @@ packages:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
version: "0.1.4"
meta:
dependency: transitive
description:
@ -419,7 +412,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
version: "1.8.1"
path_provider:
dependency: transitive
description:
@ -592,7 +585,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.2"
stack_trace:
dependency: transitive
description:
@ -634,21 +627,21 @@ packages:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.19.5"
version: "1.21.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.8"
version: "0.4.9"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.9"
version: "0.4.13"
typed_data:
dependency: transitive
description:
@ -669,7 +662,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "2.1.2"
very_good_analysis:
dependency: "direct dev"
description:

@ -47,6 +47,7 @@ flutter:
assets:
- assets/images/components/
- assets/images/bonus_animation/
flutter_gen:
line_length: 80

@ -16,10 +16,15 @@ void main() {
Assets.images.dash.bumper.a.inactive.keyName,
Assets.images.dash.bumper.b.active.keyName,
Assets.images.dash.bumper.b.inactive.keyName,
Assets.images.dash.animatronic.keyName,
Assets.images.signpost.inactive.keyName,
Assets.images.signpost.active1.keyName,
Assets.images.signpost.active2.keyName,
Assets.images.signpost.active3.keyName,
Assets.images.baseboard.left.keyName,
Assets.images.baseboard.right.keyName,
Assets.images.flipper.left.keyName,
Assets.images.flipper.right.keyName,
];
final flameTester = FlameTester(() => EmptyPinballTestGame(assets));

@ -11,7 +11,11 @@ import '../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(EmptyPinballTestGame.new);
final assets = [
Assets.images.flipper.left.keyName,
Assets.images.flipper.right.keyName,
];
final flameTester = FlameTester(() => EmptyPinballTestGame(assets));
final flameBlocTester = FlameBlocTester<EmptyPinballTestGame, GameBloc>(
gameBuilder: EmptyPinballTestGame.new,
@ -25,6 +29,7 @@ void main() {
whenListen(bloc, Stream.value(state), initialState: state);
return bloc;
},
assets: assets,
);
group('FlipperController', () {

@ -8,28 +8,21 @@ import 'package:pinball/game/game.dart';
import '../../helpers/helpers.dart';
void main() {
group('SparkyComputerController', () {
group('ControlledSparkyComputer', () {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(EmptyPinballTestGame.new);
late ControlledSparkyComputer controlledSparkyComputer;
setUp(() {
controlledSparkyComputer = ControlledSparkyComputer();
});
test('can be instantiated', () {
expect(
SparkyComputerController(controlledSparkyComputer),
isA<SparkyComputerController>(),
);
flameTester.test('loads correctly', (game) async {
final sparkyComputer = ControlledSparkyComputer();
await game.ensureAdd(sparkyComputer);
expect(game.children, contains(sparkyComputer));
});
flameTester.testGameWidget(
'SparkyTurboChargeSensorBallContactCallback turbo charges the ball',
setUp: (game, tester) async {
final contackCallback = SparkyTurboChargeSensorBallContactCallback();
final sparkyTurboChargeSensor = MockSparkyTurboChargeSensor();
final contackCallback = SparkyComputerSensorBallContactCallback();
final sparkyTurboChargeSensor = MockSparkyComputerSensor();
final ball = MockControlledBall();
final controller = MockBallController();

@ -19,6 +19,7 @@ void main() {
Assets.images.dash.bumper.a.inactive.keyName,
Assets.images.dash.bumper.b.active.keyName,
Assets.images.dash.bumper.b.inactive.keyName,
Assets.images.dash.animatronic.keyName,
Assets.images.signpost.inactive.keyName,
Assets.images.signpost.active1.keyName,
Assets.images.signpost.active2.keyName,

@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_theme/pinball_theme.dart';
import '../../helpers/helpers.dart';
@ -43,6 +44,7 @@ void main() {
when(
() => backboard.gameOverMode(
score: any(named: 'score'),
characterIconPath: any(named: 'characterIconPath'),
onSubmit: any(named: 'onSubmit'),
),
).thenAnswer((_) async {});
@ -55,6 +57,8 @@ void main() {
when(game.firstChild<Backboard>).thenReturn(backboard);
when(game.firstChild<CameraController>).thenReturn(cameraController);
when(() => game.overlays).thenReturn(overlays);
when(() => game.theme)
.thenReturn(PinballTheme(characterTheme: DashTheme()));
});
test(
@ -71,6 +75,7 @@ void main() {
verify(
() => backboard.gameOverMode(
score: 0,
characterIconPath: any(named: 'characterIconPath'),
onSubmit: any(named: 'onSubmit'),
),
).called(1);

@ -19,6 +19,7 @@ void main() {
Assets.images.dash.bumper.a.inactive.keyName,
Assets.images.dash.bumper.b.active.keyName,
Assets.images.dash.bumper.b.inactive.keyName,
Assets.images.dash.animatronic.keyName,
Assets.images.signpost.inactive.keyName,
Assets.images.signpost.active1.keyName,
Assets.images.signpost.active2.keyName,
@ -43,6 +44,14 @@ void main() {
Assets.images.spaceship.ramp.arrow.active3.keyName,
Assets.images.spaceship.ramp.arrow.active4.keyName,
Assets.images.spaceship.ramp.arrow.active5.keyName,
Assets.images.baseboard.left.keyName,
Assets.images.baseboard.right.keyName,
Assets.images.flipper.left.keyName,
Assets.images.flipper.right.keyName,
Assets.images.boundary.outer.keyName,
Assets.images.boundary.bottom.keyName,
Assets.images.slingshot.upper.keyName,
Assets.images.slingshot.lower.keyName,
];
final flameTester = FlameTester(() => PinballTestGame(assets));
final debugModeFlameTester = FlameTester(() => DebugPinballTestGame(assets));
@ -52,17 +61,6 @@ void main() {
// [BallScorePointsCallback] once the following issue is resolved:
// https://github.com/flame-engine/flame/issues/1416
group('components', () {
flameTester.test(
'has three Walls',
(game) async {
await game.ready();
final walls = game.children.where(
(component) => component is Wall && component is! BottomWall,
);
expect(walls.length, 3);
},
);
flameTester.test(
'has only one BottomWall',
(game) async {

@ -160,6 +160,10 @@ void main() {
});
group('PinballGameView', () {
setUp(() async {
await Future.wait<void>(game.preLoadAssets());
});
testWidgets('renders game and a hud', (tester) async {
final gameBloc = MockGameBloc();
whenListen(

@ -0,0 +1,99 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:flame/assets.dart';
import 'package:flame/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/view/widgets/bonus_animation.dart';
import '../../../helpers/helpers.dart';
class MockImages extends Mock implements Images {}
class MockImage extends Mock implements ui.Image {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() async {
await BonusAnimation.loadAssets();
});
group('loads SpriteAnimationWidget correctly for', () {
testWidgets('dashNest', (tester) async {
await tester.pumpApp(
BonusAnimation.dashNest(),
);
await tester.pump();
expect(find.byType(SpriteAnimationWidget), findsOneWidget);
});
testWidgets('dino', (tester) async {
await tester.pumpApp(
BonusAnimation.dino(),
);
await tester.pump();
expect(find.byType(SpriteAnimationWidget), findsOneWidget);
});
testWidgets('sparkyTurboCharge', (tester) async {
await tester.pumpApp(
BonusAnimation.sparkyTurboCharge(),
);
await tester.pump();
expect(find.byType(SpriteAnimationWidget), findsOneWidget);
});
testWidgets('google', (tester) async {
await tester.pumpApp(
BonusAnimation.google(),
);
await tester.pump();
expect(find.byType(SpriteAnimationWidget), findsOneWidget);
});
testWidgets('android', (tester) async {
await tester.pumpApp(
BonusAnimation.android(),
);
await tester.pump();
expect(find.byType(SpriteAnimationWidget), findsOneWidget);
});
});
// TODO(arturplaczek): refactor this test when there is a new version of the
// flame with an onComplete callback in SpriteAnimationWidget
// https://github.com/flame-engine/flame/issues/1543
testWidgets('called onCompleted callback at the end of animation ',
(tester) async {
final completer = Completer<void>();
await tester.runAsync(() async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: BonusAnimation.dashNest(
onCompleted: completer.complete,
),
),
),
);
await tester.pump();
await Future<void>.delayed(const Duration(seconds: 4));
await tester.pump();
expect(completer.isCompleted, isTrue);
});
});
}

@ -69,8 +69,7 @@ class MockDashNestBumper extends Mock implements DashNestBumper {}
class MockPinballAudio extends Mock implements PinballAudio {}
class MockSparkyTurboChargeSensor extends Mock
implements SparkyTurboChargeSensor {}
class MockSparkyComputerSensor extends Mock implements SparkyComputerSensor {}
class MockAssetsManagerCubit extends Mock implements AssetsManagerCubit {}

Loading…
Cancel
Save