feat: implemented rendering with `ZIndex` (#282)

* feat: defined PinballCavas

* refactor: remove unawaited

* feat: defined renderTree

* refactor: made LaunchRamp a Component

* refactor: removed RenderPriority from FlutterForest

* feat: implementing PinballCanvasComponent

* refactor: migrated to zIndex

* refactor: removed old pinball_canvas.dart

* refactor: cleaned PinballCanvasComponent

* refactor: reordered children additions

* refactor: adjusted priorities

* refactor: adjusted RenderPriority

* refactor: improved rendering algorithm

* refactor: removes Blueprint

* test: migrating blueprint tests

* docs: removed Blueprint references

* refactor: renamed PinballCanvas to ZCanvas

* test: tested z_canvas_component

* refactor: renamed LayerSensor to use zIndex

* refactor: renamed RenderPriority to ZIndexes

* refactor: removed priority instances

* refactor: removed priority occurences

* fix: plunger and tapping positions

* test: correctly testes SparkyScorch

* test: adjusted ScoringBehavior test

* docs: documented z_canvas_component

* test: fixed ScoringBehavior tests

* refactor: renamed to ZCanvas

* refactor: applied PR suggestions
pull/287/head
Alejandro Santiago 3 years ago committed by GitHub
parent d748188286
commit 95fe7a62aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,19 +1,21 @@
// ignore_for_file: avoid_renaming_method_parameters // ignore_for_file: avoid_renaming_method_parameters
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame/components.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template android_acres} /// {@template android_acres}
/// Area positioned on the left side of the board containing the /// Area positioned on the left side of the board containing the
/// [AndroidSpaceship], [SpaceshipRamp], [SpaceshipRail], and [AndroidBumper]s. /// [AndroidSpaceship], [SpaceshipRamp], [SpaceshipRail], and [AndroidBumper]s.
/// {@endtemplate} /// {@endtemplate}
class AndroidAcres extends Blueprint { class AndroidAcres extends Component {
/// {@macro android_acres} /// {@macro android_acres}
AndroidAcres() AndroidAcres()
: super( : super(
components: [ children: [
SpaceshipRamp(),
SpaceshipRail(),
AndroidSpaceship(position: Vector2(-26.5, -28.5)),
AndroidBumper.a( AndroidBumper.a(
children: [ children: [
ScoringBehavior(points: 20000), ScoringBehavior(points: 20000),
@ -30,10 +32,5 @@ class AndroidAcres extends Blueprint {
], ],
)..initialPosition = Vector2(-20.5, -13.8), )..initialPosition = Vector2(-20.5, -13.8),
], ],
blueprints: [
SpaceshipRamp(),
AndroidSpaceship(position: Vector2(-26.5, -28.5)),
SpaceshipRail(),
],
); );
} }

@ -1,6 +1,7 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template bottom_group} /// {@template bottom_group}
/// Grouping of the board's symmetrical bottom [Component]s. /// Grouping of the board's symmetrical bottom [Component]s.
@ -8,7 +9,7 @@ import 'package:pinball_components/pinball_components.dart';
/// The [BottomGroup] consists of [Flipper]s, [Baseboard]s and [Kicker]s. /// The [BottomGroup] consists of [Flipper]s, [Baseboard]s and [Kicker]s.
/// {@endtemplate} /// {@endtemplate}
// TODO(allisonryan0002): Consider renaming. // TODO(allisonryan0002): Consider renaming.
class BottomGroup extends Component { class BottomGroup extends Component with ZIndex {
/// {@macro bottom_group} /// {@macro bottom_group}
BottomGroup() BottomGroup()
: super( : super(
@ -16,8 +17,9 @@ class BottomGroup extends Component {
_BottomGroupSide(side: BoardSide.right), _BottomGroupSide(side: BoardSide.right),
_BottomGroupSide(side: BoardSide.left), _BottomGroupSide(side: BoardSide.left),
], ],
priority: RenderPriority.bottomGroup, ) {
); zIndex = ZIndexes.bottomGroup;
}
} }
/// {@template bottom_group_side} /// {@template bottom_group_side}

@ -19,8 +19,8 @@ class ControlledBall extends Ball with Controls<BallController> {
required CharacterTheme characterTheme, required CharacterTheme characterTheme,
}) : super(baseColor: characterTheme.ballColor) { }) : super(baseColor: characterTheme.ballColor) {
controller = BallController(this); controller = BallController(this);
priority = RenderPriority.ballOnLaunchRamp;
layer = Layer.launcher; layer = Layer.launcher;
zIndex = ZIndexes.ballOnLaunchRamp;
} }
/// {@template bonus_ball} /// {@template bonus_ball}
@ -30,13 +30,13 @@ class ControlledBall extends Ball with Controls<BallController> {
required CharacterTheme characterTheme, required CharacterTheme characterTheme,
}) : super(baseColor: characterTheme.ballColor) { }) : super(baseColor: characterTheme.ballColor) {
controller = BallController(this); controller = BallController(this);
priority = RenderPriority.ballOnBoard; zIndex = ZIndexes.ballOnBoard;
} }
/// [Ball] used in [DebugPinballGame]. /// [Ball] used in [DebugPinballGame].
ControlledBall.debug() : super(baseColor: const Color(0xFFFF0000)) { ControlledBall.debug() : super(baseColor: const Color(0xFFFF0000)) {
controller = BallController(this); controller = BallController(this);
priority = RenderPriority.ballOnBoard; zIndex = ZIndexes.ballOnBoard;
} }
} }

@ -1,7 +1,6 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template dino_desert} /// {@template dino_desert}
/// Area located next to the [Launcher] containing the [ChromeDino] and /// Area located next to the [Launcher] containing the [ChromeDino] and
@ -9,15 +8,14 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@endtemplate} /// {@endtemplate}
// TODO(allisonryan0002): use a controller to initiate dino bonus when dino is // TODO(allisonryan0002): use a controller to initiate dino bonus when dino is
// fully implemented. // fully implemented.
class DinoDesert extends Blueprint { class DinoDesert extends Component {
/// {@macro dino_desert} /// {@macro dino_desert}
DinoDesert() DinoDesert()
: super( : super(
components: [ children: [
ChromeDino()..initialPosition = Vector2(12.3, -6.9), ChromeDino()..initialPosition = Vector2(12.3, -6.9),
],
blueprints: [
DinoWalls(), DinoWalls(),
Slingshots(),
], ],
); );
} }

@ -5,16 +5,16 @@ import 'package:flutter/material.dart';
import 'package:pinball/game/components/flutter_forest/behaviors/behaviors.dart'; import 'package:pinball/game/components/flutter_forest/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template flutter_forest} /// {@template flutter_forest}
/// Area positioned at the top right of the board where the [Ball] can bounce /// Area positioned at the top right of the board where the [Ball] can bounce
/// off [DashNestBumper]s. /// off [DashNestBumper]s.
/// {@endtemplate} /// {@endtemplate}
class FlutterForest extends Component { class FlutterForest extends Component with ZIndex {
/// {@macro flutter_forest} /// {@macro flutter_forest}
FlutterForest() FlutterForest()
: super( : super(
priority: RenderPriority.flutterForest,
children: [ children: [
Signpost( Signpost(
children: [ children: [
@ -39,7 +39,9 @@ class FlutterForest extends Component {
DashAnimatronic()..position = Vector2(20, -66), DashAnimatronic()..position = Vector2(20, -66),
FlutterForestBonusBehavior(), FlutterForestBonusBehavior(),
], ],
); ) {
zIndex = ZIndexes.flutterForest;
}
/// Creates a [FlutterForest] without any children. /// Creates a [FlutterForest] without any children.
/// ///

@ -3,11 +3,12 @@ import 'package:flutter/material.dart';
import 'package:pinball/game/components/google_word/behaviors/behaviors.dart'; import 'package:pinball/game/components/google_word/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template google_word} /// {@template google_word}
/// Loads all [GoogleLetter]s to compose a [GoogleWord]. /// Loads all [GoogleLetter]s to compose a [GoogleWord].
/// {@endtemplate} /// {@endtemplate}
class GoogleWord extends Component { class GoogleWord extends Component with ZIndex {
/// {@macro google_word} /// {@macro google_word}
GoogleWord({ GoogleWord({
required Vector2 position, required Vector2 position,
@ -39,7 +40,9 @@ class GoogleWord extends Component {
)..initialPosition = position + Vector2(12.92, 1.82), )..initialPosition = position + Vector2(12.92, 1.82),
GoogleWordBonusBehavior(), GoogleWordBonusBehavior(),
], ],
); ) {
zIndex = ZIndexes.decal;
}
/// Creates a [GoogleWord] without any children. /// Creates a [GoogleWord] without any children.
/// ///

@ -1,21 +1,20 @@
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame/components.dart';
import 'package:pinball/game/components/components.dart'; import 'package:pinball/game/components/components.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets; import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template launcher} /// {@template launcher}
/// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and /// Channel on the right side of the board containing the [LaunchRamp],
/// [LaunchRamp]. /// [Plunger], and [RocketSpriteComponent].
/// {@endtemplate} /// {@endtemplate}
class Launcher extends Blueprint { class Launcher extends Component {
/// {@macro launcher} /// {@macro launcher}
Launcher() Launcher()
: super( : super(
components: [ children: [
LaunchRamp(),
ControlledPlunger(compressionDistance: 10.5) ControlledPlunger(compressionDistance: 10.5)
..initialPosition = Vector2(41.1, 43), ..initialPosition = Vector2(41.1, 43),
RocketSpriteComponent()..position = Vector2(43, 62.3), RocketSpriteComponent()..position = Vector2(43, 62.3),
], ],
blueprints: [LaunchRamp()],
); );
} }

@ -3,11 +3,12 @@ import 'package:flame/components.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pinball/game/components/multipliers/behaviors/behaviors.dart'; import 'package:pinball/game/components/multipliers/behaviors/behaviors.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template multipliers} /// {@template multipliers}
/// A group for the multipliers on the board. /// A group for the multipliers on the board.
/// {@endtemplate} /// {@endtemplate}
class Multipliers extends Component { class Multipliers extends Component with ZIndex {
/// {@macro multipliers} /// {@macro multipliers}
Multipliers() Multipliers()
: super( : super(
@ -34,7 +35,9 @@ class Multipliers extends Component {
), ),
MultipliersBehavior(), MultipliersBehavior(),
], ],
); ) {
zIndex = ZIndexes.decal;
}
/// Creates [Multipliers] without any children. /// Creates [Multipliers] without any children.
/// ///

@ -24,11 +24,11 @@ class ScoringBehavior extends ContactBehavior with HasGameRef<PinballGame> {
gameRef.read<GameBloc>().add(Scored(points: _points)); gameRef.read<GameBloc>().add(Scored(points: _points));
gameRef.audio.score(); gameRef.audio.score();
gameRef.add( gameRef.firstChild<ZCanvasComponent>()!.add(
ScoreText( ScoreText(
text: _points.toString(), text: _points.toString(),
position: other.body.position, position: other.body.position,
), ),
); );
} }
} }

@ -1,19 +1,19 @@
// ignore_for_file: avoid_renaming_method_parameters // ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_scorch} /// {@template sparky_scorch}
/// Area positioned at the top left of the board containing the /// Area positioned at the top left of the board containing the
/// [SparkyComputer], [SparkyAnimatronic], and [SparkyBumper]s. /// [SparkyComputer], [SparkyAnimatronic], and [SparkyBumper]s.
/// {@endtemplate} /// {@endtemplate}
class SparkyScorch extends Blueprint { class SparkyScorch extends Component {
/// {@macro sparky_scorch} /// {@macro sparky_scorch}
SparkyScorch() SparkyScorch()
: super( : super(
components: [ children: [
SparkyBumper.a( SparkyBumper.a(
children: [ children: [
ScoringBehavior(points: 20000), ScoringBehavior(points: 20000),
@ -31,8 +31,6 @@ class SparkyScorch extends Blueprint {
)..initialPosition = Vector2(-3.3, -52.55), )..initialPosition = Vector2(-3.3, -52.55),
SparkyComputerSensor()..initialPosition = Vector2(-13, -49.8), SparkyComputerSensor()..initialPosition = Vector2(-13, -49.8),
SparkyAnimatronic()..position = Vector2(-13.8, -58.2), SparkyAnimatronic()..position = Vector2(-13.8, -58.2),
],
blueprints: [
SparkyComputer(), SparkyComputer(),
], ],
); );

@ -42,32 +42,40 @@ class PinballGame extends Forge2DGame
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
unawaited(add(gameFlowController = GameFlowController(this))); await add(gameFlowController = GameFlowController(this));
unawaited(add(CameraController(this))); await add(CameraController(this));
unawaited(add(Backboard.waiting(position: Vector2(0, -88))));
await add(BoardBackgroundSpriteComponent()); final machine = [
await add(Drain()); BoardBackgroundSpriteComponent(),
await add(BottomGroup()); Boundaries(),
unawaited(addFromBlueprint(Boundaries())); Backboard.waiting(position: Vector2(0, -88)),
];
final launcher = Launcher(); final decals = [
unawaited(addFromBlueprint(launcher));
await add(Multipliers());
await add(FlutterForest());
await addFromBlueprint(SparkyScorch());
await addFromBlueprint(AndroidAcres());
await addFromBlueprint(DinoDesert());
unawaited(addFromBlueprint(Slingshots()));
await add(
GoogleWord( GoogleWord(
position: Vector2( position: Vector2(-4.1, 1.8),
BoardDimensions.bounds.center.dx - 4.1, ),
BoardDimensions.bounds.center.dy + 1.8, Multipliers(),
), ];
final characterAreas = [
AndroidAcres(),
DinoDesert(),
FlutterForest(),
SparkyScorch(),
];
await add(
ZCanvasComponent(
children: [
...machine,
...decals,
...characterAreas,
Drain(),
BottomGroup(),
Launcher(),
],
), ),
); );
controller.attachTo(launcher.components.whereType<Plunger>().single);
await super.onLoad(); await super.onLoad();
} }
@ -76,12 +84,12 @@ class PinballGame extends Forge2DGame
@override @override
void onTapDown(TapDownInfo info) { void onTapDown(TapDownInfo info) {
if (info.raw.kind == PointerDeviceKind.touch) { if (info.raw.kind == PointerDeviceKind.touch) {
final rocket = children.whereType<RocketSpriteComponent>().first; final rocket = descendants().whereType<RocketSpriteComponent>().first;
final bounds = rocket.topLeftPosition & rocket.size; final bounds = rocket.topLeftPosition & rocket.size;
// NOTE(wolfen): As long as Flame does not have https://github.com/flame-engine/flame/issues/1586 we need to check it at the highest level manually. // NOTE(wolfen): As long as Flame does not have https://github.com/flame-engine/flame/issues/1586 we need to check it at the highest level manually.
if (bounds.contains(info.eventPosition.game.toOffset())) { if (bounds.contains(info.eventPosition.game.toOffset())) {
children.whereType<Plunger>().first.pull(); descendants().whereType<Plunger>().single.pull();
} else { } else {
final leftSide = info.eventPosition.widget.x < canvasSize.x / 2; final leftSide = info.eventPosition.widget.x < canvasSize.x / 2;
focusedBoardSide = leftSide ? BoardSide.left : BoardSide.right; focusedBoardSide = leftSide ? BoardSide.left : BoardSide.right;
@ -101,7 +109,7 @@ class PinballGame extends Forge2DGame
final bounds = rocket.topLeftPosition & rocket.size; final bounds = rocket.topLeftPosition & rocket.size;
if (bounds.contains(info.eventPosition.game.toOffset())) { if (bounds.contains(info.eventPosition.game.toOffset())) {
children.whereType<Plunger>().first.release(); descendants().whereType<Plunger>().single.release();
} else { } else {
_moveFlippersDown(); _moveFlippersDown();
} }
@ -110,7 +118,7 @@ class PinballGame extends Forge2DGame
@override @override
void onTapCancel() { void onTapCancel() {
children.whereType<Plunger>().first.release(); descendants().whereType<Plunger>().single.release();
_moveFlippersDown(); _moveFlippersDown();
super.onTapCancel(); super.onTapCancel();
@ -131,8 +139,6 @@ class _GameBallsController extends ComponentController<PinballGame>
with BlocComponent<GameBloc, GameState> { with BlocComponent<GameBloc, GameState> {
_GameBallsController(PinballGame game) : super(game); _GameBallsController(PinballGame game) : super(game);
late final Plunger _plunger;
@override @override
bool listenWhen(GameState? previousState, GameState newState) { bool listenWhen(GameState? previousState, GameState newState) {
final noBallsLeft = component.descendants().whereType<Ball>().isEmpty; final noBallsLeft = component.descendants().whereType<Ball>().isEmpty;
@ -144,30 +150,27 @@ class _GameBallsController extends ComponentController<PinballGame>
@override @override
void onNewState(GameState state) { void onNewState(GameState state) {
super.onNewState(state); super.onNewState(state);
_spawnBall(); spawnBall();
} }
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
_spawnBall(); spawnBall();
}
void _spawnBall() {
final ball = ControlledBall.launch(
characterTheme: component.characterTheme,
)..initialPosition = Vector2(
_plunger.body.position.x,
_plunger.body.position.y - Ball.size.y,
);
component.add(ball);
} }
/// Attaches the controller to the plunger. void spawnBall() {
// TODO(alestiago): Remove this method and use onLoad instead. // TODO(alestiago): Refactor with behavioural pattern.
// ignore: use_setters_to_change_properties component.ready().whenComplete(() {
void attachTo(Plunger plunger) { final plunger = parent!.descendants().whereType<Plunger>().single;
_plunger = plunger; final ball = ControlledBall.launch(
characterTheme: component.characterTheme,
)..initialPosition = Vector2(
plunger.body.position.x,
plunger.body.position.y - Ball.size.y,
);
component.firstChild<ZCanvasComponent>()?.add(ball);
});
} }
} }
@ -179,7 +182,7 @@ class DebugPinballGame extends PinballGame with FPSCounter {
characterTheme: characterTheme, characterTheme: characterTheme,
audio: audio, audio: audio,
) { ) {
controller = _DebugGameBallsController(this); controller = _GameBallsController(this);
} }
@override @override
@ -188,42 +191,21 @@ class DebugPinballGame extends PinballGame with FPSCounter {
await add(_DebugInformation()); await add(_DebugInformation());
} }
// TODO(allisonryan0002): Remove after google letters have been correctly
// placed.
// Future<void> _loadBackground() async {
// final sprite = await loadSprite(
// Assets.images.components.background.path,
// );
// final spriteComponent = SpriteComponent(
// sprite: sprite,
// size: Vector2(120, 160),
// anchor: Anchor.center,
// )
// ..position = Vector2(0, -7.8)
// ..priority = RenderPriority.boardBackground;
// await add(spriteComponent);
// }
@override @override
void onTapUp(TapUpInfo info) { void onTapUp(TapUpInfo info) {
super.onTapUp(info); super.onTapUp(info);
if (info.raw.kind == PointerDeviceKind.mouse) { if (info.raw.kind == PointerDeviceKind.mouse) {
add(ControlledBall.debug()..initialPosition = info.eventPosition.game); final ball = ControlledBall.debug()
..initialPosition = info.eventPosition.game;
firstChild<ZCanvasComponent>()?.add(ball);
} }
} }
} }
class _DebugGameBallsController extends _GameBallsController {
_DebugGameBallsController(PinballGame game) : super(game);
}
// TODO(wolfenrain): investigate this CI failure. // TODO(wolfenrain): investigate this CI failure.
// coverage:ignore-start // coverage:ignore-start
class _DebugInformation extends Component with HasGameRef<DebugPinballGame> { class _DebugInformation extends Component with HasGameRef<DebugPinballGame> {
_DebugInformation() : super(priority: RenderPriority.debugInfo);
@override @override
PositionType get positionType => PositionType.widget; PositionType get positionType => PositionType.widget;

@ -12,7 +12,7 @@ export 'cubit/android_bumper_cubit.dart';
/// {@template android_bumper} /// {@template android_bumper}
/// Bumper for area under the [AndroidSpaceship]. /// Bumper for area under the [AndroidSpaceship].
/// {@endtemplate} /// {@endtemplate}
class AndroidBumper extends BodyComponent with InitialPosition { class AndroidBumper extends BodyComponent with InitialPosition, ZIndex {
/// {@macro android_bumper} /// {@macro android_bumper}
AndroidBumper._({ AndroidBumper._({
required double majorRadius, required double majorRadius,
@ -25,7 +25,6 @@ class AndroidBumper extends BodyComponent with InitialPosition {
}) : _majorRadius = majorRadius, }) : _majorRadius = majorRadius,
_minorRadius = minorRadius, _minorRadius = minorRadius,
super( super(
priority: RenderPriority.androidBumper,
renderBody: false, renderBody: false,
children: [ children: [
AndroidBumperBallContactBehavior(), AndroidBumperBallContactBehavior(),
@ -38,7 +37,9 @@ class AndroidBumper extends BodyComponent with InitialPosition {
), ),
...?children, ...?children,
], ],
); ) {
zIndex = ZIndexes.androidBumper;
}
/// {@macro android_bumper} /// {@macro android_bumper}
AndroidBumper.a({ AndroidBumper.a({

@ -9,21 +9,21 @@ import 'package:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets; import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
class AndroidSpaceship extends Blueprint { class AndroidSpaceship extends Component {
AndroidSpaceship({required Vector2 position}) AndroidSpaceship({required Vector2 position})
: super( : super(
components: [ children: [
_SpaceshipSaucer()..initialPosition = position, _SpaceshipSaucer()..initialPosition = position,
_SpaceshipSaucerSpriteAnimationComponent()..position = position, _SpaceshipSaucerSpriteAnimationComponent()..position = position,
_LightBeamSpriteComponent()..position = position + Vector2(2.5, 5), _LightBeamSpriteComponent()..position = position + Vector2(2.5, 5),
_AndroidHead()..initialPosition = position + Vector2(0.5, 0.25), _AndroidHead()..initialPosition = position + Vector2(0.5, 0.25),
_SpaceshipHole( _SpaceshipHole(
outsideLayer: Layer.spaceshipExitRail, outsideLayer: Layer.spaceshipExitRail,
outsidePriority: RenderPriority.ballOnSpaceshipRail, outsidePriority: ZIndexes.ballOnSpaceshipRail,
)..initialPosition = position - Vector2(5.3, -5.4), )..initialPosition = position - Vector2(5.3, -5.4),
_SpaceshipHole( _SpaceshipHole(
outsideLayer: Layer.board, outsideLayer: Layer.board,
outsidePriority: RenderPriority.ballOnBoard, outsidePriority: ZIndexes.ballOnBoard,
)..initialPosition = position - Vector2(-7.5, -1.1), )..initialPosition = position - Vector2(-7.5, -1.1),
], ],
); );
@ -65,12 +65,13 @@ class _SpaceshipSaucerShape extends ChainShape {
} }
class _SpaceshipSaucerSpriteAnimationComponent extends SpriteAnimationComponent class _SpaceshipSaucerSpriteAnimationComponent extends SpriteAnimationComponent
with HasGameRef { with HasGameRef, ZIndex {
_SpaceshipSaucerSpriteAnimationComponent() _SpaceshipSaucerSpriteAnimationComponent()
: super( : super(
anchor: Anchor.center, anchor: Anchor.center,
priority: RenderPriority.spaceshipSaucer, ) {
); zIndex = ZIndexes.spaceshipSaucer;
}
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -101,12 +102,14 @@ class _SpaceshipSaucerSpriteAnimationComponent extends SpriteAnimationComponent
} }
// TODO(allisonryan0002): add pulsing behavior. // TODO(allisonryan0002): add pulsing behavior.
class _LightBeamSpriteComponent extends SpriteComponent with HasGameRef { class _LightBeamSpriteComponent extends SpriteComponent
with HasGameRef, ZIndex {
_LightBeamSpriteComponent() _LightBeamSpriteComponent()
: super( : super(
anchor: Anchor.center, anchor: Anchor.center,
priority: RenderPriority.spaceshipLightBeam, ) {
); zIndex = ZIndexes.spaceshipLightBeam;
}
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -121,14 +124,14 @@ class _LightBeamSpriteComponent extends SpriteComponent with HasGameRef {
} }
} }
class _AndroidHead extends BodyComponent with InitialPosition, Layered { class _AndroidHead extends BodyComponent with InitialPosition, Layered, ZIndex {
_AndroidHead() _AndroidHead()
: super( : super(
priority: RenderPriority.androidHead,
children: [_AndroidHeadSpriteAnimationComponent()], children: [_AndroidHeadSpriteAnimationComponent()],
renderBody: false, renderBody: false,
) { ) {
layer = Layer.spaceship; layer = Layer.spaceship;
zIndex = ZIndexes.androidHead;
} }
@override @override
@ -191,8 +194,8 @@ class _SpaceshipHole extends LayerSensor {
insideLayer: Layer.spaceship, insideLayer: Layer.spaceship,
outsideLayer: outsideLayer, outsideLayer: outsideLayer,
orientation: LayerEntranceOrientation.down, orientation: LayerEntranceOrientation.down,
insidePriority: RenderPriority.ballOnSpaceship, insideZIndex: ZIndexes.ballOnSpaceship,
outsidePriority: outsidePriority, outsideZIndex: outsidePriority,
) { ) {
layer = Layer.spaceship; layer = Layer.spaceship;
} }

@ -6,12 +6,13 @@ import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template ball} /// {@template ball}
/// A solid, [BodyType.dynamic] sphere that rolls and bounces around. /// A solid, [BodyType.dynamic] sphere that rolls and bounces around.
/// {@endtemplate} /// {@endtemplate}
class Ball<T extends Forge2DGame> extends BodyComponent<T> class Ball<T extends Forge2DGame> extends BodyComponent<T>
with Layered, InitialPosition { with Layered, InitialPosition, ZIndex {
/// {@macro ball} /// {@macro ball}
Ball({ Ball({
required this.baseColor, required this.baseColor,
@ -133,13 +134,14 @@ class _BallSpriteComponent extends SpriteComponent with HasGameRef {
} }
class _TurboChargeSpriteAnimationComponent extends SpriteAnimationComponent class _TurboChargeSpriteAnimationComponent extends SpriteAnimationComponent
with HasGameRef { with HasGameRef, ZIndex {
_TurboChargeSpriteAnimationComponent() _TurboChargeSpriteAnimationComponent()
: super( : super(
anchor: const Anchor(0.53, 0.72), anchor: const Anchor(0.53, 0.72),
priority: RenderPriority.turboChargeFlame,
removeOnFinish: true, removeOnFinish: true,
); ) {
zIndex = ZIndexes.turboChargeFlame;
}
late final Vector2 _textureSize; late final Vector2 _textureSize;

@ -2,14 +2,17 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
class BoardBackgroundSpriteComponent extends SpriteComponent with HasGameRef { class BoardBackgroundSpriteComponent extends SpriteComponent
with HasGameRef, ZIndex {
BoardBackgroundSpriteComponent() BoardBackgroundSpriteComponent()
: super( : super(
anchor: Anchor.center, anchor: Anchor.center,
priority: RenderPriority.boardBackground,
position: Vector2(0, -1), position: Vector2(0, -1),
); ) {
zIndex = ZIndexes.boardBackground;
}
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {

@ -4,13 +4,13 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
/// {@template boundaries} /// {@template boundaries}
/// A [Blueprint] which creates the [_BottomBoundary] and [_OuterBoundary]. /// Pinball machine walls.
///{@endtemplate boundaries} /// {@endtemplate}
class Boundaries extends Blueprint { class Boundaries extends Component {
/// {@macro boundaries} /// {@macro boundaries}
Boundaries() Boundaries()
: super( : super(
components: [ children: [
_BottomBoundary(), _BottomBoundary(),
_OuterBoundary(), _OuterBoundary(),
_OuterBottomBoundarySpriteComponent(), _OuterBottomBoundarySpriteComponent(),
@ -22,14 +22,15 @@ class Boundaries extends Blueprint {
/// Curved boundary at the bottom of the board where the [Ball] exits the field /// Curved boundary at the bottom of the board where the [Ball] exits the field
/// of play. /// of play.
/// {@endtemplate bottom_boundary} /// {@endtemplate bottom_boundary}
class _BottomBoundary extends BodyComponent with InitialPosition { class _BottomBoundary extends BodyComponent with InitialPosition, ZIndex {
/// {@macro bottom_boundary} /// {@macro bottom_boundary}
_BottomBoundary() _BottomBoundary()
: super( : super(
renderBody: false, renderBody: false,
priority: RenderPriority.bottomBoundary,
children: [_BottomBoundarySpriteComponent()], children: [_BottomBoundarySpriteComponent()],
); ) {
zIndex = ZIndexes.bottomBoundary;
}
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final bottomLeftCurve = BezierCurveShape( final bottomLeftCurve = BezierCurveShape(
@ -84,17 +85,20 @@ class _BottomBoundarySpriteComponent extends SpriteComponent with HasGameRef {
} }
/// {@template outer_boundary} /// {@template outer_boundary}
/// Boundary enclosing the top and left side of the board. The right side of the /// Boundary enclosing the top and left side of the board.
/// board is closed by the barrier the [LaunchRamp] creates. ///
/// The right side of the board is closed by the barrier the [LaunchRamp]
/// creates.
/// {@endtemplate outer_boundary} /// {@endtemplate outer_boundary}
class _OuterBoundary extends BodyComponent with InitialPosition { class _OuterBoundary extends BodyComponent with InitialPosition, ZIndex {
/// {@macro outer_boundary} /// {@macro outer_boundary}
_OuterBoundary() _OuterBoundary()
: super( : super(
renderBody: false, renderBody: false,
priority: RenderPriority.outerBoundary,
children: [_OuterBoundarySpriteComponent()], children: [_OuterBoundarySpriteComponent()],
); ) {
zIndex = ZIndexes.outerBoundary;
}
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final topWall = EdgeShape() final topWall = EdgeShape()
@ -189,13 +193,14 @@ class _OuterBoundarySpriteComponent extends SpriteComponent with HasGameRef {
} }
class _OuterBottomBoundarySpriteComponent extends SpriteComponent class _OuterBottomBoundarySpriteComponent extends SpriteComponent
with HasGameRef { with HasGameRef, ZIndex {
_OuterBottomBoundarySpriteComponent() _OuterBottomBoundarySpriteComponent()
: super( : super(
priority: RenderPriority.outerBottomBoundary,
anchor: Anchor.center, anchor: Anchor.center,
position: Vector2(0, 71), position: Vector2(0, 71),
); ) {
zIndex = ZIndexes.outerBottomBoundary;
}
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart' hide Timer; import 'package:flame_forge2d/flame_forge2d.dart' hide Timer;
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template chrome_dino} /// {@template chrome_dino}
/// Dino that swivels back and forth, opening its mouth to eat a [Ball]. /// Dino that swivels back and forth, opening its mouth to eat a [Ball].
@ -10,13 +11,14 @@ import 'package:pinball_components/pinball_components.dart';
/// Upon eating a [Ball], the dino rotates and spits the [Ball] out in a /// Upon eating a [Ball], the dino rotates and spits the [Ball] out in a
/// different direction. /// different direction.
/// {@endtemplate} /// {@endtemplate}
class ChromeDino extends BodyComponent with InitialPosition { class ChromeDino extends BodyComponent with InitialPosition, ZIndex {
/// {@macro chrome_dino} /// {@macro chrome_dino}
ChromeDino() ChromeDino()
: super( : super(
priority: RenderPriority.dino,
renderBody: false, renderBody: false,
); ) {
zIndex = ZIndexes.dino;
}
/// The size of the dinosaur mouth. /// The size of the dinosaur mouth.
static final size = Vector2(5.5, 5); static final size = Vector2(5.5, 5);

@ -23,7 +23,6 @@ export 'layer.dart';
export 'layer_sensor.dart'; export 'layer_sensor.dart';
export 'multiplier/multiplier.dart'; export 'multiplier/multiplier.dart';
export 'plunger.dart'; export 'plunger.dart';
export 'render_priority.dart';
export 'rocket.dart'; export 'rocket.dart';
export 'score_text.dart'; export 'score_text.dart';
export 'shapes/shapes.dart'; export 'shapes/shapes.dart';
@ -34,3 +33,4 @@ export 'spaceship_ramp.dart';
export 'sparky_animatronic.dart'; export 'sparky_animatronic.dart';
export 'sparky_bumper/sparky_bumper.dart'; export 'sparky_bumper/sparky_bumper.dart';
export 'sparky_computer.dart'; export 'sparky_computer.dart';
export 'z_indexes.dart';

@ -6,14 +6,14 @@ import 'package:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets; import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
/// {@template dinowalls} /// {@template dino_walls}
/// A [Blueprint] which creates walls for the [ChromeDino]. /// Walls near the [ChromeDino].
/// {@endtemplate} /// {@endtemplate}
class DinoWalls extends Blueprint { class DinoWalls extends Component {
/// {@macro dinowalls} /// {@macro dino_walls}
DinoWalls() DinoWalls()
: super( : super(
components: [ children: [
_DinoTopWall(), _DinoTopWall(),
_DinoBottomWall(), _DinoBottomWall(),
], ],
@ -23,14 +23,15 @@ class DinoWalls extends Blueprint {
/// {@template dino_top_wall} /// {@template dino_top_wall}
/// Wall segment located above [ChromeDino]. /// Wall segment located above [ChromeDino].
/// {@endtemplate} /// {@endtemplate}
class _DinoTopWall extends BodyComponent with InitialPosition { class _DinoTopWall extends BodyComponent with InitialPosition, ZIndex {
///{@macro dino_top_wall} ///{@macro dino_top_wall}
_DinoTopWall() _DinoTopWall()
: super( : super(
priority: RenderPriority.dinoTopWall,
children: [_DinoTopWallSpriteComponent()], children: [_DinoTopWallSpriteComponent()],
renderBody: false, renderBody: false,
); ) {
zIndex = ZIndexes.dinoTopWall;
}
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final topStraightShape = EdgeShape() final topStraightShape = EdgeShape()
@ -116,14 +117,15 @@ class _DinoTopWallSpriteComponent extends SpriteComponent with HasGameRef {
/// {@template dino_bottom_wall} /// {@template dino_bottom_wall}
/// Wall segment located below [ChromeDino]. /// Wall segment located below [ChromeDino].
/// {@endtemplate} /// {@endtemplate}
class _DinoBottomWall extends BodyComponent with InitialPosition { class _DinoBottomWall extends BodyComponent with InitialPosition, ZIndex {
///{@macro dino_top_wall} ///{@macro dino_top_wall}
_DinoBottomWall() _DinoBottomWall()
: super( : super(
priority: RenderPriority.dinoBottomWall,
children: [_DinoBottomWallSpriteComponent()], children: [_DinoBottomWallSpriteComponent()],
renderBody: false, renderBody: false,
); ) {
zIndex = ZIndexes.dinoBottomWall;
}
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final topStraightShape = EdgeShape() final topStraightShape = EdgeShape()

@ -26,10 +26,8 @@ class FireEffect extends ParticleSystemComponent {
required this.burstPower, required this.burstPower,
required this.direction, required this.direction,
Vector2? position, Vector2? position,
int? priority,
}) : super( }) : super(
position: position, position: position,
priority: priority,
); );
/// A [double] value that will define how "strong" the burst of particles /// A [double] value that will define how "strong" the burst of particles

@ -8,14 +8,13 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
/// {@template launch_ramp} /// {@template launch_ramp}
/// A [Blueprint] which creates the [_LaunchRampBase] and /// Ramp where the ball is launched from.
/// [_LaunchRampForegroundRailing].
/// {@endtemplate} /// {@endtemplate}
class LaunchRamp extends Blueprint { class LaunchRamp extends Component {
/// {@macro launch_ramp} /// {@macro launch_ramp}
LaunchRamp() LaunchRamp()
: super( : super(
components: [ children: [
_LaunchRampBase(), _LaunchRampBase(),
_LaunchRampForegroundRailing(), _LaunchRampForegroundRailing(),
_LaunchRampExit()..initialPosition = Vector2(0.6, -34), _LaunchRampExit()..initialPosition = Vector2(0.6, -34),
@ -24,20 +23,16 @@ class LaunchRamp extends Blueprint {
); );
} }
/// {@template launch_ramp_base} class _LaunchRampBase extends BodyComponent with Layered, ZIndex {
/// Ramp the [Ball] is launched from at the beginning of each ball life.
/// {@endtemplate}
class _LaunchRampBase extends BodyComponent with Layered {
/// {@macro launch_ramp_base}
_LaunchRampBase() _LaunchRampBase()
: super( : super(
priority: RenderPriority.launchRamp,
renderBody: false, renderBody: false,
children: [ children: [
_LaunchRampBackgroundRailingSpriteComponent(), _LaunchRampBackgroundRailingSpriteComponent(),
_LaunchRampBaseSpriteComponent(), _LaunchRampBaseSpriteComponent(),
], ],
) { ) {
zIndex = ZIndexes.launchRamp;
layer = Layer.launcher; layer = Layer.launcher;
} }
@ -140,18 +135,14 @@ class _LaunchRampBackgroundRailingSpriteComponent extends SpriteComponent
} }
} }
/// {@template launch_ramp_foreground_railing} class _LaunchRampForegroundRailing extends BodyComponent with ZIndex {
/// Foreground railing for the [_LaunchRampBase] to render in front of the
/// [Ball].
/// {@endtemplate}
class _LaunchRampForegroundRailing extends BodyComponent {
/// {@macro launch_ramp_foreground_railing}
_LaunchRampForegroundRailing() _LaunchRampForegroundRailing()
: super( : super(
priority: RenderPriority.launchRampForegroundRailing,
children: [_LaunchRampForegroundRailingSpriteComponent()], children: [_LaunchRampForegroundRailingSpriteComponent()],
renderBody: false, renderBody: false,
); ) {
zIndex = ZIndexes.launchRampForegroundRailing;
}
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[]; final fixturesDef = <FixtureDef>[];
@ -239,8 +230,8 @@ class _LaunchRampExit extends LayerSensor {
insideLayer: Layer.launcher, insideLayer: Layer.launcher,
outsideLayer: Layer.board, outsideLayer: Layer.board,
orientation: LayerEntranceOrientation.down, orientation: LayerEntranceOrientation.down,
insidePriority: RenderPriority.ballOnLaunchRamp, insideZIndex: ZIndexes.ballOnLaunchRamp,
outsidePriority: RenderPriority.ballOnBoard, outsideZIndex: ZIndexes.ballOnBoard,
) { ) {
layer = Layer.launcher; layer = Layer.launcher;
} }

@ -8,9 +8,6 @@ import 'package:flutter/material.dart';
/// [BodyComponent]s with compatible [Layer]s can collide with each other, /// [BodyComponent]s with compatible [Layer]s can collide with each other,
/// ignoring others. This compatibility depends on bit masking operation /// ignoring others. This compatibility depends on bit masking operation
/// between layers. For more information read: https://en.wikipedia.org/wiki/Mask_(computing). /// between layers. For more information read: https://en.wikipedia.org/wiki/Mask_(computing).
///
/// A parent [Layered] have priority against its children's layer. Them won't be
/// changed but will be ignored.
/// {@endtemplate} /// {@endtemplate}
mixin Layered<T extends Forge2DGame> on BodyComponent<T> { mixin Layered<T extends Forge2DGame> on BodyComponent<T> {
Layer _layer = Layer.all; Layer _layer = Layer.all;

@ -18,7 +18,7 @@ enum LayerEntranceOrientation {
/// [BodyComponent] located at the entrance and exit of a [Layer]. /// [BodyComponent] located at the entrance and exit of a [Layer].
/// ///
/// By default the base [layer] is set to [Layer.board] and the /// By default the base [layer] is set to [Layer.board] and the
/// [outsidePriority] is set to the lowest possible [Layer]. /// [_outsideZIndex] is set to [ZIndexes.ballOnBoard].
/// {@endtemplate} /// {@endtemplate}
abstract class LayerSensor extends BodyComponent abstract class LayerSensor extends BodyComponent
with InitialPosition, Layered, ContactCallbacks { with InitialPosition, Layered, ContactCallbacks {
@ -26,32 +26,21 @@ abstract class LayerSensor extends BodyComponent
LayerSensor({ LayerSensor({
required Layer insideLayer, required Layer insideLayer,
Layer? outsideLayer, Layer? outsideLayer,
required int insidePriority, required int insideZIndex,
int? outsidePriority, int? outsideZIndex,
required this.orientation, required this.orientation,
}) : _insideLayer = insideLayer, }) : _insideLayer = insideLayer,
_outsideLayer = outsideLayer ?? Layer.board, _outsideLayer = outsideLayer ?? Layer.board,
_insidePriority = insidePriority, _insideZIndex = insideZIndex,
_outsidePriority = outsidePriority ?? RenderPriority.ballOnBoard, _outsideZIndex = outsideZIndex ?? ZIndexes.ballOnBoard,
super(renderBody: false) { super(renderBody: false) {
layer = Layer.opening; layer = Layer.opening;
} }
final Layer _insideLayer; final Layer _insideLayer;
final Layer _outsideLayer; final Layer _outsideLayer;
final int _insidePriority; final int _insideZIndex;
final int _outsidePriority; final int _outsideZIndex;
/// Mask bits value for collisions on [Layer].
Layer get insideLayer => _insideLayer;
/// Mask bits value for collisions outside of [Layer].
Layer get outsideLayer => _outsideLayer;
/// Render priority for the [Ball] on [Layer].
int get insidePriority => _insidePriority;
/// Render priority for the [Ball] outside of [Layer].
int get outsidePriority => _outsidePriority;
/// The [Shape] of the [LayerSensor]. /// The [Shape] of the [LayerSensor].
Shape get shape; Shape get shape;
@ -80,7 +69,7 @@ abstract class LayerSensor extends BodyComponent
super.beginContact(other, contact); super.beginContact(other, contact);
if (other is! Ball) return; if (other is! Ball) return;
if (other.layer != insideLayer) { if (other.layer != _insideLayer) {
final isBallEnteringOpening = final isBallEnteringOpening =
(orientation == LayerEntranceOrientation.down && (orientation == LayerEntranceOrientation.down &&
other.body.linearVelocity.y < 0) || other.body.linearVelocity.y < 0) ||
@ -89,15 +78,13 @@ abstract class LayerSensor extends BodyComponent
if (isBallEnteringOpening) { if (isBallEnteringOpening) {
other other
..layer = insideLayer ..layer = _insideLayer
..priority = insidePriority ..zIndex = _insideZIndex;
..reorderChildren();
} }
} else { } else {
other other
..layer = outsideLayer ..layer = _outsideLayer
..priority = outsidePriority ..zIndex = _outsideZIndex;
..reorderChildren();
} }
} }
} }

@ -1,6 +1,7 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template plunger} /// {@template plunger}
/// [Plunger] serves as a spring, that shoots the ball on the right side of the /// [Plunger] serves as a spring, that shoots the ball on the right side of the
@ -8,16 +9,12 @@ import 'package:pinball_components/pinball_components.dart';
/// ///
/// [Plunger] ignores gravity so the player controls its downward [pull]. /// [Plunger] ignores gravity so the player controls its downward [pull].
/// {@endtemplate} /// {@endtemplate}
class Plunger extends BodyComponent with InitialPosition, Layered { class Plunger extends BodyComponent with InitialPosition, Layered, ZIndex {
/// {@macro plunger} /// {@macro plunger}
Plunger({ Plunger({
required this.compressionDistance, required this.compressionDistance,
// TODO(ruimiguel): set to priority +1 over LaunchRamp once all priorities }) : super(renderBody: false) {
// are fixed. zIndex = ZIndexes.plunger;
}) : super(
priority: RenderPriority.plunger,
renderBody: false,
) {
layer = Layer.launcher; layer = Layer.launcher;
} }

@ -1,119 +0,0 @@
// ignore_for_file: public_member_api_docs
import 'package:pinball_components/pinball_components.dart';
/// {@template render_priority}
/// Priorities for the component rendering order in the pinball game.
/// {@endtemplate}
// TODO(allisonryan0002): find alternative to section comments.
abstract class RenderPriority {
static const _base = 0;
static const _above = 1;
static const _below = -1;
// Ball
/// Render priority for the [Ball] while it's on the board.
static const int ballOnBoard = _base;
/// Render priority for the [Ball] while it's on the [SpaceshipRamp].
static const int ballOnSpaceshipRamp =
_above + spaceshipRampBackgroundRailing;
/// Render priority for the [Ball] while it's on the [AndroidSpaceship].
static const int ballOnSpaceship = _above + spaceshipSaucer;
/// Render priority for the [Ball] while it's on the [SpaceshipRail].
static const int ballOnSpaceshipRail = _above + spaceshipRail;
/// Render priority for the [Ball] while it's on the [LaunchRamp].
static const int ballOnLaunchRamp = launchRamp;
// Background
// TODO(allisonryan0002): fix this magic priority. Could bump all priorities
// so there are no negatives.
static const int boardBackground = 3 * _below + _base;
// Boundaries
static const int bottomBoundary = _above + dinoBottomWall;
static const int outerBoundary = _above + boardBackground;
static const int outerBottomBoundary = _above + rocket;
// Bottom Group
static const int bottomGroup = _above + ballOnBoard;
// Launcher
static const int launchRamp = _above + outerBoundary;
static const int launchRampForegroundRailing = ballOnBoard;
static const int plunger = _above + launchRamp;
static const int rocket = _below + bottomBoundary;
// Dino Desert
static const int dinoTopWall = _above + ballOnBoard;
static const int dino = _above + dinoTopWall;
static const int dinoBottomWall = _above + dino;
static const int slingshot = _above + dinoBottomWall;
// Flutter Forest
static const int flutterForest = _above + launchRampForegroundRailing;
// Sparky Scorch
static const int computerBase = _below + ballOnBoard;
static const int computerTop = _above + ballOnBoard;
static const int computerGlow = _above + ballOnBoard;
static const int sparkyAnimatronic = _above + spaceshipRampForegroundRailing;
static const int sparkyBumper = _above + ballOnBoard;
static const int turboChargeFlame = _above + ballOnBoard;
// Android Acres
static const int spaceshipRail = _above + bottomGroup;
static const int spaceshipRailExit = _above + ballOnSpaceshipRail;
static const int spaceshipSaucer = _above + ballOnSpaceshipRail;
static const int spaceshipLightBeam = _below + spaceshipSaucer;
static const int androidHead = _above + spaceshipSaucer;
static const int spaceshipRamp = _above + ballOnBoard;
static const int spaceshipRampBackgroundRailing = _above + spaceshipRamp;
static const int spaceshipRampArrow = _above + spaceshipRamp;
static const int spaceshipRampForegroundRailing =
_above + ballOnSpaceshipRamp;
static const int spaceshipRampBoardOpening = _below + ballOnBoard;
static const int androidBumper = _above + ballOnBoard;
// Score Text
static const int scoreText = _above + spaceshipRampForegroundRailing;
// Debug information
static const int debugInfo = _above + scoreText;
}

@ -1,17 +1,16 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:pinball_components/gen/assets.gen.dart'; import 'package:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets; import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template rocket_sprite_component} /// {@template rocket_sprite_component}
/// A [SpriteComponent] for the rocket over [Plunger]. /// A [SpriteComponent] for the rocket over [Plunger].
/// {@endtemplate} /// {@endtemplate}
class RocketSpriteComponent extends SpriteComponent with HasGameRef { class RocketSpriteComponent extends SpriteComponent with HasGameRef, ZIndex {
/// {@macro rocket_sprite_component} /// {@macro rocket_sprite_component}
RocketSpriteComponent() RocketSpriteComponent() : super(anchor: Anchor.center) {
: super( zIndex = ZIndexes.rocket;
priority: RenderPriority.rocket, }
anchor: Anchor.center,
);
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {

@ -4,11 +4,12 @@ import 'package:flame/components.dart';
import 'package:flame/effects.dart'; import 'package:flame/effects.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template score_text} /// {@template score_text}
/// A [TextComponent] that spawns at a given [position] with a moving animation. /// A [TextComponent] that spawns at a given [position] with a moving animation.
/// {@endtemplate} /// {@endtemplate}
class ScoreText extends TextComponent { class ScoreText extends TextComponent with ZIndex {
/// {@macro score_text} /// {@macro score_text}
ScoreText({ ScoreText({
required String text, required String text,
@ -18,8 +19,9 @@ class ScoreText extends TextComponent {
text: text, text: text,
position: position, position: position,
anchor: Anchor.center, anchor: Anchor.center,
priority: RenderPriority.scoreText, ) {
); zIndex = ZIndexes.scoreText;
}
late final Effect _effect; late final Effect _effect;

@ -4,14 +4,13 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
/// {@template slingshots} /// {@template slingshots}
/// A [Blueprint] which creates the pair of [Slingshot]s on the right side of /// A collection of [Slingshot]s.
/// the board.
/// {@endtemplate} /// {@endtemplate}
class Slingshots extends Blueprint { class Slingshots extends Component with ZIndex {
/// {@macro slingshots} /// {@macro slingshots}
Slingshots() Slingshots()
: super( : super(
components: [ children: [
Slingshot( Slingshot(
length: 5.64, length: 5.64,
angle: -0.017, angle: -0.017,
@ -23,7 +22,9 @@ class Slingshots extends Blueprint {
spritePath: Assets.images.slingshot.lower.keyName, spritePath: Assets.images.slingshot.lower.keyName,
)..initialPosition = Vector2(24.7, 6.2), )..initialPosition = Vector2(24.7, 6.2),
], ],
); ) {
zIndex = ZIndexes.slingshots;
}
} }
/// {@template slingshot} /// {@template slingshot}
@ -38,7 +39,6 @@ class Slingshot extends BodyComponent with InitialPosition {
}) : _length = length, }) : _length = length,
_angle = angle, _angle = angle,
super( super(
priority: RenderPriority.slingshot,
children: [_SlinghsotSpriteComponent(spritePath, angle: angle)], children: [_SlinghsotSpriteComponent(spritePath, angle: angle)],
renderBody: false, renderBody: false,
); );

@ -6,13 +6,13 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_rail} /// {@template spaceship_rail}
/// A [Blueprint] for the rail exiting the [AndroidSpaceship]. /// Rail exiting the [AndroidSpaceship].
/// {@endtemplate} /// {@endtemplate}
class SpaceshipRail extends Blueprint { class SpaceshipRail extends Component {
/// {@macro spaceship_rail} /// {@macro spaceship_rail}
SpaceshipRail() SpaceshipRail()
: super( : super(
components: [ children: [
_SpaceshipRail(), _SpaceshipRail(),
_SpaceshipRailExit(), _SpaceshipRailExit(),
_SpaceshipRailExitSpriteComponent() _SpaceshipRailExitSpriteComponent()
@ -20,14 +20,14 @@ class SpaceshipRail extends Blueprint {
); );
} }
class _SpaceshipRail extends BodyComponent with Layered { class _SpaceshipRail extends BodyComponent with Layered, ZIndex {
_SpaceshipRail() _SpaceshipRail()
: super( : super(
priority: RenderPriority.spaceshipRail,
children: [_SpaceshipRailSpriteComponent()], children: [_SpaceshipRailSpriteComponent()],
renderBody: false, renderBody: false,
) { ) {
layer = Layer.spaceshipExitRail; layer = Layer.spaceshipExitRail;
zIndex = ZIndexes.spaceshipRail;
} }
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
@ -125,13 +125,14 @@ class _SpaceshipRailSpriteComponent extends SpriteComponent with HasGameRef {
} }
class _SpaceshipRailExitSpriteComponent extends SpriteComponent class _SpaceshipRailExitSpriteComponent extends SpriteComponent
with HasGameRef { with HasGameRef, ZIndex {
_SpaceshipRailExitSpriteComponent() _SpaceshipRailExitSpriteComponent()
: super( : super(
anchor: Anchor.center, anchor: Anchor.center,
position: Vector2(-28, 19.4), position: Vector2(-28, 19.4),
priority: RenderPriority.spaceshipRailExit, ) {
); zIndex = ZIndexes.spaceshipRailExit;
}
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -152,7 +153,7 @@ class _SpaceshipRailExit extends LayerSensor {
: super( : super(
orientation: LayerEntranceOrientation.down, orientation: LayerEntranceOrientation.down,
insideLayer: Layer.spaceshipExitRail, insideLayer: Layer.spaceshipExitRail,
insidePriority: RenderPriority.ballOnSpaceshipRail, insideZIndex: ZIndexes.ballOnSpaceshipRail,
) { ) {
layer = Layer.spaceshipExitRail; layer = Layer.spaceshipExitRail;
} }

@ -8,22 +8,22 @@ import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_ramp} /// {@template spaceship_ramp}
/// A [Blueprint] which creates the ramp leading into the [AndroidSpaceship]. /// Ramp leading into the [AndroidSpaceship].
/// {@endtemplate} /// {@endtemplate}
class SpaceshipRamp extends Blueprint { class SpaceshipRamp extends Component {
/// {@macro spaceship_ramp} /// {@macro spaceship_ramp}
SpaceshipRamp() SpaceshipRamp()
: super( : super(
components: [ children: [
_SpaceshipRampOpening( _SpaceshipRampOpening(
outsidePriority: RenderPriority.ballOnBoard, outsidePriority: ZIndexes.ballOnBoard,
rotation: math.pi, rotation: math.pi,
) )
..initialPosition = Vector2(1.7, -19.8) ..initialPosition = Vector2(1.7, -19.8)
..layer = Layer.opening, ..layer = Layer.opening,
_SpaceshipRampOpening( _SpaceshipRampOpening(
outsideLayer: Layer.spaceship, outsideLayer: Layer.spaceship,
outsidePriority: RenderPriority.ballOnSpaceship, outsidePriority: ZIndexes.ballOnSpaceship,
rotation: math.pi, rotation: math.pi,
) )
..initialPosition = Vector2(-13.7, -18.6) ..initialPosition = Vector2(-13.7, -18.6)
@ -41,10 +41,8 @@ class SpaceshipRamp extends Blueprint {
/// Forwards the sprite to the next [SpaceshipRampArrowSpriteState]. /// Forwards the sprite to the next [SpaceshipRampArrowSpriteState].
/// ///
/// If the current state is the last one it cycles back to the initial state. /// If the current state is the last one it cycles back to the initial state.
void progress() => components void progress() =>
.whereType<_SpaceshipRampArrowSpriteComponent>() firstChild<_SpaceshipRampArrowSpriteComponent>()?.progress();
.first
.progress();
} }
/// Indicates the state of the arrow on the [SpaceshipRamp]. /// Indicates the state of the arrow on the [SpaceshipRamp].
@ -94,16 +92,16 @@ extension on SpaceshipRampArrowSpriteState {
} }
class _SpaceshipRampBackground extends BodyComponent class _SpaceshipRampBackground extends BodyComponent
with InitialPosition, Layered { with InitialPosition, Layered, ZIndex {
_SpaceshipRampBackground() _SpaceshipRampBackground()
: super( : super(
priority: RenderPriority.spaceshipRamp,
renderBody: false, renderBody: false,
children: [ children: [
_SpaceshipRampBackgroundRampSpriteComponent(), _SpaceshipRampBackgroundRampSpriteComponent(),
], ],
) { ) {
layer = Layer.spaceshipEntranceRamp; layer = Layer.spaceshipEntranceRamp;
zIndex = ZIndexes.spaceshipRamp;
} }
/// Width between walls of the ramp. /// Width between walls of the ramp.
@ -148,13 +146,14 @@ class _SpaceshipRampBackground extends BodyComponent
} }
class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent
with HasGameRef { with HasGameRef, ZIndex {
_SpaceshipRampBackgroundRailingSpriteComponent() _SpaceshipRampBackgroundRailingSpriteComponent()
: super( : super(
anchor: Anchor.center, anchor: Anchor.center,
position: Vector2(-11.7, -54.3), position: Vector2(-11.7, -54.3),
priority: RenderPriority.spaceshipRampBackgroundRailing, ) {
); zIndex = ZIndexes.spaceshipRampBackgroundRailing;
}
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -197,14 +196,15 @@ class _SpaceshipRampBackgroundRampSpriteComponent extends SpriteComponent
/// {@endtemplate} /// {@endtemplate}
class _SpaceshipRampArrowSpriteComponent class _SpaceshipRampArrowSpriteComponent
extends SpriteGroupComponent<SpaceshipRampArrowSpriteState> extends SpriteGroupComponent<SpaceshipRampArrowSpriteState>
with HasGameRef { with HasGameRef, ZIndex {
/// {@macro spaceship_ramp_arrow_sprite_component} /// {@macro spaceship_ramp_arrow_sprite_component}
_SpaceshipRampArrowSpriteComponent() _SpaceshipRampArrowSpriteComponent()
: super( : super(
anchor: Anchor.center, anchor: Anchor.center,
position: Vector2(-3.9, -56.5), position: Vector2(-3.9, -56.5),
priority: RenderPriority.spaceshipRampArrow, ) {
); zIndex = ZIndexes.spaceshipRampArrow;
}
/// Changes arrow image to the next [Sprite]. /// Changes arrow image to the next [Sprite].
void progress() => current = current?.next; void progress() => current = current?.next;
@ -226,8 +226,10 @@ class _SpaceshipRampArrowSpriteComponent
} }
class _SpaceshipRampBoardOpeningSpriteComponent extends SpriteComponent class _SpaceshipRampBoardOpeningSpriteComponent extends SpriteComponent
with HasGameRef { with HasGameRef, ZIndex {
_SpaceshipRampBoardOpeningSpriteComponent() : super(anchor: Anchor.center); _SpaceshipRampBoardOpeningSpriteComponent() : super(anchor: Anchor.center) {
zIndex = ZIndexes.spaceshipRampBoardOpening;
}
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -243,14 +245,14 @@ class _SpaceshipRampBoardOpeningSpriteComponent extends SpriteComponent
} }
class _SpaceshipRampForegroundRailing extends BodyComponent class _SpaceshipRampForegroundRailing extends BodyComponent
with InitialPosition, Layered { with InitialPosition, Layered, ZIndex {
_SpaceshipRampForegroundRailing() _SpaceshipRampForegroundRailing()
: super( : super(
priority: RenderPriority.spaceshipRampForegroundRailing,
renderBody: false, renderBody: false,
children: [_SpaceshipRampForegroundRailingSpriteComponent()], children: [_SpaceshipRampForegroundRailingSpriteComponent()],
) { ) {
layer = Layer.spaceshipEntranceRamp; layer = Layer.spaceshipEntranceRamp;
zIndex = ZIndexes.spaceshipRampForegroundRailing;
} }
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
@ -352,8 +354,8 @@ class _SpaceshipRampOpening extends LayerSensor {
insideLayer: Layer.spaceshipEntranceRamp, insideLayer: Layer.spaceshipEntranceRamp,
outsideLayer: outsideLayer, outsideLayer: outsideLayer,
orientation: LayerEntranceOrientation.down, orientation: LayerEntranceOrientation.down,
insidePriority: RenderPriority.ballOnSpaceshipRamp, insideZIndex: ZIndexes.ballOnSpaceshipRamp,
outsidePriority: outsidePriority, outsideZIndex: outsidePriority,
); );
final double _rotation; final double _rotation;

@ -1,17 +1,20 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_animatronic} /// {@template sparky_animatronic}
/// Animated Sparky that sits on top of the [SparkyComputer]. /// Animated Sparky that sits on top of the [SparkyComputer].
/// {@endtemplate} /// {@endtemplate}
class SparkyAnimatronic extends SpriteAnimationComponent with HasGameRef { class SparkyAnimatronic extends SpriteAnimationComponent
with HasGameRef, ZIndex {
/// {@macro sparky_animatronic} /// {@macro sparky_animatronic}
SparkyAnimatronic() SparkyAnimatronic()
: super( : super(
anchor: Anchor.center, anchor: Anchor.center,
playing: false, playing: false,
priority: RenderPriority.sparkyAnimatronic, ) {
); zIndex = ZIndexes.sparkyAnimatronic;
}
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {

@ -12,7 +12,7 @@ export 'cubit/sparky_bumper_cubit.dart';
/// {@template sparky_bumper} /// {@template sparky_bumper}
/// Bumper for Sparky area. /// Bumper for Sparky area.
/// {@endtemplate} /// {@endtemplate}
class SparkyBumper extends BodyComponent with InitialPosition { class SparkyBumper extends BodyComponent with InitialPosition, ZIndex {
/// {@macro sparky_bumper} /// {@macro sparky_bumper}
SparkyBumper._({ SparkyBumper._({
required double majorRadius, required double majorRadius,
@ -25,7 +25,6 @@ class SparkyBumper extends BodyComponent with InitialPosition {
}) : _majorRadius = majorRadius, }) : _majorRadius = majorRadius,
_minorRadius = minorRadius, _minorRadius = minorRadius,
super( super(
priority: RenderPriority.sparkyBumper,
renderBody: false, renderBody: false,
children: [ children: [
SparkyBumperBallContactBehavior(), SparkyBumperBallContactBehavior(),
@ -38,7 +37,9 @@ class SparkyBumper extends BodyComponent with InitialPosition {
), ),
...?children, ...?children,
], ],
); ) {
zIndex = ZIndexes.sparkyBumper;
}
/// {@macro sparky_bumper} /// {@macro sparky_bumper}
SparkyBumper.a({ SparkyBumper.a({

@ -8,11 +8,11 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_computer} /// {@template sparky_computer}
/// A computer owned by Sparky. /// A computer owned by Sparky.
/// {@endtemplate} /// {@endtemplate}
class SparkyComputer extends Blueprint { class SparkyComputer extends Component {
/// {@macro sparky_computer} /// {@macro sparky_computer}
SparkyComputer() SparkyComputer()
: super( : super(
components: [ children: [
_ComputerBase(), _ComputerBase(),
_ComputerTopSpriteComponent(), _ComputerTopSpriteComponent(),
_ComputerGlowSpriteComponent(), _ComputerGlowSpriteComponent(),
@ -20,13 +20,14 @@ class SparkyComputer extends Blueprint {
); );
} }
class _ComputerBase extends BodyComponent with InitialPosition { class _ComputerBase extends BodyComponent with InitialPosition, ZIndex {
_ComputerBase() _ComputerBase()
: super( : super(
priority: RenderPriority.computerBase,
renderBody: false, renderBody: false,
children: [_ComputerBaseSpriteComponent()], children: [_ComputerBaseSpriteComponent()],
); ) {
zIndex = ZIndexes.computerBase;
}
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final leftEdge = EdgeShape() final leftEdge = EdgeShape()
@ -83,13 +84,15 @@ class _ComputerBaseSpriteComponent extends SpriteComponent with HasGameRef {
} }
} }
class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef { class _ComputerTopSpriteComponent extends SpriteComponent
with HasGameRef, ZIndex {
_ComputerTopSpriteComponent() _ComputerTopSpriteComponent()
: super( : super(
anchor: Anchor.center, anchor: Anchor.center,
position: Vector2(-12.52, -49.37), position: Vector2(-12.52, -49.37),
priority: RenderPriority.computerTop, ) {
); zIndex = ZIndexes.computerTop;
}
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -105,13 +108,15 @@ class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef {
} }
} }
class _ComputerGlowSpriteComponent extends SpriteComponent with HasGameRef { class _ComputerGlowSpriteComponent extends SpriteComponent
with HasGameRef, ZIndex {
_ComputerGlowSpriteComponent() _ComputerGlowSpriteComponent()
: super( : super(
anchor: Anchor.center, anchor: Anchor.center,
position: Vector2(7.4, 10), position: Vector2(7.4, 10),
priority: RenderPriority.computerGlow, ) {
); zIndex = ZIndexes.computerGlow;
}
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {

@ -0,0 +1,110 @@
// ignore_for_file: public_member_api_docs
/// Z-Indexes for the component rendering order in the pinball game.
// TODO(allisonryan0002): find alternative to section comments.
abstract class ZIndexes {
static const _base = 0;
static const _above = 1;
static const _below = -1;
// Ball
static const ballOnBoard = _base;
static const ballOnSpaceshipRamp = _above + spaceshipRampBackgroundRailing;
static const ballOnSpaceship = _above + spaceshipSaucer;
static const ballOnSpaceshipRail = _above + spaceshipRail;
static const ballOnLaunchRamp = _above + launchRamp;
// Background
// TODO(allisonryan0002): fix this magic zindex. Could bump all priorities so
// there are no negatives.
static const boardBackground = 3 * _below + _base;
static const decal = _above + boardBackground;
// Boundaries
static const bottomBoundary = _above + dinoBottomWall;
static const outerBoundary = _above + boardBackground;
static const outerBottomBoundary = _above + rocket;
// Bottom Group
static const bottomGroup = _above + ballOnBoard;
// Launcher
static const launchRamp = _above + outerBoundary;
static const launchRampForegroundRailing = _above + ballOnLaunchRamp;
static const plunger = _above + launchRamp;
static const rocket = _below + bottomBoundary;
// Dino Desert
static const dinoTopWall = _above + ballOnBoard;
static const dino = _above + dinoTopWall;
static const dinoBottomWall = _above + dino;
static const slingshots = _above + dinoBottomWall;
// Flutter Forest
static const flutterForest = _above + launchRampForegroundRailing;
// Sparky Scorch
static const computerBase = _below + ballOnBoard;
static const computerTop = _above + ballOnBoard;
static const computerGlow = _above + ballOnBoard;
static const sparkyAnimatronic = _above + spaceshipRampForegroundRailing;
static const sparkyBumper = _above + ballOnBoard;
static const turboChargeFlame = _above + ballOnBoard;
// Android Acres
static const spaceshipRail = _above + bottomGroup;
static const spaceshipRailExit = _above + ballOnSpaceshipRail;
static const spaceshipSaucer = _above + ballOnSpaceshipRail;
static const spaceshipLightBeam = _below + spaceshipSaucer;
static const androidHead = _above + ballOnSpaceship;
static const spaceshipRamp = _above + sparkyBumper;
static const spaceshipRampBackgroundRailing = _above + spaceshipRamp;
static const spaceshipRampArrow = _above + spaceshipRamp;
static const spaceshipRampForegroundRailing = _above + ballOnSpaceshipRamp;
static const spaceshipRampBoardOpening = _below + ballOnBoard;
static const androidBumper = _above + ballOnBoard;
// Score Text
static const scoreText = _above + spaceshipRampForegroundRailing;
// Debug information
static const debugInfo = _above + scoreText;
}

@ -2,13 +2,12 @@ import 'dart:async';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class AndroidSpaceshipGame extends BallGame { class AndroidSpaceshipGame extends BallGame {
AndroidSpaceshipGame() AndroidSpaceshipGame()
: super( : super(
ballPriority: RenderPriority.ballOnSpaceship, ballPriority: ZIndexes.ballOnSpaceship,
ballLayer: Layer.spaceship, ballLayer: Layer.spaceship,
imagesFileNames: [ imagesFileNames: [
Assets.images.android.spaceship.saucer.keyName, Assets.images.android.spaceship.saucer.keyName,
@ -29,7 +28,7 @@ class AndroidSpaceshipGame extends BallGame {
await super.onLoad(); await super.onLoad();
camera.followVector2(Vector2.zero()); camera.followVector2(Vector2.zero());
await addFromBlueprint( await add(
AndroidSpaceship(position: Vector2.zero()), AndroidSpaceship(position: Vector2.zero()),
); );

@ -3,14 +3,13 @@ import 'dart:async';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SpaceshipRailGame extends BallGame { class SpaceshipRailGame extends BallGame {
SpaceshipRailGame() SpaceshipRailGame()
: super( : super(
color: Colors.blue, color: Colors.blue,
ballPriority: RenderPriority.ballOnSpaceshipRail, ballPriority: ZIndexes.ballOnSpaceshipRail,
ballLayer: Layer.spaceshipExitRail, ballLayer: Layer.spaceshipExitRail,
imagesFileNames: [ imagesFileNames: [
Assets.images.android.rail.main.keyName, Assets.images.android.rail.main.keyName,
@ -30,7 +29,7 @@ class SpaceshipRailGame extends BallGame {
await super.onLoad(); await super.onLoad();
camera.followVector2(Vector2(-30, -10)); camera.followVector2(Vector2(-30, -10));
await addFromBlueprint(SpaceshipRail()); await add(SpaceshipRail());
await ready(); await ready();
await traceAllBodies(); await traceAllBodies();
} }

@ -4,14 +4,13 @@ import 'package:flame/input.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SpaceshipRampGame extends BallGame with KeyboardEvents { class SpaceshipRampGame extends BallGame with KeyboardEvents {
SpaceshipRampGame() SpaceshipRampGame()
: super( : super(
color: Colors.blue, color: Colors.blue,
ballPriority: RenderPriority.ballOnSpaceshipRamp, ballPriority: ZIndexes.ballOnSpaceshipRamp,
ballLayer: Layer.spaceshipEntranceRamp, ballLayer: Layer.spaceshipEntranceRamp,
imagesFileNames: [ imagesFileNames: [
Assets.images.android.ramp.railingBackground.keyName, Assets.images.android.ramp.railingBackground.keyName,
@ -42,7 +41,7 @@ class SpaceshipRampGame extends BallGame with KeyboardEvents {
await super.onLoad(); await super.onLoad();
camera.followVector2(Vector2(-12, -50)); camera.followVector2(Vector2(-12, -50));
await addFromBlueprint( await add(
_spaceshipRamp = SpaceshipRamp(), _spaceshipRamp = SpaceshipRamp(),
); );
await traceAllBodies(); await traceAllBodies();

@ -1,6 +1,5 @@
import 'package:flame/extensions.dart'; import 'package:flame/extensions.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class BoundariesGame extends BallGame { class BoundariesGame extends BallGame {
@ -27,7 +26,7 @@ class BoundariesGame extends BallGame {
camera camera
..followVector2(Vector2.zero()) ..followVector2(Vector2.zero())
..zoom = 6; ..zoom = 6;
await addFromBlueprint(Boundaries()); await add(Boundaries());
await ready(); await ready();
await traceAllBodies(); await traceAllBodies();
} }

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class DinoWallGame extends BallGame { class DinoWallGame extends BallGame {
@ -24,7 +23,7 @@ class DinoWallGame extends BallGame {
Assets.images.dino.bottomWall.keyName, Assets.images.dino.bottomWall.keyName,
]); ]);
await addFromBlueprint(DinoWalls()); await add(DinoWalls());
camera.followVector2(Vector2.zero()); camera.followVector2(Vector2.zero());
await traceAllBodies(); await traceAllBodies();
} }

@ -3,14 +3,13 @@ import 'dart:async';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class LaunchRampGame extends BallGame { class LaunchRampGame extends BallGame {
LaunchRampGame() LaunchRampGame()
: super( : super(
color: Colors.blue, color: Colors.blue,
ballPriority: RenderPriority.ballOnLaunchRamp, ballPriority: ZIndexes.ballOnLaunchRamp,
ballLayer: Layer.launcher, ballLayer: Layer.launcher,
); );
@ -28,7 +27,7 @@ class LaunchRampGame extends BallGame {
camera camera
..followVector2(Vector2.zero()) ..followVector2(Vector2.zero())
..zoom = 7.5; ..zoom = 7.5;
await addFromBlueprint(LaunchRamp()); await add(LaunchRamp());
await ready(); await ready();
await traceAllBodies(); await traceAllBodies();
} }

@ -1,6 +1,5 @@
import 'package:flame/extensions.dart'; import 'package:flame/extensions.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SlingshotGame extends BallGame { class SlingshotGame extends BallGame {
@ -24,7 +23,7 @@ class SlingshotGame extends BallGame {
await super.onLoad(); await super.onLoad();
camera.followVector2(Vector2.zero()); camera.followVector2(Vector2.zero());
await addFromBlueprint(Slingshots()); await add(Slingshots());
await ready(); await ready();
await traceAllBodies(); await traceAllBodies();
} }

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SparkyComputerGame extends BallGame { class SparkyComputerGame extends BallGame {
@ -24,7 +23,7 @@ class SparkyComputerGame extends BallGame {
]); ]);
camera.followVector2(Vector2(-10, -40)); camera.followVector2(Vector2(-10, -40));
await addFromBlueprint(SparkyComputer()); await add(SparkyComputer());
await ready(); await ready();
await traceAllBodies(); await traceAllBodies();
} }

@ -10,57 +10,58 @@ import '../../helpers/helpers.dart';
void main() { void main() {
group('AndroidSpaceship', () { group('AndroidSpaceship', () {
group('Spaceship', () { final assets = [
final assets = [ Assets.images.android.spaceship.saucer.keyName,
Assets.images.android.spaceship.saucer.keyName, Assets.images.android.spaceship.animatronic.keyName,
Assets.images.android.spaceship.animatronic.keyName, Assets.images.android.spaceship.lightBeam.keyName,
Assets.images.android.spaceship.lightBeam.keyName, ];
]; final flameTester = FlameTester(() => TestGame(assets));
final flameTester = FlameTester(() => TestGame(assets));
flameTester.test('loads correctly', (game) async { flameTester.test('loads correctly', (game) async {
await game.addFromBlueprint(AndroidSpaceship(position: Vector2.zero())); final component = AndroidSpaceship(position: Vector2.zero());
await game.ready(); await game.ensureAdd(component);
}); expect(game.contains(component), isTrue);
});
flameTester.testGameWidget( flameTester.testGameWidget(
'renders correctly', 'renders correctly',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
await game final canvas = ZCanvasComponent(
.addFromBlueprint(AndroidSpaceship(position: Vector2.zero())); children: [AndroidSpaceship(position: Vector2.zero())],
game.camera.followVector2(Vector2.zero()); );
await game.ready(); await game.ensureAdd(canvas);
await tester.pump(); game.camera.followVector2(Vector2.zero());
}, await game.ready();
verify: (game, tester) async { await tester.pump();
final animationDuration = game },
.descendants() verify: (game, tester) async {
.whereType<SpriteAnimationComponent>() final animationDuration = game
.last .descendants()
.animation! .whereType<SpriteAnimationComponent>()
.totalDuration(); .last
.animation!
.totalDuration();
await expectLater( await expectLater(
find.byGame<TestGame>(), find.byGame<TestGame>(),
matchesGoldenFile('golden/android_spaceship/start.png'), matchesGoldenFile('golden/android_spaceship/start.png'),
); );
game.update(animationDuration * 0.5); game.update(animationDuration * 0.5);
await tester.pump(); await tester.pump();
await expectLater( await expectLater(
find.byGame<TestGame>(), find.byGame<TestGame>(),
matchesGoldenFile('golden/android_spaceship/middle.png'), matchesGoldenFile('golden/android_spaceship/middle.png'),
); );
game.update(animationDuration * 0.5); game.update(animationDuration * 0.5);
await tester.pump(); await tester.pump();
await expectLater( await expectLater(
find.byGame<TestGame>(), find.byGame<TestGame>(),
matchesGoldenFile('golden/android_spaceship/end.png'), matchesGoldenFile('golden/android_spaceship/end.png'),
); );
}, },
); );
});
}); });
} }

@ -17,17 +17,24 @@ void main() {
Assets.images.boundary.outerBottom.keyName, Assets.images.boundary.outerBottom.keyName,
Assets.images.boundary.bottom.keyName, Assets.images.boundary.bottom.keyName,
]; ];
final flameTester = FlameTester(TestGame.new); final flameTester = FlameTester(() => TestGame(assets));
flameTester.test('loads correctly', (game) async {
final component = Boundaries();
await game.ensureAdd(component);
expect(game.contains(component), isTrue);
});
flameTester.testGameWidget( flameTester.testGameWidget(
'render correctly', 'render correctly',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
await game.addFromBlueprint(Boundaries()); final canvas = ZCanvasComponent(children: [Boundaries()]);
await game.ready(); await game.ensureAdd(canvas);
game.camera.followVector2(Vector2.zero()); game.camera.followVector2(Vector2.zero());
game.camera.zoom = 3.2; game.camera.zoom = 3.2;
await tester.pump();
}, },
verify: (game, tester) async { verify: (game, tester) async {
await expectLater( await expectLater(

@ -4,7 +4,6 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
@ -17,12 +16,17 @@ void main() {
]; ];
final flameTester = FlameTester(() => TestGame(assets)); final flameTester = FlameTester(() => TestGame(assets));
flameTester.test('loads correctly', (game) async {
final component = DinoWalls();
await game.ensureAdd(component);
expect(game.contains(component), isTrue);
});
flameTester.testGameWidget( flameTester.testGameWidget(
'renders correctly', 'renders correctly',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
await game.addFromBlueprint(DinoWalls()); await game.ensureAdd(DinoWalls());
await game.ready();
game.camera.followVector2(Vector2.zero()); game.camera.followVector2(Vector2.zero());
game.camera.zoom = 6.5; game.camera.zoom = 6.5;
@ -36,18 +40,5 @@ void main() {
); );
}, },
); );
flameTester.test(
'loads correctly',
(game) async {
final dinoWalls = DinoWalls();
await game.addFromBlueprint(dinoWalls);
await game.ready();
for (final wall in dinoWalls.components) {
expect(game.contains(wall), isTrue);
}
},
);
}); });
} }

@ -4,19 +4,23 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
void main() { void main() {
group('LaunchRamp', () { group('LaunchRamp', () {
final tester = FlameTester(TestGame.new); final flameTester = FlameTester(TestGame.new);
tester.testGameWidget( flameTester.test('loads correctly', (game) async {
final component = LaunchRamp();
await game.ensureAdd(component);
expect(game.contains(component), isTrue);
});
flameTester.testGameWidget(
'renders correctly', 'renders correctly',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.addFromBlueprint(LaunchRamp()); await game.ensureAdd(LaunchRamp());
await game.ready();
game.camera.followVector2(Vector2.zero()); game.camera.followVector2(Vector2.zero());
game.camera.zoom = 4.1; game.camera.zoom = 4.1;
}, },

@ -10,11 +10,11 @@ import '../../helpers/helpers.dart';
class TestLayerSensor extends LayerSensor { class TestLayerSensor extends LayerSensor {
TestLayerSensor({ TestLayerSensor({
required LayerEntranceOrientation orientation, required LayerEntranceOrientation orientation,
required int insidePriority, required int insideZIndex,
required Layer insideLayer, required Layer insideLayer,
}) : super( }) : super(
insideLayer: insideLayer, insideLayer: insideLayer,
insidePriority: insidePriority, insideZIndex: insideZIndex,
orientation: orientation, orientation: orientation,
); );
@ -33,7 +33,7 @@ void main() {
(game) async { (game) async {
final layerSensor = TestLayerSensor( final layerSensor = TestLayerSensor(
orientation: LayerEntranceOrientation.down, orientation: LayerEntranceOrientation.down,
insidePriority: insidePriority, insideZIndex: insidePriority,
insideLayer: Layer.spaceshipEntranceRamp, insideLayer: Layer.spaceshipEntranceRamp,
); );
await game.ensureAdd(layerSensor); await game.ensureAdd(layerSensor);
@ -48,7 +48,7 @@ void main() {
(game) async { (game) async {
final layerSensor = TestLayerSensor( final layerSensor = TestLayerSensor(
orientation: LayerEntranceOrientation.down, orientation: LayerEntranceOrientation.down,
insidePriority: insidePriority, insideZIndex: insidePriority,
insideLayer: Layer.spaceshipEntranceRamp, insideLayer: Layer.spaceshipEntranceRamp,
); );
await game.ensureAdd(layerSensor); await game.ensureAdd(layerSensor);
@ -66,7 +66,7 @@ void main() {
(game) async { (game) async {
final layerSensor = TestLayerSensor( final layerSensor = TestLayerSensor(
orientation: LayerEntranceOrientation.down, orientation: LayerEntranceOrientation.down,
insidePriority: insidePriority, insideZIndex: insidePriority,
insideLayer: pathwayLayer, insideLayer: pathwayLayer,
)..layer = openingLayer; )..layer = openingLayer;
await game.ensureAdd(layerSensor); await game.ensureAdd(layerSensor);
@ -80,7 +80,7 @@ void main() {
(game) async { (game) async {
final layerSensor = TestLayerSensor( final layerSensor = TestLayerSensor(
orientation: LayerEntranceOrientation.down, orientation: LayerEntranceOrientation.down,
insidePriority: insidePriority, insideZIndex: insidePriority,
insideLayer: pathwayLayer, insideLayer: pathwayLayer,
)..layer = openingLayer; )..layer = openingLayer;
await game.ensureAdd(layerSensor); await game.ensureAdd(layerSensor);
@ -95,7 +95,7 @@ void main() {
(game) async { (game) async {
final layerSensor = TestLayerSensor( final layerSensor = TestLayerSensor(
orientation: LayerEntranceOrientation.down, orientation: LayerEntranceOrientation.down,
insidePriority: insidePriority, insideZIndex: insidePriority,
insideLayer: pathwayLayer, insideLayer: pathwayLayer,
)..layer = openingLayer; )..layer = openingLayer;
await game.ensureAdd(layerSensor); await game.ensureAdd(layerSensor);
@ -111,64 +111,63 @@ void main() {
group('beginContact', () { group('beginContact', () {
late Ball ball; late Ball ball;
late Body body; late Body body;
late int insideZIndex;
late Layer insideLayer;
setUp(() { setUp(() {
ball = MockBall(); ball = MockBall();
body = MockBody(); body = MockBody();
insideZIndex = 1;
insideLayer = Layer.spaceshipEntranceRamp;
when(() => ball.body).thenReturn(body); when(() => ball.body).thenReturn(body);
when(() => ball.priority).thenReturn(1);
when(() => ball.layer).thenReturn(Layer.board); when(() => ball.layer).thenReturn(Layer.board);
}); });
flameTester.test( flameTester.test(
'changes ball layer and priority ' 'changes ball layer and zIndex '
'when a ball enters and exits a downward oriented LayerSensor', 'when a ball enters and exits a downward oriented LayerSensor',
(game) async { (game) async {
final sensor = TestLayerSensor( final sensor = TestLayerSensor(
orientation: LayerEntranceOrientation.down, orientation: LayerEntranceOrientation.down,
insidePriority: insidePriority, insideZIndex: insidePriority,
insideLayer: Layer.spaceshipEntranceRamp, insideLayer: insideLayer,
)..initialPosition = Vector2(0, 10); )..initialPosition = Vector2(0, 10);
when(() => body.linearVelocity).thenReturn(Vector2(0, -1)); when(() => body.linearVelocity).thenReturn(Vector2(0, -1));
sensor.beginContact(ball, MockContact()); sensor.beginContact(ball, MockContact());
verify(() => ball.layer = sensor.insideLayer).called(1); verify(() => ball.layer = insideLayer).called(1);
verify(() => ball.priority = sensor.insidePriority).called(1); verify(() => ball.zIndex = insideZIndex).called(1);
verify(ball.reorderChildren).called(1);
when(() => ball.layer).thenReturn(sensor.insideLayer); when(() => ball.layer).thenReturn(insideLayer);
sensor.beginContact(ball, MockContact()); sensor.beginContact(ball, MockContact());
verify(() => ball.layer = Layer.board); verify(() => ball.layer = Layer.board);
verify(() => ball.priority = RenderPriority.ballOnBoard).called(1); verify(() => ball.zIndex = ZIndexes.ballOnBoard).called(1);
verify(ball.reorderChildren).called(1);
}); });
flameTester.test( flameTester.test(
'changes ball layer and priority ' 'changes ball layer and zIndex '
'when a ball enters and exits an upward oriented LayerSensor', 'when a ball enters and exits an upward oriented LayerSensor',
(game) async { (game) async {
final sensor = TestLayerSensor( final sensor = TestLayerSensor(
orientation: LayerEntranceOrientation.up, orientation: LayerEntranceOrientation.up,
insidePriority: insidePriority, insideZIndex: insidePriority,
insideLayer: Layer.spaceshipEntranceRamp, insideLayer: insideLayer,
)..initialPosition = Vector2(0, 10); )..initialPosition = Vector2(0, 10);
when(() => body.linearVelocity).thenReturn(Vector2(0, 1)); when(() => body.linearVelocity).thenReturn(Vector2(0, 1));
sensor.beginContact(ball, MockContact()); sensor.beginContact(ball, MockContact());
verify(() => ball.layer = sensor.insideLayer).called(1); verify(() => ball.layer = insideLayer).called(1);
verify(() => ball.priority = sensor.insidePriority).called(1); verify(() => ball.zIndex = insidePriority).called(1);
verify(ball.reorderChildren).called(1);
when(() => ball.layer).thenReturn(sensor.insideLayer); when(() => ball.layer).thenReturn(insideLayer);
sensor.beginContact(ball, MockContact()); sensor.beginContact(ball, MockContact());
verify(() => ball.layer = Layer.board); verify(() => ball.layer = Layer.board);
verify(() => ball.priority = RenderPriority.ballOnBoard).called(1); verify(() => ball.zIndex = ZIndexes.ballOnBoard).called(1);
verify(ball.reorderChildren).called(1);
}); });
}); });
} }

@ -4,7 +4,6 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
@ -18,11 +17,17 @@ void main() {
const length = 2.0; const length = 2.0;
const angle = 0.0; const angle = 0.0;
flameTester.test('loads correctly', (game) async {
final component = Slingshots();
await game.ensureAdd(component);
expect(game.contains(component), isTrue);
});
flameTester.testGameWidget( flameTester.testGameWidget(
'renders correctly', 'renders correctly',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
await game.addFromBlueprint(Slingshots()); await game.ensureAdd(Slingshots());
game.camera.followVector2(Vector2.zero()); game.camera.followVector2(Vector2.zero());
await game.ready(); await game.ready();
await tester.pump(); await tester.pump();

@ -4,7 +4,6 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
@ -17,12 +16,17 @@ void main() {
]; ];
final flameTester = FlameTester(() => TestGame(assets)); final flameTester = FlameTester(() => TestGame(assets));
flameTester.test('loads correctly', (game) async {
final component = SpaceshipRail();
await game.ensureAdd(component);
expect(game.contains(component), isTrue);
});
flameTester.testGameWidget( flameTester.testGameWidget(
'renders correctly', 'renders correctly',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
await game.addFromBlueprint(SpaceshipRail()); await game.ensureAdd(SpaceshipRail());
await game.ready();
await tester.pump(); await tester.pump();
game.camera.followVector2(Vector2.zero()); game.camera.followVector2(Vector2.zero());
@ -35,18 +39,5 @@ void main() {
); );
}, },
); );
flameTester.test(
'loads correctly',
(game) async {
final spaceshipRail = SpaceshipRail();
await game.addFromBlueprint(spaceshipRail);
await game.ready();
for (final element in spaceshipRail.components) {
expect(game.contains(element), isTrue);
}
},
);
}); });
} }

@ -25,18 +25,11 @@ void main() {
final flameTester = FlameTester(() => TestGame(assets)); final flameTester = FlameTester(() => TestGame(assets));
group('SpaceshipRamp', () { group('SpaceshipRamp', () {
flameTester.test( flameTester.test('loads correctly', (game) async {
'loads correctly', final component = SpaceshipRamp();
(game) async { await game.ensureAdd(component);
final spaceshipRamp = SpaceshipRamp(); expect(game.contains(component), isTrue);
await game.addFromBlueprint(spaceshipRamp); });
await game.ready();
for (final component in spaceshipRamp.components) {
expect(game.contains(component), isTrue);
}
},
);
group('renders correctly', () { group('renders correctly', () {
const goldenFilePath = 'golden/spaceship_ramp/'; const goldenFilePath = 'golden/spaceship_ramp/';
@ -46,16 +39,14 @@ void main() {
'inactive sprite', 'inactive sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final spaceshipRamp = SpaceshipRamp(); final component = SpaceshipRamp();
await game.addFromBlueprint(spaceshipRamp); final canvas = ZCanvasComponent(children: [component]);
await game.ready(); await game.ensureAdd(canvas);
await tester.pump(); await tester.pump();
expect( expect(
spaceshipRamp.components component.children.whereType<SpriteGroupComponent>().first.current,
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.inactive, SpaceshipRampArrowSpriteState.inactive,
); );
@ -73,17 +64,15 @@ void main() {
'active1 sprite', 'active1 sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final spaceshipRamp = SpaceshipRamp(); final component = SpaceshipRamp();
await game.addFromBlueprint(spaceshipRamp); final canvas = ZCanvasComponent(children: [component]);
await game.ready(); await game.ensureAdd(canvas);
spaceshipRamp.progress();
component.progress();
await tester.pump(); await tester.pump();
expect( expect(
spaceshipRamp.components component.children.whereType<SpriteGroupComponent>().first.current,
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.active1, SpaceshipRampArrowSpriteState.active1,
); );
@ -101,19 +90,17 @@ void main() {
'active2 sprite', 'active2 sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final spaceshipRamp = SpaceshipRamp(); final component = SpaceshipRamp();
await game.addFromBlueprint(spaceshipRamp); final canvas = ZCanvasComponent(children: [component]);
await game.ready(); await game.ensureAdd(canvas);
spaceshipRamp
component
..progress() ..progress()
..progress(); ..progress();
await tester.pump(); await tester.pump();
expect( expect(
spaceshipRamp.components component.children.whereType<SpriteGroupComponent>().first.current,
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.active2, SpaceshipRampArrowSpriteState.active2,
); );
@ -131,20 +118,18 @@ void main() {
'active3 sprite', 'active3 sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final spaceshipRamp = SpaceshipRamp(); final component = SpaceshipRamp();
await game.addFromBlueprint(spaceshipRamp); final canvas = ZCanvasComponent(children: [component]);
await game.ready(); await game.ensureAdd(canvas);
spaceshipRamp
component
..progress() ..progress()
..progress() ..progress()
..progress(); ..progress();
await tester.pump(); await tester.pump();
expect( expect(
spaceshipRamp.components component.children.whereType<SpriteGroupComponent>().first.current,
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.active3, SpaceshipRampArrowSpriteState.active3,
); );
@ -162,10 +147,11 @@ void main() {
'active4 sprite', 'active4 sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final spaceshipRamp = SpaceshipRamp(); final component = SpaceshipRamp();
await game.addFromBlueprint(spaceshipRamp); final canvas = ZCanvasComponent(children: [component]);
await game.ready(); await game.ensureAdd(canvas);
spaceshipRamp
component
..progress() ..progress()
..progress() ..progress()
..progress() ..progress()
@ -173,10 +159,7 @@ void main() {
await tester.pump(); await tester.pump();
expect( expect(
spaceshipRamp.components component.children.whereType<SpriteGroupComponent>().first.current,
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.active4, SpaceshipRampArrowSpriteState.active4,
); );
@ -194,10 +177,11 @@ void main() {
'active5 sprite', 'active5 sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final spaceshipRamp = SpaceshipRamp(); final component = SpaceshipRamp();
await game.addFromBlueprint(spaceshipRamp); final canvas = ZCanvasComponent(children: [component]);
await game.ready(); await game.ensureAdd(canvas);
spaceshipRamp
component
..progress() ..progress()
..progress() ..progress()
..progress() ..progress()
@ -206,10 +190,7 @@ void main() {
await tester.pump(); await tester.pump();
expect( expect(
spaceshipRamp.components component.children.whereType<SpriteGroupComponent>().first.current,
.whereType<SpriteGroupComponent>()
.first
.current,
SpaceshipRampArrowSpriteState.active5, SpaceshipRampArrowSpriteState.active5,
); );

@ -4,7 +4,6 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
@ -18,20 +17,17 @@ void main() {
]; ];
final flameTester = FlameTester(() => TestGame(assets)); final flameTester = FlameTester(() => TestGame(assets));
flameTester.test( flameTester.test('loads correctly', (game) async {
'loads correctly', final component = SparkyComputer();
(game) async { await game.ensureAdd(component);
await game.addFromBlueprint(SparkyComputer()); expect(game.contains(component), isTrue);
await game.ready(); });
},
);
flameTester.testGameWidget( flameTester.testGameWidget(
'renders correctly', 'renders correctly',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
await game.addFromBlueprint(SparkyComputer()); await game.ensureAdd(SparkyComputer());
await game.ready();
await tester.pump(); await tester.pump();
game.camera game.camera

@ -1,8 +1,8 @@
library pinball_flame; library pinball_flame;
export 'src/blueprint.dart';
export 'src/component_controller.dart'; export 'src/component_controller.dart';
export 'src/contact_behavior.dart'; export 'src/contact_behavior.dart';
export 'src/keyboard_input_controller.dart'; export 'src/keyboard_input_controller.dart';
export 'src/parent_is_a.dart'; export 'src/parent_is_a.dart';
export 'src/sprite_animation.dart'; export 'src/sprite_animation.dart';
export 'src/z_canvas_component.dart';

@ -1,46 +0,0 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
// TODO(erickzanardo): Keeping this inside our code base so we can experiment
// with the idea, but this is a potential upstream change on Flame.
/// {@template blueprint}
/// A [Blueprint] is a virtual way of grouping [Component]s that are related.
/// {@endtemplate blueprint}
class Blueprint {
/// {@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);
}
}
}
final List<Component> _components = [];
final List<Blueprint> _blueprints = [];
Future<void> _addToParent(Component parent) async {
await parent.addAll(_components);
}
/// Returns a copy of the components built by this blueprint.
List<Component> get components => List.unmodifiable(_components);
/// Returns a copy of the blueprints built by this blueprint.
List<Blueprint> get blueprints => List.unmodifiable(_blueprints);
}
/// 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._addToParent(this);
}
}

@ -0,0 +1,249 @@
import 'dart:typed_data';
import 'dart:ui';
import 'package:flame/components.dart';
/// {@template z_canvas_component}
/// Draws [ZIndex] components after the all non-[ZIndex] components have been
/// drawn.
/// {@endtemplate}
class ZCanvasComponent extends Component {
/// {@macro z_canvas_component}
ZCanvasComponent({
Iterable<Component>? children,
}) : _zCanvas = ZCanvas(),
super(children: children);
final ZCanvas _zCanvas;
@override
void renderTree(Canvas canvas) {
_zCanvas.canvas = canvas;
super.renderTree(_zCanvas);
_zCanvas.render();
}
}
/// Apply to any [Component] that will be rendered according to a
/// [ZIndex.zIndex].
///
/// [ZIndex] components must be descendants of a [ZCanvasComponent].
///
/// {@macro z_canvas.render}
mixin ZIndex on Component {
/// The z-index of this component.
///
/// The higher the value, the later the component will be drawn. Hence,
/// rendering in front of [Component]s with lower [zIndex] values.
int zIndex = 0;
@override
void renderTree(
Canvas canvas,
) {
if (canvas is ZCanvas) {
canvas.buffer(this);
} else {
super.renderTree(canvas);
}
}
}
/// The [ZCanvas] allows to postpone the rendering of [ZIndex] components.
///
/// You should not use this class directly.
class ZCanvas implements Canvas {
/// The [Canvas] to render to.
///
/// This is set by [ZCanvasComponent] when rendering.
late Canvas canvas;
final List<ZIndex> _zBuffer = [];
/// Postpones the rendering of [ZIndex] component and its children.
void buffer(ZIndex component) => _zBuffer.add(component);
/// Renders all [ZIndex] components and their children.
///
/// {@template z_canvas.render}
/// The rendering order is defined by the parent [ZIndex]. The children of
/// the same parent are rendered in the order they were added.
///
/// If two [Component]s ever overlap each other, and have the same
/// [ZIndex.zIndex], there is no guarantee that the first one will be rendered
/// before the second one.
/// {@endtemplate}
void render() => _zBuffer
..sort((a, b) => a.zIndex.compareTo(b.zIndex))
..whereType<Component>().forEach(_render)
..clear();
void _render(Component component) => component.renderTree(canvas);
@override
void clipPath(Path path, {bool doAntiAlias = true}) =>
canvas.clipPath(path, doAntiAlias: doAntiAlias);
@override
void clipRRect(RRect rrect, {bool doAntiAlias = true}) =>
canvas.clipRRect(rrect, doAntiAlias: doAntiAlias);
@override
void clipRect(
Rect rect, {
ClipOp clipOp = ClipOp.intersect,
bool doAntiAlias = true,
}) =>
canvas.clipRect(rect, clipOp: clipOp, doAntiAlias: doAntiAlias);
@override
void drawArc(
Rect rect,
double startAngle,
double sweepAngle,
bool useCenter,
Paint paint,
) =>
canvas.drawArc(rect, startAngle, sweepAngle, useCenter, paint);
@override
void drawAtlas(
Image atlas,
List<RSTransform> transforms,
List<Rect> rects,
List<Color>? colors,
BlendMode? blendMode,
Rect? cullRect,
Paint paint,
) =>
canvas.drawAtlas(
atlas,
transforms,
rects,
colors,
blendMode,
cullRect,
paint,
);
@override
void drawCircle(Offset c, double radius, Paint paint) => canvas.drawCircle(
c,
radius,
paint,
);
@override
void drawColor(Color color, BlendMode blendMode) =>
canvas.drawColor(color, blendMode);
@override
void drawDRRect(RRect outer, RRect inner, Paint paint) =>
canvas.drawDRRect(outer, inner, paint);
@override
void drawImage(Image image, Offset offset, Paint paint) =>
canvas.drawImage(image, offset, paint);
@override
void drawImageNine(Image image, Rect center, Rect dst, Paint paint) =>
canvas.drawImageNine(image, center, dst, paint);
@override
void drawImageRect(Image image, Rect src, Rect dst, Paint paint) =>
canvas.drawImageRect(image, src, dst, paint);
@override
void drawLine(Offset p1, Offset p2, Paint paint) =>
canvas.drawLine(p1, p2, paint);
@override
void drawOval(Rect rect, Paint paint) => canvas.drawOval(rect, paint);
@override
void drawPaint(Paint paint) => canvas.drawPaint(paint);
@override
void drawParagraph(Paragraph paragraph, Offset offset) =>
canvas.drawParagraph(paragraph, offset);
@override
void drawPath(Path path, Paint paint) => canvas.drawPath(path, paint);
@override
void drawPicture(Picture picture) => canvas.drawPicture(picture);
@override
void drawPoints(PointMode pointMode, List<Offset> points, Paint paint) =>
canvas.drawPoints(pointMode, points, paint);
@override
void drawRRect(RRect rrect, Paint paint) => canvas.drawRRect(rrect, paint);
@override
void drawRawAtlas(
Image atlas,
Float32List rstTransforms,
Float32List rects,
Int32List? colors,
BlendMode? blendMode,
Rect? cullRect,
Paint paint,
) =>
canvas.drawRawAtlas(
atlas,
rstTransforms,
rects,
colors,
blendMode,
cullRect,
paint,
);
@override
void drawRawPoints(PointMode pointMode, Float32List points, Paint paint) =>
canvas.drawRawPoints(pointMode, points, paint);
@override
void drawRect(Rect rect, Paint paint) => canvas.drawRect(rect, paint);
@override
void drawShadow(
Path path,
Color color,
double elevation,
bool transparentOccluder,
) =>
canvas.drawShadow(path, color, elevation, transparentOccluder);
@override
void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint) =>
canvas.drawVertices(vertices, blendMode, paint);
@override
int getSaveCount() => canvas.getSaveCount();
@override
void restore() => canvas.restore();
@override
void rotate(double radians) => canvas.rotate(radians);
@override
void save() => canvas.save();
@override
void saveLayer(Rect? bounds, Paint paint) => canvas.saveLayer(bounds, paint);
@override
void scale(double sx, [double? sy]) => canvas.scale(sx, sy);
@override
void skew(double sx, double sy) => canvas.skew(sx, sy);
@override
void transform(Float64List matrix4) => canvas.transform(matrix4);
@override
void translate(double dx, double dy) => canvas.translate(dx, dy);
}

@ -1,86 +0,0 @@
// ignore_for_file: cascade_invocations
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_flame/pinball_flame.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
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('correctly sets and gets blueprints', () {
final blueprint2 = Blueprint(
components: [Component()],
);
final blueprint1 = Blueprint(
components: [Component()],
blueprints: [blueprint2],
);
expect(blueprint1.blueprints, contains(blueprint2));
});
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);
}
});
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));
}
});
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

@ -0,0 +1,385 @@
// ignore_for_file: cascade_invocations
import 'dart:typed_data';
import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter/material.dart' hide Image;
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_flame/pinball_flame.dart';
class _TestCircleComponent extends CircleComponent with ZIndex {
_TestCircleComponent(Color color)
: super(
paint: Paint()..color = color,
radius: 10,
);
}
class _MockCanvas extends Mock implements Canvas {}
class _MockImage extends Mock implements Image {}
class _MockPicture extends Mock implements Picture {}
class _MockParagraph extends Mock implements Paragraph {}
class _MockVertices extends Mock implements Vertices {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(FlameGame.new);
const goldenPrefix = 'golden/rendering/';
group('ZCanvasComponent', () {
flameTester.test('loads correctly', (game) async {
final component = ZCanvasComponent();
await game.ensureAdd(component);
expect(game.contains(component), isTrue);
});
flameTester.testGameWidget(
'red circle renders behind blue circle',
setUp: (game, tester) async {
final canvas = ZCanvasComponent(
children: [
_TestCircleComponent(Colors.blue)..zIndex = 1,
_TestCircleComponent(Colors.red)..zIndex = 0,
],
);
await game.ensureAdd(canvas);
game.camera.followVector2(Vector2.zero());
},
verify: (game, tester) async {
await expectLater(
find.byGame<FlameGame>(),
matchesGoldenFile('${goldenPrefix}red_blue.png'),
);
},
);
flameTester.testGameWidget(
'blue circle renders behind red circle',
setUp: (game, tester) async {
final canvas = ZCanvasComponent(
children: [
_TestCircleComponent(Colors.blue)..zIndex = 0,
_TestCircleComponent(Colors.red)..zIndex = 1
],
);
await game.ensureAdd(canvas);
game.camera.followVector2(Vector2.zero());
},
verify: (game, tester) async {
await expectLater(
find.byGame<FlameGame>(),
matchesGoldenFile('${goldenPrefix}blue_red.png'),
);
},
);
});
group('ZCanvas', () {
late Canvas canvas;
late Path path;
late RRect rRect;
late Rect rect;
late Paint paint;
late Image atlas;
late BlendMode blendMode;
late Color color;
late Offset offset;
late Float64List float64list;
late Float32List float32list;
late Int32List int32list;
late Picture picture;
late Paragraph paragraph;
late Vertices vertices;
setUp(() {
canvas = _MockCanvas();
path = Path();
rRect = RRect.zero;
rect = Rect.zero;
paint = Paint();
atlas = _MockImage();
blendMode = BlendMode.clear;
color = Colors.black;
offset = Offset.zero;
float64list = Float64List(1);
float32list = Float32List(1);
int32list = Int32List(1);
picture = _MockPicture();
paragraph = _MockParagraph();
vertices = _MockVertices();
});
test("clipPath calls Canvas's clipPath", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.clipPath(path, doAntiAlias: false);
verify(
() => canvas.clipPath(path, doAntiAlias: false),
).called(1);
});
test("clipRRect calls Canvas's clipRRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.clipRRect(rRect, doAntiAlias: false);
verify(
() => canvas.clipRRect(rRect, doAntiAlias: false),
).called(1);
});
test("clipRect calls Canvas's clipRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.clipRect(rect, doAntiAlias: false);
verify(
() => canvas.clipRect(rect, doAntiAlias: false),
).called(1);
});
test("drawArc calls Canvas's drawArc", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawArc(rect, 0, 1, false, paint);
verify(
() => canvas.drawArc(rect, 0, 1, false, paint),
).called(1);
});
test("drawAtlas calls Canvas's drawAtlas", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawAtlas(atlas, [], [], [], blendMode, rect, paint);
verify(
() => canvas.drawAtlas(atlas, [], [], [], blendMode, rect, paint),
).called(1);
});
test("drawCircle calls Canvas's drawCircle", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawCircle(offset, 0, paint);
verify(
() => canvas.drawCircle(offset, 0, paint),
).called(1);
});
test("drawColor calls Canvas's drawColor", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawColor(color, blendMode);
verify(
() => canvas.drawColor(color, blendMode),
).called(1);
});
test("drawDRRect calls Canvas's drawDRRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawDRRect(rRect, rRect, paint);
verify(
() => canvas.drawDRRect(rRect, rRect, paint),
).called(1);
});
test("drawImage calls Canvas's drawImage", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawImage(atlas, offset, paint);
verify(
() => canvas.drawImage(atlas, offset, paint),
).called(1);
});
test("drawImageNine calls Canvas's drawImageNine", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawImageNine(atlas, rect, rect, paint);
verify(
() => canvas.drawImageNine(atlas, rect, rect, paint),
).called(1);
});
test("drawImageRect calls Canvas's drawImageRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawImageRect(atlas, rect, rect, paint);
verify(
() => canvas.drawImageRect(atlas, rect, rect, paint),
).called(1);
});
test("drawLine calls Canvas's drawLine", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawLine(offset, offset, paint);
verify(
() => canvas.drawLine(offset, offset, paint),
).called(1);
});
test("drawOval calls Canvas's drawOval", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawOval(rect, paint);
verify(
() => canvas.drawOval(rect, paint),
).called(1);
});
test("drawPaint calls Canvas's drawPaint", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawPaint(paint);
verify(
() => canvas.drawPaint(paint),
).called(1);
});
test("drawParagraph calls Canvas's drawParagraph", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawParagraph(paragraph, offset);
verify(
() => canvas.drawParagraph(paragraph, offset),
).called(1);
});
test("drawPath calls Canvas's drawPath", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawPath(path, paint);
verify(
() => canvas.drawPath(path, paint),
).called(1);
});
test("drawPicture calls Canvas's drawPicture", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawPicture(picture);
verify(
() => canvas.drawPicture(picture),
).called(1);
});
test("drawPoints calls Canvas's drawPoints", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawPoints(PointMode.points, [offset], paint);
verify(
() => canvas.drawPoints(PointMode.points, [offset], paint),
).called(1);
});
test("drawRRect calls Canvas's drawRRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawRRect(rRect, paint);
verify(
() => canvas.drawRRect(rRect, paint),
).called(1);
});
test("drawRawAtlas calls Canvas's drawRawAtlas", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawRawAtlas(
atlas,
float32list,
float32list,
int32list,
BlendMode.clear,
rect,
paint,
);
verify(
() => canvas.drawRawAtlas(
atlas,
float32list,
float32list,
int32list,
BlendMode.clear,
rect,
paint,
),
).called(1);
});
test("drawRawPoints calls Canvas's drawRawPoints", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawRawPoints(PointMode.points, float32list, paint);
verify(
() => canvas.drawRawPoints(PointMode.points, float32list, paint),
).called(1);
});
test("drawRect calls Canvas's drawRect", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawRect(rect, paint);
verify(
() => canvas.drawRect(rect, paint),
).called(1);
});
test("drawShadow calls Canvas's drawShadow", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawShadow(path, color, 0, false);
verify(
() => canvas.drawShadow(path, color, 0, false),
).called(1);
});
test("drawVertices calls Canvas's drawVertices", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.drawVertices(vertices, blendMode, paint);
verify(
() => canvas.drawVertices(vertices, blendMode, paint),
).called(1);
});
test("getSaveCount calls Canvas's getSaveCount", () {
final zcanvas = ZCanvas()..canvas = canvas;
when(() => canvas.getSaveCount()).thenReturn(1);
zcanvas.getSaveCount();
verify(() => canvas.getSaveCount()).called(1);
});
test("restore calls Canvas's restore", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.restore();
verify(() => canvas.restore()).called(1);
});
test("rotate calls Canvas's rotate", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.rotate(0);
verify(() => canvas.rotate(0)).called(1);
});
test("save calls Canvas's save", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.save();
verify(() => canvas.save()).called(1);
});
test("saveLayer calls Canvas's saveLayer", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.saveLayer(rect, paint);
verify(() => canvas.saveLayer(rect, paint)).called(1);
});
test("scale calls Canvas's scale", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.scale(0, 0);
verify(() => canvas.scale(0, 0)).called(1);
});
test("skew calls Canvas's skew", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.skew(0, 0);
verify(() => canvas.skew(0, 0)).called(1);
});
test("transform calls Canvas's transform", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.transform(float64list);
verify(() => canvas.transform(float64list)).called(1);
});
test("translate calls Canvas's translate", () {
final zcanvas = ZCanvas()..canvas = canvas;
zcanvas.translate(0, 0);
verify(() => canvas.translate(0, 0)).called(1);
});
});
}

@ -4,7 +4,6 @@ import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
@ -38,21 +37,20 @@ void main() {
); );
group('AndroidAcres', () { group('AndroidAcres', () {
flameTester.test( flameTester.test('loads correctly', (game) async {
'loads correctly', final component = AndroidAcres();
(game) async { await game.ensureAdd(component);
await game.addFromBlueprint(AndroidAcres()); expect(game.contains(component), isTrue);
await game.ready(); });
},
);
group('loads', () { group('loads', () {
flameTester.test( flameTester.test(
'a Spaceship', 'a Spaceship',
(game) async { (game) async {
await game.ensureAdd(AndroidAcres());
expect( expect(
AndroidAcres().blueprints.whereType<AndroidSpaceship>().single, game.descendants().whereType<AndroidSpaceship>().length,
isNotNull, equals(1),
); );
}, },
); );
@ -60,9 +58,10 @@ void main() {
flameTester.test( flameTester.test(
'a SpaceshipRamp', 'a SpaceshipRamp',
(game) async { (game) async {
await game.ensureAdd(AndroidAcres());
expect( expect(
AndroidAcres().blueprints.whereType<SpaceshipRamp>().single, game.descendants().whereType<SpaceshipRamp>().length,
isNotNull, equals(1),
); );
}, },
); );
@ -70,9 +69,10 @@ void main() {
flameTester.test( flameTester.test(
'a SpaceshipRail', 'a SpaceshipRail',
(game) async { (game) async {
await game.ensureAdd(AndroidAcres());
expect( expect(
AndroidAcres().blueprints.whereType<SpaceshipRail>().single, game.descendants().whereType<SpaceshipRail>().length,
isNotNull, equals(1),
); );
}, },
); );
@ -80,10 +80,7 @@ void main() {
flameTester.test( flameTester.test(
'three AndroidBumper', 'three AndroidBumper',
(game) async { (game) async {
final androidZone = AndroidAcres(); await game.ensureAdd(AndroidAcres());
await game.addFromBlueprint(androidZone);
await game.ready();
expect( expect(
game.descendants().whereType<AndroidBumper>().length, game.descendants().whereType<AndroidBumper>().length,
equals(3), equals(3),

@ -8,6 +8,7 @@ import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
@ -58,7 +59,8 @@ void main() {
const points = 20; const points = 20;
final scoringBehavior = ScoringBehavior(points: points); final scoringBehavior = ScoringBehavior(points: points);
await parent.add(scoringBehavior); await parent.add(scoringBehavior);
await game.ensureAdd(parent); final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
scoringBehavior.beginContact(ball, MockContact()); scoringBehavior.beginContact(ball, MockContact());
@ -76,7 +78,8 @@ void main() {
const points = 20; const points = 20;
final scoringBehavior = ScoringBehavior(points: points); final scoringBehavior = ScoringBehavior(points: points);
await parent.add(scoringBehavior); await parent.add(scoringBehavior);
await game.ensureAdd(parent); final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
scoringBehavior.beginContact(ball, MockContact()); scoringBehavior.beginContact(ball, MockContact());
@ -90,7 +93,8 @@ void main() {
const points = 20; const points = 20;
final scoringBehavior = ScoringBehavior(points: points); final scoringBehavior = ScoringBehavior(points: points);
await parent.add(scoringBehavior); await parent.add(scoringBehavior);
await game.ensureAdd(parent); final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
scoringBehavior.beginContact(ball, MockContact()); scoringBehavior.beginContact(ball, MockContact());
await game.ready(); await game.ready();

@ -5,7 +5,6 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
@ -30,17 +29,19 @@ void main() {
group('SparkyScorch', () { group('SparkyScorch', () {
flameTester.test('loads correctly', (game) async { flameTester.test('loads correctly', (game) async {
await game.addFromBlueprint(SparkyScorch()); final component = SparkyScorch();
await game.ready(); await game.ensureAdd(component);
expect(game.contains(component), isTrue);
}); });
group('loads', () { group('loads', () {
flameTester.test( flameTester.test(
'a SparkyComputer', 'a SparkyComputer',
(game) async { (game) async {
await game.ensureAdd(SparkyScorch());
expect( expect(
SparkyScorch().blueprints.whereType<SparkyComputer>().single, game.descendants().whereType<SparkyComputer>().length,
isNotNull, equals(1),
); );
}, },
); );
@ -48,13 +49,10 @@ void main() {
flameTester.test( flameTester.test(
'a SparkyAnimatronic', 'a SparkyAnimatronic',
(game) async { (game) async {
final sparkysScorch = SparkyScorch(); await game.ensureAdd(SparkyScorch());
await game.addFromBlueprint(sparkysScorch);
await game.ready();
expect( expect(
game.descendants().whereType<SparkyAnimatronic>().single, game.descendants().whereType<SparkyAnimatronic>().length,
isNotNull, equals(1),
); );
}, },
); );
@ -62,10 +60,7 @@ void main() {
flameTester.test( flameTester.test(
'three SparkyBumper', 'three SparkyBumper',
(game) async { (game) async {
final sparkysScorch = SparkyScorch(); await game.ensureAdd(SparkyScorch());
await game.addFromBlueprint(sparkysScorch);
await game.ready();
expect( expect(
game.descendants().whereType<SparkyBumper>().length, game.descendants().whereType<SparkyBumper>().length,
equals(3), equals(3),

@ -134,7 +134,7 @@ void main() {
(game) async { (game) async {
await game.ready(); await game.ready();
expect( expect(
game.children.whereType<Drain>().length, game.descendants().whereType<Drain>().length,
equals(1), equals(1),
); );
}, },
@ -145,18 +145,18 @@ void main() {
(game) async { (game) async {
await game.ready(); await game.ready();
expect( expect(
game.children.whereType<BottomGroup>().length, game.descendants().whereType<BottomGroup>().length,
equals(1), equals(1),
); );
}, },
); );
flameBlocTester.test( flameBlocTester.test(
'has only one Plunger', 'has only one Launcher',
(game) async { (game) async {
await game.ready(); await game.ready();
expect( expect(
game.children.whereType<Plunger>().length, game.descendants().whereType<Launcher>().length,
equals(1), equals(1),
); );
}, },
@ -165,7 +165,7 @@ void main() {
flameBlocTester.test('has one FlutterForest', (game) async { flameBlocTester.test('has one FlutterForest', (game) async {
await game.ready(); await game.ready();
expect( expect(
game.children.whereType<FlutterForest>().length, game.descendants().whereType<FlutterForest>().length,
equals(1), equals(1),
); );
}); });
@ -174,7 +174,10 @@ void main() {
'one GoogleWord', 'one GoogleWord',
(game) async { (game) async {
await game.ready(); await game.ready();
expect(game.children.whereType<GoogleWord>().length, equals(1)); expect(
game.descendants().whereType<GoogleWord>().length,
equals(1),
);
}, },
); );
@ -462,7 +465,7 @@ void main() {
await game.ready(); await game.ready();
expect( expect(
game.children.whereType<ControlledBall>().length, game.descendants().whereType<ControlledBall>().length,
equals(previousBalls.length + 1), equals(previousBalls.length + 1),
); );
}, },

Loading…
Cancel
Save