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