feat: defined `ScoringBehavior` and `ScoringContactBehavior` (#329)

* refactor: renamed ScoringBehavior to ScoringContactBehavior

* feat: defined ScoringBehavior

* docs: improved documentation
pull/333/head
Alejandro Santiago 3 years ago committed by GitHub
parent 41aaaa4646
commit 8957b96ae3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,18 +1,63 @@
// 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 when the [Ball] contacts the [parent].
/// Adds [_points] to the score and shows a text effect.
///
/// The behavior removes itself after the duration.
/// {@endtemplate}
class ScoringBehavior extends ContactBehavior with HasGameRef<PinballGame> {
/// {@macro scoring_behavior}
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;
@ -22,9 +67,8 @@ class ScoringBehavior extends ContactBehavior with HasGameRef<PinballGame> {
super.beginContact(other, contact);
if (other is! Ball) return;
gameRef.read<GameBloc>().add(Scored(points: _points.value));
gameRef.firstChild<ZCanvasComponent>()!.add(
ScoreComponent(
parent.add(
ScoringBehavior(
points: _points,
position: other.body.position,
),

@ -20,24 +20,24 @@ 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: [
ScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-25, 1.3),
AndroidBumper.b(
children: [
ScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-32.8, -9.2),
AndroidBumper.cow(
children: [
ScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-20.5, -13.8),

@ -52,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,

@ -17,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),

@ -18,25 +18,25 @@ class FlutterForest extends Component with ZIndex {
children: [
Signpost(
children: [
ScoringBehavior(points: Points.fiveThousand),
ScoringContactBehavior(points: Points.fiveThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(8.35, -58.3),
DashNestBumper.main(
children: [
ScoringBehavior(points: Points.twoHundredThousand),
ScoringContactBehavior(points: Points.twoHundredThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(18.55, -59.35),
DashNestBumper.a(
children: [
ScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(8.95, -51.95),
DashNestBumper.b(
children: [
ScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(22.3, -46.75),

@ -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(),
],

@ -17,19 +17,19 @@ class SparkyScorch extends Component {
children: [
SparkyBumper.a(
children: [
ScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-22.9, -41.65),
SparkyBumper.b(
children: [
ScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-21.25, -57.9),
SparkyBumper.c(
children: [
ScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoisyBehavior(),
],
)..initialPosition = Vector2(-3.3, -52.55),
@ -51,7 +51,7 @@ class SparkyComputerSensor extends BodyComponent
: super(
renderBody: false,
children: [
ScoringBehavior(points: Points.twentyThousand),
ScoringContactBehavior(points: Points.twentyThousand),
],
);

@ -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,
),
);
}

@ -1,3 +1,4 @@
import 'package:flame/effects.dart';
import 'package:flame/input.dart';
import 'package:pinball_components/pinball_components.dart' as components;
import 'package:pinball_theme/pinball_theme.dart';
@ -52,6 +53,7 @@ class BackboardGameOverGame extends AssetsGame
points: components.Points.values
.firstWhere((element) => element.value == score),
position: Vector2(0, 50),
effectController: EffectController(duration: 1),
),
);
},

@ -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),
),
);
}

@ -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),
),
);

@ -34,8 +34,6 @@ void main() {
Assets.images.score.oneMillion.keyName,
];
group('ScoringBehavior', () {
group('beginContact', () {
late GameBloc bloc;
late Ball ball;
late BodyComponent parent;
@ -65,16 +63,47 @@ void main() {
assets: assets,
);
group('ScoringBehavior', () {
test('can be instantiated', () {
expect(
ScoringBehavior(
points: Points.fiveThousand,
position: Vector2.zero(),
),
isA<ScoringBehavior>(),
);
});
flameBlocTester.testGameWidget(
'emits Scored event with points',
'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 scoringBehavior = ScoringBehavior(points: points);
await parent.add(scoringBehavior);
final canvas = ZCanvasComponent(children: [parent]);
await game.ensureAdd(canvas);
scoringBehavior.beginContact(ball, _MockContact());
final behavior = ScoringBehavior(
points: points,
position: Vector2(0, 0),
);
await parent.ensureAdd(behavior);
verify(
() => bloc.add(
@ -85,16 +114,18 @@ void main() {
);
flameBlocTester.testGameWidget(
"adds a ScoreComponent at Ball's position with points",
'correctly renders text',
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();
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));
@ -104,10 +135,76 @@ void main() {
);
expect(
scoreText.first.position,
equals(ball.body.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),
);
},
);
});
}

@ -68,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,
);
},

Loading…
Cancel
Save