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
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame/components.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template android_acres}
/// Area positioned on the left side of the board containing the
/// [AndroidSpaceship], [SpaceshipRamp], [SpaceshipRail], and [AndroidBumper]s.
/// {@endtemplate}
class AndroidAcres extends Blueprint {
class AndroidAcres extends Component {
/// {@macro android_acres}
AndroidAcres()
: super(
components: [
children: [
SpaceshipRamp(),
SpaceshipRail(),
AndroidSpaceship(position: Vector2(-26.5, -28.5)),
AndroidBumper.a(
children: [
ScoringBehavior(points: 20000),
@ -30,10 +32,5 @@ class AndroidAcres extends Blueprint {
],
)..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:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template bottom_group}
/// 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.
/// {@endtemplate}
// TODO(allisonryan0002): Consider renaming.
class BottomGroup extends Component {
class BottomGroup extends Component with ZIndex {
/// {@macro bottom_group}
BottomGroup()
: super(
@ -16,8 +17,9 @@ class BottomGroup extends Component {
_BottomGroupSide(side: BoardSide.right),
_BottomGroupSide(side: BoardSide.left),
],
priority: RenderPriority.bottomGroup,
);
) {
zIndex = ZIndexes.bottomGroup;
}
}
/// {@template bottom_group_side}

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

@ -1,7 +1,6 @@
import 'package:flame/components.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template dino_desert}
/// Area located next to the [Launcher] containing the [ChromeDino] and
@ -9,15 +8,14 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@endtemplate}
// TODO(allisonryan0002): use a controller to initiate dino bonus when dino is
// fully implemented.
class DinoDesert extends Blueprint {
class DinoDesert extends Component {
/// {@macro dino_desert}
DinoDesert()
: super(
components: [
children: [
ChromeDino()..initialPosition = Vector2(12.3, -6.9),
],
blueprints: [
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/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template flutter_forest}
/// Area positioned at the top right of the board where the [Ball] can bounce
/// off [DashNestBumper]s.
/// {@endtemplate}
class FlutterForest extends Component {
class FlutterForest extends Component with ZIndex {
/// {@macro flutter_forest}
FlutterForest()
: super(
priority: RenderPriority.flutterForest,
children: [
Signpost(
children: [
@ -39,7 +39,9 @@ class FlutterForest extends Component {
DashAnimatronic()..position = Vector2(20, -66),
FlutterForestBonusBehavior(),
],
);
) {
zIndex = ZIndexes.flutterForest;
}
/// 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/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template google_word}
/// Loads all [GoogleLetter]s to compose a [GoogleWord].
/// {@endtemplate}
class GoogleWord extends Component {
class GoogleWord extends Component with ZIndex {
/// {@macro google_word}
GoogleWord({
required Vector2 position,
@ -39,7 +40,9 @@ class GoogleWord extends Component {
)..initialPosition = position + Vector2(12.92, 1.82),
GoogleWordBonusBehavior(),
],
);
) {
zIndex = ZIndexes.decal;
}
/// 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_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template launcher}
/// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and
/// [LaunchRamp].
/// Channel on the right side of the board containing the [LaunchRamp],
/// [Plunger], and [RocketSpriteComponent].
/// {@endtemplate}
class Launcher extends Blueprint {
class Launcher extends Component {
/// {@macro launcher}
Launcher()
: super(
components: [
children: [
LaunchRamp(),
ControlledPlunger(compressionDistance: 10.5)
..initialPosition = Vector2(41.1, 43),
RocketSpriteComponent()..position = Vector2(43, 62.3),
],
blueprints: [LaunchRamp()],
);
}

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

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

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

@ -42,32 +42,40 @@ class PinballGame extends Forge2DGame
@override
Future<void> onLoad() async {
unawaited(add(gameFlowController = GameFlowController(this)));
unawaited(add(CameraController(this)));
unawaited(add(Backboard.waiting(position: Vector2(0, -88))));
await add(BoardBackgroundSpriteComponent());
await add(Drain());
await add(BottomGroup());
unawaited(addFromBlueprint(Boundaries()));
final launcher = Launcher();
unawaited(addFromBlueprint(launcher));
await add(Multipliers());
await add(FlutterForest());
await addFromBlueprint(SparkyScorch());
await addFromBlueprint(AndroidAcres());
await addFromBlueprint(DinoDesert());
unawaited(addFromBlueprint(Slingshots()));
await add(
await add(gameFlowController = GameFlowController(this));
await add(CameraController(this));
final machine = [
BoardBackgroundSpriteComponent(),
Boundaries(),
Backboard.waiting(position: Vector2(0, -88)),
];
final decals = [
GoogleWord(
position: Vector2(
BoardDimensions.bounds.center.dx - 4.1,
BoardDimensions.bounds.center.dy + 1.8,
),
position: Vector2(-4.1, 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();
}
@ -76,12 +84,12 @@ class PinballGame extends Forge2DGame
@override
void onTapDown(TapDownInfo info) {
if (info.raw.kind == PointerDeviceKind.touch) {
final rocket = children.whereType<RocketSpriteComponent>().first;
final rocket = descendants().whereType<RocketSpriteComponent>().first;
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.
if (bounds.contains(info.eventPosition.game.toOffset())) {
children.whereType<Plunger>().first.pull();
descendants().whereType<Plunger>().single.pull();
} else {
final leftSide = info.eventPosition.widget.x < canvasSize.x / 2;
focusedBoardSide = leftSide ? BoardSide.left : BoardSide.right;
@ -101,7 +109,7 @@ class PinballGame extends Forge2DGame
final bounds = rocket.topLeftPosition & rocket.size;
if (bounds.contains(info.eventPosition.game.toOffset())) {
children.whereType<Plunger>().first.release();
descendants().whereType<Plunger>().single.release();
} else {
_moveFlippersDown();
}
@ -110,7 +118,7 @@ class PinballGame extends Forge2DGame
@override
void onTapCancel() {
children.whereType<Plunger>().first.release();
descendants().whereType<Plunger>().single.release();
_moveFlippersDown();
super.onTapCancel();
@ -131,8 +139,6 @@ class _GameBallsController extends ComponentController<PinballGame>
with BlocComponent<GameBloc, GameState> {
_GameBallsController(PinballGame game) : super(game);
late final Plunger _plunger;
@override
bool listenWhen(GameState? previousState, GameState newState) {
final noBallsLeft = component.descendants().whereType<Ball>().isEmpty;
@ -144,30 +150,27 @@ class _GameBallsController extends ComponentController<PinballGame>
@override
void onNewState(GameState state) {
super.onNewState(state);
_spawnBall();
spawnBall();
}
@override
Future<void> onLoad() async {
await super.onLoad();
_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);
spawnBall();
}
/// Attaches the controller to the plunger.
// TODO(alestiago): Remove this method and use onLoad instead.
// ignore: use_setters_to_change_properties
void attachTo(Plunger plunger) {
_plunger = plunger;
void spawnBall() {
// TODO(alestiago): Refactor with behavioural pattern.
component.ready().whenComplete(() {
final plunger = parent!.descendants().whereType<Plunger>().single;
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,
audio: audio,
) {
controller = _DebugGameBallsController(this);
controller = _GameBallsController(this);
}
@override
@ -188,42 +191,21 @@ class DebugPinballGame extends PinballGame with FPSCounter {
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
void onTapUp(TapUpInfo info) {
super.onTapUp(info);
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.
// coverage:ignore-start
class _DebugInformation extends Component with HasGameRef<DebugPinballGame> {
_DebugInformation() : super(priority: RenderPriority.debugInfo);
@override
PositionType get positionType => PositionType.widget;

@ -12,7 +12,7 @@ export 'cubit/android_bumper_cubit.dart';
/// {@template android_bumper}
/// Bumper for area under the [AndroidSpaceship].
/// {@endtemplate}
class AndroidBumper extends BodyComponent with InitialPosition {
class AndroidBumper extends BodyComponent with InitialPosition, ZIndex {
/// {@macro android_bumper}
AndroidBumper._({
required double majorRadius,
@ -25,7 +25,6 @@ class AndroidBumper extends BodyComponent with InitialPosition {
}) : _majorRadius = majorRadius,
_minorRadius = minorRadius,
super(
priority: RenderPriority.androidBumper,
renderBody: false,
children: [
AndroidBumperBallContactBehavior(),
@ -38,7 +37,9 @@ class AndroidBumper extends BodyComponent with InitialPosition {
),
...?children,
],
);
) {
zIndex = ZIndexes.androidBumper;
}
/// {@macro android_bumper}
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_flame/pinball_flame.dart';
class AndroidSpaceship extends Blueprint {
class AndroidSpaceship extends Component {
AndroidSpaceship({required Vector2 position})
: super(
components: [
children: [
_SpaceshipSaucer()..initialPosition = position,
_SpaceshipSaucerSpriteAnimationComponent()..position = position,
_LightBeamSpriteComponent()..position = position + Vector2(2.5, 5),
_AndroidHead()..initialPosition = position + Vector2(0.5, 0.25),
_SpaceshipHole(
outsideLayer: Layer.spaceshipExitRail,
outsidePriority: RenderPriority.ballOnSpaceshipRail,
outsidePriority: ZIndexes.ballOnSpaceshipRail,
)..initialPosition = position - Vector2(5.3, -5.4),
_SpaceshipHole(
outsideLayer: Layer.board,
outsidePriority: RenderPriority.ballOnBoard,
outsidePriority: ZIndexes.ballOnBoard,
)..initialPosition = position - Vector2(-7.5, -1.1),
],
);
@ -65,12 +65,13 @@ class _SpaceshipSaucerShape extends ChainShape {
}
class _SpaceshipSaucerSpriteAnimationComponent extends SpriteAnimationComponent
with HasGameRef {
with HasGameRef, ZIndex {
_SpaceshipSaucerSpriteAnimationComponent()
: super(
anchor: Anchor.center,
priority: RenderPriority.spaceshipSaucer,
);
) {
zIndex = ZIndexes.spaceshipSaucer;
}
@override
Future<void> onLoad() async {
@ -101,12 +102,14 @@ class _SpaceshipSaucerSpriteAnimationComponent extends SpriteAnimationComponent
}
// TODO(allisonryan0002): add pulsing behavior.
class _LightBeamSpriteComponent extends SpriteComponent with HasGameRef {
class _LightBeamSpriteComponent extends SpriteComponent
with HasGameRef, ZIndex {
_LightBeamSpriteComponent()
: super(
anchor: Anchor.center,
priority: RenderPriority.spaceshipLightBeam,
);
) {
zIndex = ZIndexes.spaceshipLightBeam;
}
@override
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()
: super(
priority: RenderPriority.androidHead,
children: [_AndroidHeadSpriteAnimationComponent()],
renderBody: false,
) {
layer = Layer.spaceship;
zIndex = ZIndexes.androidHead;
}
@override
@ -191,8 +194,8 @@ class _SpaceshipHole extends LayerSensor {
insideLayer: Layer.spaceship,
outsideLayer: outsideLayer,
orientation: LayerEntranceOrientation.down,
insidePriority: RenderPriority.ballOnSpaceship,
outsidePriority: outsidePriority,
insideZIndex: ZIndexes.ballOnSpaceship,
outsideZIndex: outsidePriority,
) {
layer = Layer.spaceship;
}

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

@ -2,14 +2,17 @@
import 'package:flame/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()
: super(
anchor: Anchor.center,
priority: RenderPriority.boardBackground,
position: Vector2(0, -1),
);
) {
zIndex = ZIndexes.boardBackground;
}
@override
Future<void> onLoad() async {

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

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart' hide Timer;
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template chrome_dino}
/// 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
/// different direction.
/// {@endtemplate}
class ChromeDino extends BodyComponent with InitialPosition {
class ChromeDino extends BodyComponent with InitialPosition, ZIndex {
/// {@macro chrome_dino}
ChromeDino()
: super(
priority: RenderPriority.dino,
renderBody: false,
);
) {
zIndex = ZIndexes.dino;
}
/// The size of the dinosaur mouth.
static final size = Vector2(5.5, 5);

@ -23,7 +23,6 @@ export 'layer.dart';
export 'layer_sensor.dart';
export 'multiplier/multiplier.dart';
export 'plunger.dart';
export 'render_priority.dart';
export 'rocket.dart';
export 'score_text.dart';
export 'shapes/shapes.dart';
@ -34,3 +33,4 @@ export 'spaceship_ramp.dart';
export 'sparky_animatronic.dart';
export 'sparky_bumper/sparky_bumper.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_flame/pinball_flame.dart';
/// {@template dinowalls}
/// A [Blueprint] which creates walls for the [ChromeDino].
/// {@template dino_walls}
/// Walls near the [ChromeDino].
/// {@endtemplate}
class DinoWalls extends Blueprint {
/// {@macro dinowalls}
class DinoWalls extends Component {
/// {@macro dino_walls}
DinoWalls()
: super(
components: [
children: [
_DinoTopWall(),
_DinoBottomWall(),
],
@ -23,14 +23,15 @@ class DinoWalls extends Blueprint {
/// {@template dino_top_wall}
/// Wall segment located above [ChromeDino].
/// {@endtemplate}
class _DinoTopWall extends BodyComponent with InitialPosition {
class _DinoTopWall extends BodyComponent with InitialPosition, ZIndex {
///{@macro dino_top_wall}
_DinoTopWall()
: super(
priority: RenderPriority.dinoTopWall,
children: [_DinoTopWallSpriteComponent()],
renderBody: false,
);
) {
zIndex = ZIndexes.dinoTopWall;
}
List<FixtureDef> _createFixtureDefs() {
final topStraightShape = EdgeShape()
@ -116,14 +117,15 @@ class _DinoTopWallSpriteComponent extends SpriteComponent with HasGameRef {
/// {@template dino_bottom_wall}
/// Wall segment located below [ChromeDino].
/// {@endtemplate}
class _DinoBottomWall extends BodyComponent with InitialPosition {
class _DinoBottomWall extends BodyComponent with InitialPosition, ZIndex {
///{@macro dino_top_wall}
_DinoBottomWall()
: super(
priority: RenderPriority.dinoBottomWall,
children: [_DinoBottomWallSpriteComponent()],
renderBody: false,
);
) {
zIndex = ZIndexes.dinoBottomWall;
}
List<FixtureDef> _createFixtureDefs() {
final topStraightShape = EdgeShape()

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

@ -8,9 +8,6 @@ import 'package:flutter/material.dart';
/// [BodyComponent]s with compatible [Layer]s can collide with each other,
/// ignoring others. This compatibility depends on bit masking operation
/// 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}
mixin Layered<T extends Forge2DGame> on BodyComponent<T> {
Layer _layer = Layer.all;

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

@ -1,6 +1,7 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template plunger}
/// [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].
/// {@endtemplate}
class Plunger extends BodyComponent with InitialPosition, Layered {
class Plunger extends BodyComponent with InitialPosition, Layered, ZIndex {
/// {@macro plunger}
Plunger({
required this.compressionDistance,
// TODO(ruimiguel): set to priority +1 over LaunchRamp once all priorities
// are fixed.
}) : super(
priority: RenderPriority.plunger,
renderBody: false,
) {
}) : super(renderBody: false) {
zIndex = ZIndexes.plunger;
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:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template rocket_sprite_component}
/// A [SpriteComponent] for the rocket over [Plunger].
/// {@endtemplate}
class RocketSpriteComponent extends SpriteComponent with HasGameRef {
class RocketSpriteComponent extends SpriteComponent with HasGameRef, ZIndex {
/// {@macro rocket_sprite_component}
RocketSpriteComponent()
: super(
priority: RenderPriority.rocket,
anchor: Anchor.center,
);
RocketSpriteComponent() : super(anchor: Anchor.center) {
zIndex = ZIndexes.rocket;
}
@override
Future<void> onLoad() async {

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

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

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

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

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

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

@ -8,11 +8,11 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_computer}
/// A computer owned by Sparky.
/// {@endtemplate}
class SparkyComputer extends Blueprint {
class SparkyComputer extends Component {
/// {@macro sparky_computer}
SparkyComputer()
: super(
components: [
children: [
_ComputerBase(),
_ComputerTopSpriteComponent(),
_ComputerGlowSpriteComponent(),
@ -20,13 +20,14 @@ class SparkyComputer extends Blueprint {
);
}
class _ComputerBase extends BodyComponent with InitialPosition {
class _ComputerBase extends BodyComponent with InitialPosition, ZIndex {
_ComputerBase()
: super(
priority: RenderPriority.computerBase,
renderBody: false,
children: [_ComputerBaseSpriteComponent()],
);
) {
zIndex = ZIndexes.computerBase;
}
List<FixtureDef> _createFixtureDefs() {
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()
: super(
anchor: Anchor.center,
position: Vector2(-12.52, -49.37),
priority: RenderPriority.computerTop,
);
) {
zIndex = ZIndexes.computerTop;
}
@override
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()
: super(
anchor: Anchor.center,
position: Vector2(7.4, 10),
priority: RenderPriority.computerGlow,
);
) {
zIndex = ZIndexes.computerGlow;
}
@override
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:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class AndroidSpaceshipGame extends BallGame {
AndroidSpaceshipGame()
: super(
ballPriority: RenderPriority.ballOnSpaceship,
ballPriority: ZIndexes.ballOnSpaceship,
ballLayer: Layer.spaceship,
imagesFileNames: [
Assets.images.android.spaceship.saucer.keyName,
@ -29,7 +28,7 @@ class AndroidSpaceshipGame extends BallGame {
await super.onLoad();
camera.followVector2(Vector2.zero());
await addFromBlueprint(
await add(
AndroidSpaceship(position: Vector2.zero()),
);

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

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

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

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

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

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

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

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

@ -17,17 +17,24 @@ void main() {
Assets.images.boundary.outerBottom.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(
'render correctly',
setUp: (game, tester) async {
await game.images.loadAll(assets);
await game.addFromBlueprint(Boundaries());
await game.ready();
final canvas = ZCanvasComponent(children: [Boundaries()]);
await game.ensureAdd(canvas);
game.camera.followVector2(Vector2.zero());
game.camera.zoom = 3.2;
await tester.pump();
},
verify: (game, tester) async {
await expectLater(

@ -4,7 +4,6 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart';
@ -17,12 +16,17 @@ void main() {
];
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(
'renders correctly',
setUp: (game, tester) async {
await game.images.loadAll(assets);
await game.addFromBlueprint(DinoWalls());
await game.ready();
await game.ensureAdd(DinoWalls());
game.camera.followVector2(Vector2.zero());
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:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart';
void main() {
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',
setUp: (game, tester) async {
await game.addFromBlueprint(LaunchRamp());
await game.ready();
await game.ensureAdd(LaunchRamp());
game.camera.followVector2(Vector2.zero());
game.camera.zoom = 4.1;
},

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

@ -4,7 +4,6 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart';
@ -18,11 +17,17 @@ void main() {
const length = 2.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(
'renders correctly',
setUp: (game, tester) async {
await game.images.loadAll(assets);
await game.addFromBlueprint(Slingshots());
await game.ensureAdd(Slingshots());
game.camera.followVector2(Vector2.zero());
await game.ready();
await tester.pump();

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

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

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

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

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

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

Loading…
Cancel
Save