Merge branch 'main' into refactor/backboard-asset-and-sizing

pull/319/head
Allison Ryan 3 years ago
commit 9174d90e58

@ -0,0 +1,2 @@
export 'bumper_noisy_behavior.dart';
export 'scoring_behavior.dart';

@ -0,0 +1,14 @@
// ignore_for_file: public_member_api_docs
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/game/pinball_game.dart';
import 'package:pinball_flame/pinball_flame.dart';
class BumperNoisyBehavior extends ContactBehavior with HasGameRef<PinballGame> {
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
gameRef.audio.bumper();
}
}

@ -0,0 +1,77 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.dart';
import 'package:flame/effects.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 scoring_behavior}
/// Adds [_points] to the score and shows a text effect.
///
/// The behavior removes itself after the duration.
/// {@endtemplate}
class ScoringBehavior extends Component with HasGameRef<PinballGame> {
/// {@macto scoring_behavior}
ScoringBehavior({
required Points points,
required Vector2 position,
double duration = 1,
}) : _points = points,
_position = position,
_effectController = EffectController(
duration: duration,
);
final Points _points;
final Vector2 _position;
final EffectController _effectController;
@override
void update(double dt) {
super.update(dt);
if (_effectController.completed) {
removeFromParent();
}
}
@override
Future<void> onLoad() async {
gameRef.read<GameBloc>().add(Scored(points: _points.value));
await gameRef.firstChild<ZCanvasComponent>()!.add(
ScoreComponent(
points: _points,
position: _position,
effectController: _effectController,
),
);
}
}
/// {@template scoring_contact_behavior}
/// Adds points to the score when the [Ball] contacts the [parent].
/// {@endtemplate}
class ScoringContactBehavior extends ContactBehavior
with HasGameRef<PinballGame> {
/// {@macro scoring_contact_behavior}
ScoringContactBehavior({
required Points points,
}) : _points = points;
final Points _points;
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is! Ball) return;
parent.add(
ScoringBehavior(
points: _points,
position: other.body.position,
),
);
}
}

@ -2,8 +2,8 @@
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/components/android_acres/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template android_acres}
@ -20,22 +20,25 @@ class AndroidAcres extends Component {
AndroidSpaceship(position: Vector2(-26.5, -28.5)),
AndroidAnimatronic(
children: [
ScoringBehavior(points: Points.twoHundredThousand),
ScoringContactBehavior(points: Points.twoHundredThousand),
],
)..initialPosition = Vector2(-26, -28.25),
AndroidBumper.a(
children: [
BumperScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-25, 1.3),
AndroidBumper.b(
children: [
BumperScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-32.8, -9.2),
AndroidBumper.cow(
children: [
BumperScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-20.5, -13.8),
AndroidSpaceshipBonusBehavior(),

@ -1,4 +1,5 @@
import 'package:flame/components.dart';
import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
@ -51,7 +52,8 @@ class _BottomGroupSide extends Component {
final kicker = Kicker(
side: _side,
children: [
ScoringBehavior(points: Points.fiveThousand)..applyTo(['bouncy_edge']),
ScoringContactBehavior(points: Points.fiveThousand)
..applyTo(['bouncy_edge']),
],
)..initialPosition = Vector2(
(22.64 * direction) + centerXAdjustment,

@ -13,5 +13,4 @@ export 'google_word/google_word.dart';
export 'launcher.dart';
export 'multiballs/multiballs.dart';
export 'multipliers/multipliers.dart';
export 'scoring_behavior.dart';
export 'sparky_scorch.dart';

@ -67,7 +67,9 @@ class BallController extends ComponentController<Ball>
const Duration(milliseconds: 2583),
);
component.resume();
await component.boost(Vector2(40, 110));
await component.add(
BallTurboChargingBehavior(impulse: Vector2(40, 110)),
);
}
@override

@ -1,6 +1,7 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/components/dino_desert/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
@ -16,7 +17,7 @@ class DinoDesert extends Component {
children: [
ChromeDino(
children: [
ScoringBehavior(points: Points.twoHundredThousand)
ScoringContactBehavior(points: Points.twoHundredThousand)
..applyTo(['inside_mouth']),
],
)..initialPosition = Vector2(12.6, -6.9),

@ -2,8 +2,8 @@
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/behaviors/behaviors.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';
@ -18,22 +18,26 @@ class FlutterForest extends Component with ZIndex {
children: [
Signpost(
children: [
BumperScoringBehavior(points: Points.fiveThousand),
ScoringContactBehavior(points: Points.fiveThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(8.35, -58.3),
DashNestBumper.main(
children: [
BumperScoringBehavior(points: Points.twoHundredThousand),
ScoringContactBehavior(points: Points.twoHundredThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(18.55, -59.35),
DashNestBumper.a(
children: [
BumperScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(8.95, -51.95),
DashNestBumper.b(
children: [
BumperScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(22.3, -46.75),
DashAnimatronic()..position = Vector2(20, -66),

@ -1,7 +1,7 @@
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/behaviors/scoring_behavior.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';
@ -16,27 +16,27 @@ class GoogleWord extends Component with ZIndex {
children: [
GoogleLetter(
0,
children: [ScoringBehavior(points: Points.fiveThousand)],
children: [ScoringContactBehavior(points: Points.fiveThousand)],
)..initialPosition = position + Vector2(-13.1, 1.72),
GoogleLetter(
1,
children: [ScoringBehavior(points: Points.fiveThousand)],
children: [ScoringContactBehavior(points: Points.fiveThousand)],
)..initialPosition = position + Vector2(-8.33, -0.75),
GoogleLetter(
2,
children: [ScoringBehavior(points: Points.fiveThousand)],
children: [ScoringContactBehavior(points: Points.fiveThousand)],
)..initialPosition = position + Vector2(-2.88, -1.85),
GoogleLetter(
3,
children: [ScoringBehavior(points: Points.fiveThousand)],
children: [ScoringContactBehavior(points: Points.fiveThousand)],
)..initialPosition = position + Vector2(2.88, -1.85),
GoogleLetter(
4,
children: [ScoringBehavior(points: Points.fiveThousand)],
children: [ScoringContactBehavior(points: Points.fiveThousand)],
)..initialPosition = position + Vector2(8.33, -0.75),
GoogleLetter(
5,
children: [ScoringBehavior(points: Points.fiveThousand)],
children: [ScoringContactBehavior(points: Points.fiveThousand)],
)..initialPosition = position + Vector2(13.1, 1.72),
GoogleWordBonusBehavior(),
],

@ -1,53 +0,0 @@
// 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 scoring_behavior}
/// Adds points to the score when the ball contacts the [parent].
/// {@endtemplate}
class ScoringBehavior extends ContactBehavior with HasGameRef<PinballGame> {
/// {@macro scoring_behavior}
ScoringBehavior({
required Points points,
}) : _points = points;
final Points _points;
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is! Ball) return;
gameRef.read<GameBloc>().add(Scored(points: _points.value));
gameRef.firstChild<ZCanvasComponent>()!.add(
ScoreComponent(
points: _points,
position: other.body.position,
),
);
}
}
/// {@template bumper_scoring_behavior}
/// A specific [ScoringBehavior] used for Bumpers.
/// In addition to its parent logic, also plays the
/// SFX for bumpers
/// {@endtemplate}
class BumperScoringBehavior extends ScoringBehavior {
/// {@macro bumper_scoring_behavior}
BumperScoringBehavior({
required Points points,
}) : super(points: points);
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is! Ball) return;
gameRef.audio.bumper();
}
}

@ -2,7 +2,8 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/components/components.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template sparky_scorch}
@ -16,17 +17,20 @@ class SparkyScorch extends Component {
children: [
SparkyBumper.a(
children: [
BumperScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-22.9, -41.65),
SparkyBumper.b(
children: [
BumperScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-21.25, -57.9),
SparkyBumper.c(
children: [
BumperScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-3.3, -52.55),
SparkyComputerSensor()..initialPosition = Vector2(-13, -49.9),
@ -47,7 +51,7 @@ class SparkyComputerSensor extends BodyComponent
: super(
renderBody: false,
children: [
ScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
],
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 38 KiB

@ -78,11 +78,11 @@ class AndroidBumper extends BodyComponent with InitialPosition, ZIndex {
AndroidBumper.cow({
Iterable<Component>? children,
}) : this._(
majorRadius: 3.4,
minorRadius: 2.9,
majorRadius: 3.45,
minorRadius: 3.11,
litAssetPath: Assets.images.android.bumper.cow.lit.keyName,
dimmedAssetPath: Assets.images.android.bumper.cow.dimmed.keyName,
spritePosition: Vector2(0, -0.68),
spritePosition: Vector2(0, -0.35),
bloc: AndroidBumperCubit(),
children: [
...?children,

@ -1,14 +1,13 @@
import 'dart:async';
import 'dart:math' as math;
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_components/src/components/ball/behaviors/ball_gravitating_behavior.dart';
import 'package:pinball_components/src/components/ball/behaviors/ball_scaling_behavior.dart';
import 'package:pinball_flame/pinball_flame.dart';
export 'behaviors/behaviors.dart';
/// {@template ball}
/// A solid, [BodyType.dynamic] sphere that rolls and bounces around.
/// {@endtemplate}
@ -50,17 +49,13 @@ class Ball extends BodyComponent with Layered, InitialPosition, ZIndex {
@override
Body createBody() {
final shape = CircleShape()..radius = size.x / 2;
final fixtureDef = FixtureDef(
shape,
density: 1,
);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
type: BodyType.dynamic,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
return world.createBody(bodyDef)..createFixtureFromShape(shape, 1);
}
/// Immediatly and completly [stop]s the ball.
@ -81,12 +76,6 @@ class Ball extends BodyComponent with Layered, InitialPosition, ZIndex {
void resume() {
body.gravityScale = Vector2(1, 1);
}
/// Applies a boost and [_TurboChargeSpriteAnimationComponent] on this [Ball].
Future<void> boost(Vector2 impulse) async {
body.linearVelocity = impulse;
await add(_TurboChargeSpriteAnimationComponent());
}
}
class _BallSpriteComponent extends SpriteComponent with HasGameRef {
@ -101,55 +90,3 @@ class _BallSpriteComponent extends SpriteComponent with HasGameRef {
anchor = Anchor.center;
}
}
class _TurboChargeSpriteAnimationComponent extends SpriteAnimationComponent
with HasGameRef, ZIndex {
_TurboChargeSpriteAnimationComponent()
: super(
anchor: const Anchor(0.53, 0.72),
removeOnFinish: true,
) {
zIndex = ZIndexes.turboChargeFlame;
}
late final Vector2 _textureSize;
@override
Future<void> onLoad() async {
await super.onLoad();
final spriteSheet = await gameRef.images.load(
Assets.images.ball.flameEffect.keyName,
);
const amountPerRow = 8;
const amountPerColumn = 4;
_textureSize = Vector2(
spriteSheet.width / amountPerRow,
spriteSheet.height / amountPerColumn,
);
animation = SpriteAnimation.fromFrameData(
spriteSheet,
SpriteAnimationData.sequenced(
amount: amountPerRow * amountPerColumn,
amountPerRow: amountPerRow,
stepTime: 1 / 24,
textureSize: _textureSize,
loop: false,
),
);
}
@override
void update(double dt) {
super.update(dt);
if (parent != null) {
final body = (parent! as BodyComponent).body;
final direction = -body.linearVelocity.normalized();
angle = math.atan2(direction.x, -direction.y);
size = (_textureSize / 45) * body.fixtures.first.shape.radius;
}
}
}

@ -0,0 +1,81 @@
import 'dart:math' as math;
import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template ball_turbo_charging_behavior}
/// Puts the [Ball] in flames and [_impulse]s it.
/// {@endtemplate}
class BallTurboChargingBehavior extends TimerComponent with ParentIsA<Ball> {
/// {@macro ball_turbo_charging_behavior}
BallTurboChargingBehavior({
required Vector2 impulse,
}) : _impulse = impulse,
super(period: 5, removeOnFinish: true);
final Vector2 _impulse;
@override
Future<void> onLoad() async {
await super.onLoad();
parent.body.linearVelocity = _impulse;
await parent.add(_TurboChargeSpriteAnimationComponent());
}
@override
void onRemove() {
parent
.firstChild<_TurboChargeSpriteAnimationComponent>()!
.removeFromParent();
super.onRemove();
}
}
class _TurboChargeSpriteAnimationComponent extends SpriteAnimationComponent
with HasGameRef, ZIndex, ParentIsA<Ball> {
_TurboChargeSpriteAnimationComponent()
: super(
anchor: const Anchor(0.53, 0.72),
) {
zIndex = ZIndexes.turboChargeFlame;
}
late final Vector2 _textureSize;
@override
void update(double dt) {
super.update(dt);
final direction = -parent.body.linearVelocity.normalized();
angle = math.atan2(direction.x, -direction.y);
size = (_textureSize / 45) * parent.body.fixtures.first.shape.radius;
}
@override
Future<void> onLoad() async {
await super.onLoad();
final spriteSheet = await gameRef.images.load(
Assets.images.ball.flameEffect.keyName,
);
const amountPerRow = 8;
const amountPerColumn = 4;
_textureSize = Vector2(
spriteSheet.width / amountPerRow,
spriteSheet.height / amountPerColumn,
);
animation = SpriteAnimation.fromFrameData(
spriteSheet,
SpriteAnimationData.sequenced(
amount: amountPerRow * amountPerColumn,
amountPerRow: amountPerRow,
stepTime: 1 / 24,
textureSize: _textureSize,
),
);
}
}

@ -1,2 +1,3 @@
export 'ball_gravitating_behavior.dart';
export 'ball_scaling_behavior.dart';
export 'ball_turbo_charging_behavior.dart';

@ -23,16 +23,20 @@ class ScoreComponent extends SpriteComponent with HasGameRef, ZIndex {
ScoreComponent({
required this.points,
required Vector2 position,
}) : super(
required EffectController effectController,
}) : _effectController = effectController,
super(
position: position,
anchor: Anchor.center,
) {
zIndex = ZIndexes.score;
}
late Points points;
late final Effect _effect;
late Points points;
final EffectController _effectController;
@override
Future<void> onLoad() async {
@ -46,7 +50,7 @@ class ScoreComponent extends SpriteComponent with HasGameRef, ZIndex {
await add(
_effect = MoveEffect.by(
Vector2(0, -5),
EffectController(duration: 1),
_effectController,
),
);
}

@ -13,8 +13,9 @@ class BallBoosterGame extends LineGame {
@override
void onLine(Vector2 line) {
final ball = Ball(baseColor: Colors.transparent);
add(ball);
final impulse = line * -1 * 20;
ball.add(BallTurboChargingBehavior(impulse: impulse));
ball.mounted.then((value) => ball.boost(line * -1 * 20));
add(ball);
}
}

@ -1,5 +1,6 @@
import 'dart:math';
import 'package:flame/effects.dart';
import 'package:flame/input.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart';
@ -38,6 +39,7 @@ class ScoreGame extends AssetsGame with TapDetector {
ScoreComponent(
points: score,
position: info.eventPosition.game..multiply(Vector2(1, -1)),
effectController: EffectController(duration: 1),
),
);
}

@ -1,12 +1,10 @@
// ignore_for_file: cascade_invocations
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/ball/behaviors/behaviors.dart';
import '../../../helpers/helpers.dart';
@ -180,50 +178,5 @@ void main() {
);
});
});
group('boost', () {
flameTester.test('applies an impulse to the ball', (game) async {
final ball = Ball(baseColor: baseColor);
await game.ensureAdd(ball);
expect(ball.body.linearVelocity, equals(Vector2.zero()));
await ball.boost(Vector2.all(10));
expect(ball.body.linearVelocity.x, greaterThan(0));
expect(ball.body.linearVelocity.y, greaterThan(0));
});
flameTester.test('adds TurboChargeSpriteAnimation', (game) async {
final ball = Ball(baseColor: baseColor);
await game.ensureAdd(ball);
await ball.boost(Vector2.all(10));
game.update(0);
expect(
ball.children.whereType<SpriteAnimationComponent>().single,
isNotNull,
);
});
flameTester.test('removes TurboChargeSpriteAnimation after it finishes',
(game) async {
final ball = Ball(baseColor: baseColor);
await game.ensureAdd(ball);
await ball.boost(Vector2.all(10));
game.update(0);
final turboChargeSpriteAnimation =
ball.children.whereType<SpriteAnimationComponent>().single;
expect(ball.contains(turboChargeSpriteAnimation), isTrue);
game.update(turboChargeSpriteAnimation.animation!.totalDuration());
game.update(0.1);
expect(ball.contains(turboChargeSpriteAnimation), isFalse);
});
});
});
}

@ -6,7 +6,6 @@ import 'package:flame/components.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/ball/behaviors/behaviors.dart';
import '../../../../helpers/helpers.dart';

@ -6,7 +6,6 @@ import 'package:flame/components.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/ball/behaviors/behaviors.dart';
import '../../../../helpers/helpers.dart';

@ -0,0 +1,94 @@
// ignore_for_file: cascade_invocations
import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
import '../../../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group(
'BallTurboChargingBehavior',
() {
final assets = [Assets.images.ball.ball.keyName];
final flameTester = FlameTester(() => TestGame(assets));
const baseColor = Color(0xFFFFFFFF);
test('can be instantiated', () {
expect(
BallTurboChargingBehavior(impulse: Vector2.zero()),
isA<BallTurboChargingBehavior>(),
);
});
flameTester.test('can be loaded', (game) async {
final ball = Ball.test(baseColor: baseColor);
final behavior = BallTurboChargingBehavior(impulse: Vector2.zero());
await ball.add(behavior);
await game.ensureAdd(ball);
expect(
ball.firstChild<BallTurboChargingBehavior>(),
equals(behavior),
);
});
flameTester.test(
'impulses the ball velocity when loaded',
(game) async {
final ball = Ball.test(baseColor: baseColor);
await game.ensureAdd(ball);
final impulse = Vector2.all(1);
final behavior = BallTurboChargingBehavior(impulse: impulse);
await ball.ensureAdd(behavior);
expect(
ball.body.linearVelocity.x,
equals(impulse.x),
);
expect(
ball.body.linearVelocity.y,
equals(impulse.y),
);
},
);
flameTester.test('adds sprite', (game) async {
final ball = Ball(baseColor: baseColor);
await game.ensureAdd(ball);
await ball.ensureAdd(
BallTurboChargingBehavior(impulse: Vector2.zero()),
);
expect(
ball.children.whereType<SpriteAnimationComponent>().single,
isNotNull,
);
});
flameTester.test('removes sprite after it finishes', (game) async {
final ball = Ball(baseColor: baseColor);
await game.ensureAdd(ball);
final behavior = BallTurboChargingBehavior(impulse: Vector2.zero());
await ball.ensureAdd(behavior);
final turboChargeSpriteAnimation =
ball.children.whereType<SpriteAnimationComponent>().single;
expect(ball.contains(turboChargeSpriteAnimation), isTrue);
game.update(behavior.timer.limit);
game.update(0.1);
expect(ball.contains(turboChargeSpriteAnimation), isFalse);
});
},
);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 80 KiB

@ -28,6 +28,7 @@ void main() {
ScoreComponent(
points: Points.oneMillion,
position: Vector2.zero(),
effectController: EffectController(duration: 1),
),
);
},
@ -46,6 +47,7 @@ void main() {
ScoreComponent(
points: Points.oneMillion,
position: Vector2.zero(),
effectController: EffectController(duration: 1),
),
);
@ -67,6 +69,7 @@ void main() {
ScoreComponent(
points: Points.oneMillion,
position: Vector2.zero(),
effectController: EffectController(duration: 1),
),
);
@ -88,6 +91,7 @@ void main() {
ScoreComponent(
points: Points.fiveThousand,
position: Vector2.zero(),
effectController: EffectController(duration: 1),
),
);
@ -113,6 +117,7 @@ void main() {
ScoreComponent(
points: Points.twentyThousand,
position: Vector2.zero(),
effectController: EffectController(duration: 1),
),
);
@ -138,6 +143,7 @@ void main() {
ScoreComponent(
points: Points.twoHundredThousand,
position: Vector2.zero(),
effectController: EffectController(duration: 1),
),
);
@ -163,6 +169,7 @@ void main() {
ScoreComponent(
points: Points.oneMillion,
position: Vector2.zero(),
effectController: EffectController(duration: 1),
),
);

@ -0,0 +1,50 @@
// ignore_for_file: cascade_invocations
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball_audio/pinball_audio.dart';
import '../../helpers/helpers.dart';
class _TestBodyComponent extends BodyComponent {
@override
Body createBody() {
return world.createBody(BodyDef());
}
}
class _MockPinballAudio extends Mock implements PinballAudio {}
class _MockContact extends Mock implements Contact {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('BumperNoisyBehavior', () {});
late PinballAudio audio;
final flameTester = FlameTester(
() => EmptyPinballTestGame(audio: audio),
);
setUp(() {
audio = _MockPinballAudio();
});
flameTester.testGameWidget(
'plays bumper sound',
setUp: (game, _) async {
final behavior = BumperNoisyBehavior();
final parent = _TestBodyComponent();
await game.ensureAdd(parent);
await parent.ensureAdd(behavior);
behavior.beginContact(Object(), _MockContact());
},
verify: (_, __) async {
verify(audio.bumper).called(1);
},
);
}

@ -0,0 +1,210 @@
// ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/behaviors/behaviors.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';
class _TestBodyComponent extends BodyComponent {
@override
Body createBody() => world.createBody(BodyDef());
}
class _MockBall extends Mock implements Ball {}
class _MockBody extends Mock implements Body {}
class _MockGameBloc extends Mock implements GameBloc {}
class _MockContact extends Mock implements Contact {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final assets = [
Assets.images.score.fiveThousand.keyName,
Assets.images.score.twentyThousand.keyName,
Assets.images.score.twoHundredThousand.keyName,
Assets.images.score.oneMillion.keyName,
];
late GameBloc bloc;
late Ball ball;
late BodyComponent parent;
setUp(() {
ball = _MockBall();
final ballBody = _MockBody();
when(() => ball.body).thenReturn(ballBody);
when(() => ballBody.position).thenReturn(Vector2.all(4));
parent = _TestBodyComponent();
});
final flameBlocTester = FlameBlocTester<EmptyPinballTestGame, GameBloc>(
gameBuilder: EmptyPinballTestGame.new,
blocBuilder: () {
bloc = _MockGameBloc();
const state = GameState(
score: 0,
multiplier: 1,
rounds: 3,
bonusHistory: [],
);
whenListen(bloc, Stream.value(state), initialState: state);
return bloc;
},
assets: assets,
);
group('ScoringBehavior', () {
test('can be instantiated', () {
expect(
ScoringBehavior(
points: Points.fiveThousand,
position: Vector2.zero(),
),
isA<ScoringBehavior>(),
);
});
flameBlocTester.testGameWidget(
'can be loaded',
setUp: (game, tester) async {
final canvas = ZCanvasComponent(children: [parent]);
final behavior = ScoringBehavior(
points: Points.fiveThousand,
position: Vector2.zero(),
);
await parent.add(behavior);
await game.ensureAdd(canvas);
expect(
parent.firstChild<ScoringBehavior>(),
equals(behavior),
);
},
);
flameBlocTester.testGameWidget(
'emits Scored event with points when added',
setUp: (game, tester) async {
const points = Points.oneMillion;
final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
final behavior = ScoringBehavior(
points: points,
position: Vector2(0, 0),
);
await parent.ensureAdd(behavior);
verify(
() => bloc.add(
Scored(points: points.value),
),
).called(1);
},
);
flameBlocTester.testGameWidget(
'correctly renders text',
setUp: (game, tester) async {
final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
const points = Points.oneMillion;
final position = Vector2.all(1);
final behavior = ScoringBehavior(
points: points,
position: position,
);
await parent.ensureAdd(behavior);
final scoreText = game.descendants().whereType<ScoreComponent>();
expect(scoreText.length, equals(1));
expect(
scoreText.first.points,
equals(points),
);
expect(
scoreText.first.position,
equals(position),
);
},
);
flameBlocTester.testGameWidget(
'is removed after duration',
setUp: (game, tester) async {
final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
const duration = 2.0;
final behavior = ScoringBehavior(
points: Points.oneMillion,
position: Vector2(0, 0),
duration: duration,
);
await parent.ensureAdd(behavior);
game.update(duration);
game.update(0);
await tester.pump();
},
verify: (game, _) async {
expect(
game.descendants().whereType<ScoringBehavior>(),
isEmpty,
);
},
);
});
group('ScoringContactBehavior', () {
flameBlocTester.testGameWidget(
'beginContact adds a ScoringBehavior',
setUp: (game, tester) async {
final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
final behavior = ScoringContactBehavior(points: Points.oneMillion);
await parent.ensureAdd(behavior);
behavior.beginContact(ball, _MockContact());
await game.ready();
expect(
parent.firstChild<ScoringBehavior>(),
isNotNull,
);
},
);
flameBlocTester.testGameWidget(
"beginContact positions text at contact's position",
setUp: (game, tester) async {
final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
final behavior = ScoringContactBehavior(points: Points.oneMillion);
await parent.ensureAdd(behavior);
behavior.beginContact(ball, _MockContact());
await game.ready();
final scoreText = game.descendants().whereType<ScoreComponent>();
expect(
scoreText.first.position,
equals(ball.body.position),
);
},
);
});
}

@ -2,6 +2,7 @@
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball/game/behaviors/bumper_noisy_behavior.dart';
import 'package:pinball/game/components/android_acres/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
@ -33,11 +34,12 @@ void main() {
Assets.images.android.bumper.cow.lit.keyName,
Assets.images.android.bumper.cow.dimmed.keyName,
];
final flameTester = FlameTester(
() => EmptyPinballTestGame(assets: assets),
);
group('AndroidAcres', () {
final flameTester = FlameTester(
() => EmptyPinballTestGame(assets: assets),
);
flameTester.test('loads correctly', (game) async {
final component = AndroidAcres();
await game.ensureAdd(component);
@ -99,6 +101,20 @@ void main() {
);
},
);
flameTester.test(
'three AndroidBumpers with BumperNoisyBehavior',
(game) async {
await game.ensureAdd(AndroidAcres());
final bumpers = game.descendants().whereType<AndroidBumper>();
for (final bumper in bumpers) {
expect(
bumper.firstChild<BumperNoisyBehavior>(),
isNotNull,
);
}
},
);
});
flameTester.test('adds an AndroidSpaceshipBonusBehavior', (game) async {

@ -1,6 +1,7 @@
// ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart';
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
@ -99,6 +100,7 @@ void main() {
group('turboCharge', () {
setUpAll(() {
registerFallbackValue(Vector2.zero());
registerFallbackValue(Component());
});
flameBlocTester.testGameWidget(
@ -124,7 +126,7 @@ void main() {
final controller = _WrappedBallController(ball, gameRef);
when(() => gameRef.read<GameBloc>()).thenReturn(gameBloc);
when(() => ball.controller).thenReturn(controller);
when(() => ball.boost(any())).thenAnswer((_) async {});
when(() => ball.add(any())).thenAnswer((_) async {});
await controller.turboCharge();
@ -140,29 +142,13 @@ void main() {
final controller = _WrappedBallController(ball, gameRef);
when(() => gameRef.read<GameBloc>()).thenReturn(gameBloc);
when(() => ball.controller).thenReturn(controller);
when(() => ball.boost(any())).thenAnswer((_) async {});
when(() => ball.add(any())).thenAnswer((_) async {});
await controller.turboCharge();
verify(ball.resume).called(1);
},
);
flameBlocTester.test(
'boosts the ball',
(game) async {
final gameRef = _MockPinballGame();
final ball = _MockControlledBall();
final controller = _WrappedBallController(ball, gameRef);
when(() => gameRef.read<GameBloc>()).thenReturn(gameBloc);
when(() => ball.controller).thenReturn(controller);
when(() => ball.boost(any())).thenAnswer((_) async {});
await controller.turboCharge();
verify(() => ball.boost(any())).called(1);
},
);
});
});
}

@ -2,6 +2,7 @@
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/components/dino_desert/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
@ -67,13 +68,13 @@ void main() {
group('adds', () {
flameTester.test(
'ScoringBehavior to ChromeDino',
'ScoringContactBehavior to ChromeDino',
(game) async {
await game.ensureAdd(DinoDesert());
final chromeDino = game.descendants().whereType<ChromeDino>().single;
expect(
chromeDino.firstChild<ScoringBehavior>(),
chromeDino.firstChild<ScoringContactBehavior>(),
isNotNull,
);
},

@ -2,6 +2,7 @@
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
@ -73,6 +74,21 @@ void main() {
);
},
);
flameTester.test(
'three DashNestBumpers with BumperNoisyBehavior',
(game) async {
final flutterForest = FlutterForest();
await game.ensureAdd(ZCanvasComponent(children: [flutterForest]));
final bumpers = game.descendants().whereType<DashNestBumper>();
for (final bumper in bumpers) {
expect(
bumper.firstChild<BumperNoisyBehavior>(),
isNotNull,
);
}
},
);
});
});
}

@ -1,172 +0,0 @@
// ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
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';
class _TestBodyComponent extends BodyComponent {
@override
Body createBody() => world.createBody(BodyDef());
}
class _MockPinballAudio extends Mock implements PinballAudio {}
class _MockBall extends Mock implements Ball {}
class _MockBody extends Mock implements Body {}
class _MockGameBloc extends Mock implements GameBloc {}
class _MockContact extends Mock implements Contact {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final assets = [
Assets.images.score.fiveThousand.keyName,
Assets.images.score.twentyThousand.keyName,
Assets.images.score.twoHundredThousand.keyName,
Assets.images.score.oneMillion.keyName,
];
group('ScoringBehavior', () {
group('beginContact', () {
late GameBloc bloc;
late PinballAudio audio;
late Ball ball;
late BodyComponent parent;
setUp(() {
audio = _MockPinballAudio();
ball = _MockBall();
final ballBody = _MockBody();
when(() => ball.body).thenReturn(ballBody);
when(() => ballBody.position).thenReturn(Vector2.all(4));
parent = _TestBodyComponent();
});
final flameBlocTester = FlameBlocTester<EmptyPinballTestGame, GameBloc>(
gameBuilder: () => EmptyPinballTestGame(
audio: audio,
),
blocBuilder: () {
bloc = _MockGameBloc();
const state = GameState(
score: 0,
multiplier: 1,
rounds: 3,
bonusHistory: [],
);
whenListen(bloc, Stream.value(state), initialState: state);
return bloc;
},
assets: assets,
);
flameBlocTester.testGameWidget(
'emits Scored event with points',
setUp: (game, tester) async {
const points = Points.oneMillion;
final scoringBehavior = ScoringBehavior(points: points);
await parent.add(scoringBehavior);
final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
scoringBehavior.beginContact(ball, _MockContact());
verify(
() => bloc.add(
Scored(points: points.value),
),
).called(1);
},
);
flameBlocTester.testGameWidget(
"adds a ScoreComponent at Ball's position with points",
setUp: (game, tester) async {
const points = Points.oneMillion;
final scoringBehavior = ScoringBehavior(points: points);
await parent.add(scoringBehavior);
final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
scoringBehavior.beginContact(ball, _MockContact());
await game.ready();
final scoreText = game.descendants().whereType<ScoreComponent>();
expect(scoreText.length, equals(1));
expect(
scoreText.first.points,
equals(points),
);
expect(
scoreText.first.position,
equals(ball.body.position),
);
},
);
});
});
group('BumperScoringBehavior', () {
group('beginContact', () {
late GameBloc bloc;
late PinballAudio audio;
late Ball ball;
late BodyComponent parent;
setUp(() {
audio = _MockPinballAudio();
ball = _MockBall();
final ballBody = _MockBody();
when(() => ball.body).thenReturn(ballBody);
when(() => ballBody.position).thenReturn(Vector2.all(4));
parent = _TestBodyComponent();
});
final flameBlocTester = FlameBlocTester<EmptyPinballTestGame, GameBloc>(
gameBuilder: () => EmptyPinballTestGame(
audio: audio,
),
blocBuilder: () {
bloc = _MockGameBloc();
const state = GameState(
score: 0,
multiplier: 1,
rounds: 3,
bonusHistory: [],
);
whenListen(bloc, Stream.value(state), initialState: state);
return bloc;
},
assets: assets,
);
flameBlocTester.testGameWidget(
'plays bumper sound',
setUp: (game, tester) async {
final scoringBehavior = BumperScoringBehavior(
points: Points.oneMillion,
);
await parent.add(scoringBehavior);
final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
scoringBehavior.beginContact(ball, _MockContact());
verify(audio.bumper).called(1);
},
);
});
});
}

@ -4,6 +4,7 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
@ -74,6 +75,20 @@ void main() {
);
},
);
flameTester.test(
'three SparkyBumpers with BumperNoisyBehavior',
(game) async {
await game.ensureAdd(SparkyScorch());
final bumpers = game.descendants().whereType<SparkyBumper>();
for (final bumper in bumpers) {
expect(
bumper.firstChild<BumperNoisyBehavior>(),
isNotNull,
);
}
},
);
});
});

@ -139,19 +139,16 @@ void main() {
);
});
final flameTester = FlameTester(
() => PinballTestGame(assets: assets),
);
final debugModeFlameTester = FlameTester(
() => DebugPinballTestGame(assets: assets),
);
final flameBlocTester = FlameBlocTester<PinballGame, GameBloc>(
gameBuilder: () => PinballTestGame(assets: assets),
blocBuilder: () => gameBloc,
);
group('PinballGame', () {
final flameTester = FlameTester(
() => PinballTestGame(assets: assets),
);
final flameBlocTester = FlameBlocTester<PinballGame, GameBloc>(
gameBuilder: () => PinballTestGame(assets: assets),
blocBuilder: () => gameBloc,
);
group('components', () {
// TODO(alestiago): tests that Blueprints get added once the Blueprint
// class is removed.
@ -246,6 +243,8 @@ void main() {
final newState = _MockGameState();
when(() => newState.isGameOver).thenReturn(false);
await game.ready();
expect(
game.descendants().whereType<ControlledBall>().length,
greaterThan(0),
@ -438,6 +437,10 @@ void main() {
});
group('DebugPinballGame', () {
final debugModeFlameTester = FlameTester(
() => DebugPinballTestGame(assets: assets),
);
debugModeFlameTester.test(
'adds a ball on tap up',
(game) async {
@ -451,6 +454,7 @@ void main() {
when(() => tapUpEvent.eventPosition).thenReturn(eventPosition);
when(() => tapUpEvent.raw).thenReturn(raw);
await game.ready();
final previousBalls =
game.descendants().whereType<ControlledBall>().toList();

Loading…
Cancel
Save