feat: add score assets (#276)
* feat: added assets for scores * refactor: modified scoretext component to show sprite with the score * refactor: changed all ocurrences of scoretext to use score enum as value * feat: extension to get value * test: refactor score texts * refactor: refactored sandbox for scoretexts * refactor: moved score_text to score_component * refactor: score enum to Points * test: golden tests for points * chore: unused imports * chore: unused imports * test: coverage for points extension * refactor: removed unused points and changed enum names * test: golden test for kept scores * Update test/game/components/scoring_behavior_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * refactor: score priority over sparky animatronic * test: reorder tests * chore: removed empty test group * fix: missed dino desert points Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>pull/284/head
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 19 KiB |
@ -0,0 +1,92 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame/effects.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:pinball_flame/pinball_flame.dart';
|
||||||
|
|
||||||
|
enum Points {
|
||||||
|
fiveThousand,
|
||||||
|
twentyThousand,
|
||||||
|
twoHundredThousand,
|
||||||
|
oneMillion,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// {@template score_component}
|
||||||
|
/// A [ScoreComponent] that spawns at a given [position] with a moving
|
||||||
|
/// animation.
|
||||||
|
/// {@endtemplate}
|
||||||
|
class ScoreComponent extends SpriteComponent with HasGameRef, ZIndex {
|
||||||
|
/// {@macro score_component}
|
||||||
|
ScoreComponent({
|
||||||
|
required this.points,
|
||||||
|
required Vector2 position,
|
||||||
|
}) : super(
|
||||||
|
position: position,
|
||||||
|
anchor: Anchor.center,
|
||||||
|
) {
|
||||||
|
zIndex = ZIndexes.score;
|
||||||
|
}
|
||||||
|
|
||||||
|
late final Effect _effect;
|
||||||
|
|
||||||
|
late Points points;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onLoad() async {
|
||||||
|
await super.onLoad();
|
||||||
|
final sprite = Sprite(
|
||||||
|
gameRef.images.fromCache(points.asset),
|
||||||
|
);
|
||||||
|
this.sprite = sprite;
|
||||||
|
size = sprite.originalSize / 55;
|
||||||
|
|
||||||
|
await add(
|
||||||
|
_effect = MoveEffect.by(
|
||||||
|
Vector2(0, -5),
|
||||||
|
EffectController(duration: 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(double dt) {
|
||||||
|
super.update(dt);
|
||||||
|
|
||||||
|
if (_effect.controller.completed) {
|
||||||
|
removeFromParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PointsX on Points {
|
||||||
|
int get value {
|
||||||
|
switch (this) {
|
||||||
|
case Points.fiveThousand:
|
||||||
|
return 5000;
|
||||||
|
case Points.twentyThousand:
|
||||||
|
return 20000;
|
||||||
|
case Points.twoHundredThousand:
|
||||||
|
return 200000;
|
||||||
|
case Points.oneMillion:
|
||||||
|
return 1000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on Points {
|
||||||
|
String get asset {
|
||||||
|
switch (this) {
|
||||||
|
case Points.fiveThousand:
|
||||||
|
return Assets.images.score.fiveThousand.keyName;
|
||||||
|
case Points.twentyThousand:
|
||||||
|
return Assets.images.score.twentyThousand.keyName;
|
||||||
|
case Points.twoHundredThousand:
|
||||||
|
return Assets.images.score.twoHundredThousand.keyName;
|
||||||
|
case Points.oneMillion:
|
||||||
|
return Assets.images.score.oneMillion.keyName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,57 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
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 with ZIndex {
|
|
||||||
/// {@macro score_text}
|
|
||||||
ScoreText({
|
|
||||||
required String text,
|
|
||||||
required Vector2 position,
|
|
||||||
this.color = Colors.black,
|
|
||||||
}) : super(
|
|
||||||
text: text,
|
|
||||||
position: position,
|
|
||||||
anchor: Anchor.center,
|
|
||||||
) {
|
|
||||||
zIndex = ZIndexes.scoreText;
|
|
||||||
}
|
|
||||||
|
|
||||||
late final Effect _effect;
|
|
||||||
|
|
||||||
/// The [text]'s [Color].
|
|
||||||
final Color color;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> onLoad() async {
|
|
||||||
textRenderer = TextPaint(
|
|
||||||
style: TextStyle(
|
|
||||||
fontFamily: PinballFonts.pixeloidMono,
|
|
||||||
color: color,
|
|
||||||
fontSize: 4,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await add(
|
|
||||||
_effect = MoveEffect.by(
|
|
||||||
Vector2(0, -5),
|
|
||||||
EffectController(duration: 1),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void update(double dt) {
|
|
||||||
super.update(dt);
|
|
||||||
|
|
||||||
if (_effect.controller.completed) {
|
|
||||||
removeFromParent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,44 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flame/input.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:sandbox/common/common.dart';
|
||||||
|
|
||||||
|
class ScoreGame extends AssetsGame with TapDetector {
|
||||||
|
ScoreGame()
|
||||||
|
: super(
|
||||||
|
imagesFileNames: [
|
||||||
|
Assets.images.score.fiveThousand.keyName,
|
||||||
|
Assets.images.score.twentyThousand.keyName,
|
||||||
|
Assets.images.score.twoHundredThousand.keyName,
|
||||||
|
Assets.images.score.oneMillion.keyName,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
static const description = '''
|
||||||
|
Simple game to show how score component works,
|
||||||
|
|
||||||
|
- Tap anywhere on the screen to spawn an image on the given location.
|
||||||
|
''';
|
||||||
|
|
||||||
|
final random = Random();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onLoad() async {
|
||||||
|
await super.onLoad();
|
||||||
|
camera.followVector2(Vector2.zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onTapUp(TapUpInfo info) {
|
||||||
|
final index = random.nextInt(Points.values.length);
|
||||||
|
final score = Points.values[index];
|
||||||
|
|
||||||
|
add(
|
||||||
|
ScoreComponent(
|
||||||
|
points: score,
|
||||||
|
position: info.eventPosition.game..multiply(Vector2(1, -1)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:dashbook/dashbook.dart';
|
||||||
|
import 'package:sandbox/common/common.dart';
|
||||||
|
import 'package:sandbox/stories/score/score_game.dart';
|
||||||
|
|
||||||
|
void addScoreStories(Dashbook dashbook) {
|
||||||
|
dashbook.storiesOf('Score').addGame(
|
||||||
|
title: 'Basic',
|
||||||
|
description: ScoreGame.description,
|
||||||
|
gameBuilder: (_) => ScoreGame(),
|
||||||
|
);
|
||||||
|
}
|
@ -1,32 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:flame/input.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:pinball_components/pinball_components.dart';
|
|
||||||
import 'package:sandbox/common/common.dart';
|
|
||||||
|
|
||||||
class ScoreTextGame extends AssetsGame with TapDetector {
|
|
||||||
static const description = '''
|
|
||||||
Simple game to show how score text works,
|
|
||||||
|
|
||||||
- Tap anywhere on the screen to spawn an text on the given location.
|
|
||||||
''';
|
|
||||||
|
|
||||||
final random = Random();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> onLoad() async {
|
|
||||||
camera.followVector2(Vector2.zero());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onTapUp(TapUpInfo info) {
|
|
||||||
add(
|
|
||||||
ScoreText(
|
|
||||||
text: random.nextInt(100000).toString(),
|
|
||||||
color: Colors.white,
|
|
||||||
position: info.eventPosition.game..multiply(Vector2(1, -1)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import 'package:dashbook/dashbook.dart';
|
|
||||||
import 'package:sandbox/common/common.dart';
|
|
||||||
import 'package:sandbox/stories/score_text/score_text_game.dart';
|
|
||||||
|
|
||||||
void addScoreTextStories(Dashbook dashbook) {
|
|
||||||
dashbook.storiesOf('ScoreText').addGame(
|
|
||||||
title: 'Basic',
|
|
||||||
description: ScoreTextGame.description,
|
|
||||||
gameBuilder: (_) => ScoreTextGame(),
|
|
||||||
);
|
|
||||||
}
|
|
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 27 KiB |
@ -0,0 +1,202 @@
|
|||||||
|
// ignore_for_file: cascade_invocations
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame/effects.dart';
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
import '../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
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,
|
||||||
|
];
|
||||||
|
final flameTester = FlameTester(() => TestGame(assets));
|
||||||
|
|
||||||
|
group('ScoreComponent', () {
|
||||||
|
flameTester.testGameWidget(
|
||||||
|
'loads correctly',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
await game.images.loadAll(assets);
|
||||||
|
game.camera.followVector2(Vector2.zero());
|
||||||
|
await game.ensureAdd(
|
||||||
|
ScoreComponent(
|
||||||
|
points: Points.oneMillion,
|
||||||
|
position: Vector2.zero(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
final texts = game.descendants().whereType<SpriteComponent>().length;
|
||||||
|
expect(texts, equals(1));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.testGameWidget(
|
||||||
|
'has a movement effect',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
await game.images.loadAll(assets);
|
||||||
|
game.camera.followVector2(Vector2.zero());
|
||||||
|
await game.ensureAdd(
|
||||||
|
ScoreComponent(
|
||||||
|
points: Points.oneMillion,
|
||||||
|
position: Vector2.zero(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
game.update(0.5);
|
||||||
|
await tester.pump();
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
final text = game.descendants().whereType<SpriteComponent>().first;
|
||||||
|
expect(text.firstChild<MoveEffect>(), isNotNull);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.testGameWidget(
|
||||||
|
'is removed once finished',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
await game.images.loadAll(assets);
|
||||||
|
game.camera.followVector2(Vector2.zero());
|
||||||
|
await game.ensureAdd(
|
||||||
|
ScoreComponent(
|
||||||
|
points: Points.oneMillion,
|
||||||
|
position: Vector2.zero(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
game.update(1);
|
||||||
|
game.update(0); // Ensure all component removals
|
||||||
|
await tester.pump();
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
expect(game.children.length, equals(0));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
group('renders correctly', () {
|
||||||
|
flameTester.testGameWidget(
|
||||||
|
'5000 points',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
await game.images.loadAll(assets);
|
||||||
|
await game.ensureAdd(
|
||||||
|
ScoreComponent(
|
||||||
|
points: Points.fiveThousand,
|
||||||
|
position: Vector2.zero(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
game.camera
|
||||||
|
..followVector2(Vector2.zero())
|
||||||
|
..zoom = 8;
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
await expectLater(
|
||||||
|
find.byGame<TestGame>(),
|
||||||
|
matchesGoldenFile('golden/score/5k.png'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.testGameWidget(
|
||||||
|
'20000 points',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
await game.images.loadAll(assets);
|
||||||
|
await game.ensureAdd(
|
||||||
|
ScoreComponent(
|
||||||
|
points: Points.twentyThousand,
|
||||||
|
position: Vector2.zero(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
game.camera
|
||||||
|
..followVector2(Vector2.zero())
|
||||||
|
..zoom = 8;
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
await expectLater(
|
||||||
|
find.byGame<TestGame>(),
|
||||||
|
matchesGoldenFile('golden/score/20k.png'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.testGameWidget(
|
||||||
|
'200000 points',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
await game.images.loadAll(assets);
|
||||||
|
await game.ensureAdd(
|
||||||
|
ScoreComponent(
|
||||||
|
points: Points.twoHundredThousand,
|
||||||
|
position: Vector2.zero(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
game.camera
|
||||||
|
..followVector2(Vector2.zero())
|
||||||
|
..zoom = 8;
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
await expectLater(
|
||||||
|
find.byGame<TestGame>(),
|
||||||
|
matchesGoldenFile('golden/score/200k.png'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.testGameWidget(
|
||||||
|
'1000000 points',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
await game.images.loadAll(assets);
|
||||||
|
await game.ensureAdd(
|
||||||
|
ScoreComponent(
|
||||||
|
points: Points.oneMillion,
|
||||||
|
position: Vector2.zero(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
game.camera
|
||||||
|
..followVector2(Vector2.zero())
|
||||||
|
..zoom = 8;
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
await expectLater(
|
||||||
|
find.byGame<TestGame>(),
|
||||||
|
matchesGoldenFile('golden/score/1m.png'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('PointsX', () {
|
||||||
|
test('5k value return 5000', () {
|
||||||
|
expect(Points.fiveThousand.value, 5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('20k value return 20000', () {
|
||||||
|
expect(Points.twentyThousand.value, 20000);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('200k value return 200000', () {
|
||||||
|
expect(Points.twoHundredThousand.value, 200000);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('1m value return 1000000', () {
|
||||||
|
expect(Points.oneMillion.value, 1000000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -1,75 +0,0 @@
|
|||||||
// ignore_for_file: cascade_invocations
|
|
||||||
|
|
||||||
import 'package:flame/components.dart';
|
|
||||||
import 'package:flame/effects.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() {
|
|
||||||
group('ScoreText', () {
|
|
||||||
final flameTester = FlameTester(TestGame.new);
|
|
||||||
|
|
||||||
flameTester.testGameWidget(
|
|
||||||
'renders correctly',
|
|
||||||
setUp: (game, tester) async {
|
|
||||||
game.camera.followVector2(Vector2.zero());
|
|
||||||
await game.ensureAdd(
|
|
||||||
ScoreText(
|
|
||||||
text: '123',
|
|
||||||
position: Vector2.zero(),
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
verify: (game, tester) async {
|
|
||||||
final texts = game.descendants().whereType<TextComponent>().length;
|
|
||||||
expect(texts, equals(1));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
flameTester.testGameWidget(
|
|
||||||
'has a movement effect',
|
|
||||||
setUp: (game, tester) async {
|
|
||||||
game.camera.followVector2(Vector2.zero());
|
|
||||||
await game.ensureAdd(
|
|
||||||
ScoreText(
|
|
||||||
text: '123',
|
|
||||||
position: Vector2.zero(),
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
game.update(0.5);
|
|
||||||
await tester.pump();
|
|
||||||
},
|
|
||||||
verify: (game, tester) async {
|
|
||||||
final text = game.descendants().whereType<TextComponent>().first;
|
|
||||||
expect(text.firstChild<MoveEffect>(), isNotNull);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
flameTester.testGameWidget(
|
|
||||||
'is removed once finished',
|
|
||||||
setUp: (game, tester) async {
|
|
||||||
game.camera.followVector2(Vector2.zero());
|
|
||||||
await game.ensureAdd(
|
|
||||||
ScoreText(
|
|
||||||
text: '123',
|
|
||||||
position: Vector2.zero(),
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
game.update(1);
|
|
||||||
game.update(0); // Ensure all component removals
|
|
||||||
},
|
|
||||||
verify: (game, tester) async {
|
|
||||||
expect(game.children.length, equals(0));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|