feat: adds Sprite states to SignPost (#205)
* feat: included new assets * chore: update game assets loader * feat: allowed progressing sprite * feat(sanbox): updated games * feat: updated tests * feat: updated goldens * feat: renamed FlutterSignPost to SignPost * chore: updated goldens * chore: renamed story * refactor: removed caching * refactor: changed how Sprites are loaded * feat: made SignpostSpriteState visibleForTesting * refactor: rename SignPost to Signpost * chore: updated goldens * refactor: improved test name Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * docs: documented new Signpost functionality Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * docs: improved progress doc * feat: removed loop Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>pull/211/head
Before Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 15 KiB |
@ -1,43 +0,0 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template flutter_sign_post}
|
||||
/// A sign, found in the Flutter Forest.
|
||||
/// {@endtemplate}
|
||||
class FlutterSignPost extends BodyComponent with InitialPosition {
|
||||
/// {@macro flutter_sign_post}
|
||||
FlutterSignPost()
|
||||
: super(
|
||||
priority: RenderPriority.flutterSignPost,
|
||||
children: [_FlutterSignPostSpriteComponent()],
|
||||
) {
|
||||
renderBody = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Body createBody() {
|
||||
final shape = CircleShape()..radius = 0.25;
|
||||
final fixtureDef = FixtureDef(shape);
|
||||
final bodyDef = BodyDef(
|
||||
position: initialPosition,
|
||||
);
|
||||
|
||||
return world.createBody(bodyDef)..createFixture(fixtureDef);
|
||||
}
|
||||
}
|
||||
|
||||
class _FlutterSignPostSpriteComponent extends SpriteComponent with HasGameRef {
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final sprite = await gameRef.loadSprite(
|
||||
Assets.images.flutterSignPost.keyName,
|
||||
);
|
||||
this.sprite = sprite;
|
||||
size = sprite.originalSize / 10;
|
||||
anchor = Anchor.bottomCenter;
|
||||
position = Vector2(0.65, 0.45);
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// Represents the [Signpost]'s current [Sprite] state.
|
||||
@visibleForTesting
|
||||
enum SignpostSpriteState {
|
||||
/// Signpost with no active dashes.
|
||||
inactive,
|
||||
|
||||
/// Signpost with a single sign of active dashes.
|
||||
active1,
|
||||
|
||||
/// Signpost with two signs of active dashes.
|
||||
active2,
|
||||
|
||||
/// Signpost with all signs of active dashes.
|
||||
active3,
|
||||
}
|
||||
|
||||
extension on SignpostSpriteState {
|
||||
String get path {
|
||||
switch (this) {
|
||||
case SignpostSpriteState.inactive:
|
||||
return Assets.images.signpost.inactive.keyName;
|
||||
case SignpostSpriteState.active1:
|
||||
return Assets.images.signpost.active1.keyName;
|
||||
case SignpostSpriteState.active2:
|
||||
return Assets.images.signpost.active2.keyName;
|
||||
case SignpostSpriteState.active3:
|
||||
return Assets.images.signpost.active3.keyName;
|
||||
}
|
||||
}
|
||||
|
||||
SignpostSpriteState get next {
|
||||
return SignpostSpriteState
|
||||
.values[(index + 1) % SignpostSpriteState.values.length];
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template signpost}
|
||||
/// A sign, found in the Flutter Forest.
|
||||
///
|
||||
/// Lights up a new sign whenever all three [DashNestBumper]s are hit.
|
||||
/// {@endtemplate}
|
||||
class Signpost extends BodyComponent with InitialPosition {
|
||||
/// {@macro signpost}
|
||||
Signpost()
|
||||
: super(
|
||||
priority: RenderPriority.signpost,
|
||||
children: [_SignpostSpriteComponent()],
|
||||
) {
|
||||
renderBody = false;
|
||||
}
|
||||
|
||||
/// Forwards the sprite to the next [SignpostSpriteState].
|
||||
///
|
||||
/// If the current state is the last one it cycles back to the initial state.
|
||||
void progress() => firstChild<_SignpostSpriteComponent>()!.progress();
|
||||
|
||||
@override
|
||||
Body createBody() {
|
||||
final shape = CircleShape()..radius = 0.25;
|
||||
final fixtureDef = FixtureDef(shape);
|
||||
final bodyDef = BodyDef(
|
||||
position: initialPosition,
|
||||
);
|
||||
|
||||
return world.createBody(bodyDef)..createFixture(fixtureDef);
|
||||
}
|
||||
}
|
||||
|
||||
class _SignpostSpriteComponent extends SpriteGroupComponent<SignpostSpriteState>
|
||||
with HasGameRef {
|
||||
_SignpostSpriteComponent()
|
||||
: super(
|
||||
anchor: Anchor.bottomCenter,
|
||||
position: Vector2(0.65, 0.45),
|
||||
);
|
||||
|
||||
void progress() => current = current?.next;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final sprites = <SignpostSpriteState, Sprite>{};
|
||||
this.sprites = sprites;
|
||||
for (final spriteState in SignpostSpriteState.values) {
|
||||
// TODO(allisonryan0002): Support caching
|
||||
// https://github.com/VGVentures/pinball/pull/204
|
||||
// sprites[spriteState] = Sprite(
|
||||
// gameRef.images.fromCache(spriteState.path),
|
||||
// );
|
||||
sprites[spriteState] = await gameRef.loadSprite(spriteState.path);
|
||||
}
|
||||
|
||||
current = SignpostSpriteState.inactive;
|
||||
size = sprites[current]!.originalSize / 10;
|
||||
}
|
||||
}
|
@ -1,22 +1,30 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
import 'package:sandbox/stories/ball/basic_ball_game.dart';
|
||||
|
||||
class FlutterSignPostGame extends BasicBallGame with Traceable {
|
||||
class SignpostGame extends BasicBallGame with Traceable, TapDetector {
|
||||
static const info = '''
|
||||
Shows how a FlutterSignPost is rendered.
|
||||
Shows how a Signpost is rendered.
|
||||
|
||||
- Activate the "trace" parameter to overlay the body.
|
||||
- Tap to progress the sprite.
|
||||
''';
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
camera.followVector2(Vector2.zero());
|
||||
await add(FlutterSignPost()..priority = 1);
|
||||
await add(Signpost()..priority = 1);
|
||||
await traceAllBodies();
|
||||
}
|
||||
|
||||
@override
|
||||
void onTap() {
|
||||
super.onTap();
|
||||
firstChild<Signpost>()!.progress();
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
// 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:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final flameTester = FlameTester(TestGame.new);
|
||||
|
||||
group('FlutterSignPost', () {
|
||||
flameTester.testGameWidget(
|
||||
'renders correctly',
|
||||
setUp: (game, tester) async {
|
||||
await game.ensureAdd(FlutterSignPost());
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/flutter-sign-post.png'),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'loads correctly',
|
||||
(game) async {
|
||||
final flutterSignPost = FlutterSignPost();
|
||||
await game.ready();
|
||||
await game.ensureAdd(flutterSignPost);
|
||||
|
||||
expect(game.contains(flutterSignPost), isTrue);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 41 KiB |
@ -0,0 +1,141 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
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 '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final flameTester = FlameTester(TestGame.new);
|
||||
|
||||
group('Signpost', () {
|
||||
flameTester.test(
|
||||
'loads correctly',
|
||||
(game) async {
|
||||
final signpost = Signpost();
|
||||
await game.ready();
|
||||
await game.ensureAdd(signpost);
|
||||
|
||||
expect(game.contains(signpost), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
group('renders correctly', () {
|
||||
flameTester.testGameWidget(
|
||||
'inactive sprite',
|
||||
setUp: (game, tester) async {
|
||||
final signpost = Signpost();
|
||||
await game.ensureAdd(signpost);
|
||||
|
||||
expect(
|
||||
signpost.firstChild<SpriteGroupComponent>()!.current,
|
||||
SignpostSpriteState.inactive,
|
||||
);
|
||||
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/signpost/inactive.png'),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'active1 sprite',
|
||||
setUp: (game, tester) async {
|
||||
final signpost = Signpost();
|
||||
await game.ensureAdd(signpost);
|
||||
signpost.progress();
|
||||
|
||||
expect(
|
||||
signpost.firstChild<SpriteGroupComponent>()!.current,
|
||||
SignpostSpriteState.active1,
|
||||
);
|
||||
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/signpost/active1.png'),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'active2 sprite',
|
||||
setUp: (game, tester) async {
|
||||
final signpost = Signpost();
|
||||
await game.ensureAdd(signpost);
|
||||
signpost
|
||||
..progress()
|
||||
..progress();
|
||||
|
||||
expect(
|
||||
signpost.firstChild<SpriteGroupComponent>()!.current,
|
||||
SignpostSpriteState.active2,
|
||||
);
|
||||
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/signpost/active2.png'),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'active3 sprite',
|
||||
setUp: (game, tester) async {
|
||||
final signpost = Signpost();
|
||||
await game.ensureAdd(signpost);
|
||||
signpost
|
||||
..progress()
|
||||
..progress()
|
||||
..progress();
|
||||
|
||||
expect(
|
||||
signpost.firstChild<SpriteGroupComponent>()!.current,
|
||||
SignpostSpriteState.active3,
|
||||
);
|
||||
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/signpost/active3.png'),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
flameTester.test(
|
||||
'progress correctly cycles through all sprites',
|
||||
(game) async {
|
||||
final signpost = Signpost();
|
||||
await game.ready();
|
||||
await game.ensureAdd(signpost);
|
||||
|
||||
final spriteComponent = signpost.firstChild<SpriteGroupComponent>()!;
|
||||
|
||||
expect(spriteComponent.current, SignpostSpriteState.inactive);
|
||||
signpost.progress();
|
||||
expect(spriteComponent.current, SignpostSpriteState.active1);
|
||||
signpost.progress();
|
||||
expect(spriteComponent.current, SignpostSpriteState.active2);
|
||||
signpost.progress();
|
||||
expect(spriteComponent.current, SignpostSpriteState.active3);
|
||||
signpost.progress();
|
||||
expect(spriteComponent.current, SignpostSpriteState.inactive);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|