feat: add sparky animatronic (#194)
* feat: add sparky animatronic * refactor: move computer into sparky fire zone * test: sparky animatronic and controller * chore: removed old test * test: remove test for sparky fire zone component * refactor: use onComplete * chore: included SparkyTurboChargeSensor * feat: specified priority * fix: specified render priority * refactor: spacing changes * refactor: used children param * feat: made SparkyFireZone a Blueprint * feat: updated assets * refactor: moved SparkyComputerSensor * test: fix contact callback tests * test: updated SparkyComputer loading test * test: updated golden animatronic tests * feat: modified turboCharge duration * fix: included missing cached image * fix: coverage * refactor: removed unused HasGameRef mixin * fix: included missing plunger assets * Update test/game/components/sparky_fire_zone_test.dart * fix: unused import Co-authored-by: alestiago <dev@alestiago.com>pull/228/head
@ -1,52 +0,0 @@
|
||||
// ignore_for_file: avoid_renaming_method_parameters
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template controlled_sparky_computer}
|
||||
/// [SparkyComputer] with a [SparkyComputerController] attached.
|
||||
/// {@endtemplate}
|
||||
class ControlledSparkyComputer extends SparkyComputer
|
||||
with Controls<SparkyComputerController>, HasGameRef<Forge2DGame> {
|
||||
/// {@macro controlled_sparky_computer}
|
||||
ControlledSparkyComputer() : super() {
|
||||
controller = SparkyComputerController(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
gameRef.addContactCallback(SparkyComputerSensorBallContactCallback());
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template sparky_computer_controller}
|
||||
/// Controller attached to a [SparkyComputer] that handles its game related
|
||||
/// logic.
|
||||
/// {@endtemplate}
|
||||
// TODO(allisonryan0002): listen for turbo charge game bonus and animate Sparky.
|
||||
class SparkyComputerController
|
||||
extends ComponentController<ControlledSparkyComputer> {
|
||||
/// {@macro sparky_computer_controller}
|
||||
SparkyComputerController(ControlledSparkyComputer controlledComputer)
|
||||
: super(controlledComputer);
|
||||
}
|
||||
|
||||
/// {@template sparky_computer_sensor_ball_contact_callback}
|
||||
/// Turbo charges the [Ball] when it enters the [SparkyComputer]
|
||||
/// {@endtemplate}
|
||||
@visibleForTesting
|
||||
class SparkyComputerSensorBallContactCallback
|
||||
extends ContactCallback<SparkyComputerSensor, ControlledBall> {
|
||||
/// {@macro sparky_computer_sensor_ball_contact_callback}
|
||||
SparkyComputerSensorBallContactCallback();
|
||||
|
||||
@override
|
||||
void begin(_, ControlledBall ball, __) {
|
||||
ball.controller.turboCharge();
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 365 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 7.1 KiB |
@ -0,0 +1,46 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template sparky_animatronic}
|
||||
/// Animated Sparky that sits on top of the [SparkyComputer].
|
||||
/// {@endtemplate}
|
||||
class SparkyAnimatronic extends SpriteAnimationComponent with HasGameRef {
|
||||
/// {@macro sparky_animatronic}
|
||||
SparkyAnimatronic()
|
||||
: super(
|
||||
anchor: Anchor.center,
|
||||
playing: false,
|
||||
priority: RenderPriority.sparkyAnimatronic,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final spriteSheet = gameRef.images.fromCache(
|
||||
Assets.images.sparky.animatronic.keyName,
|
||||
);
|
||||
|
||||
const amountPerRow = 9;
|
||||
const amountPerColumn = 7;
|
||||
final textureSize = Vector2(
|
||||
spriteSheet.width / amountPerRow,
|
||||
spriteSheet.height / amountPerColumn,
|
||||
);
|
||||
size = textureSize / 10;
|
||||
|
||||
animation = SpriteAnimation.fromFrameData(
|
||||
spriteSheet,
|
||||
SpriteAnimationData.sequenced(
|
||||
amount: (amountPerRow * amountPerColumn) - 1,
|
||||
amountPerRow: amountPerRow,
|
||||
stepTime: 1 / 24,
|
||||
textureSize: textureSize,
|
||||
loop: false,
|
||||
),
|
||||
)..onComplete = () {
|
||||
animation?.reset();
|
||||
playing = false;
|
||||
};
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 34 KiB |
@ -0,0 +1,76 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame/extensions.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();
|
||||
|
||||
group('SparkyAnimatronic', () {
|
||||
final asset = Assets.images.sparky.animatronic.keyName;
|
||||
final flameTester = FlameTester(() => TestGame([asset]));
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'renders correctly',
|
||||
setUp: (game, tester) async {
|
||||
await game.images.load(asset);
|
||||
await game.ensureAdd(SparkyAnimatronic()..playing = true);
|
||||
await tester.pump();
|
||||
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
final animationDuration =
|
||||
game.firstChild<SparkyAnimatronic>()!.animation!.totalDuration();
|
||||
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/sparky_animatronic/start.png'),
|
||||
);
|
||||
|
||||
game.update(animationDuration * 0.25);
|
||||
await tester.pump();
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/sparky_animatronic/middle.png'),
|
||||
);
|
||||
|
||||
game.update(animationDuration * 0.75);
|
||||
await tester.pump();
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/sparky_animatronic/end.png'),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'loads correctly',
|
||||
(game) async {
|
||||
final sparkyAnimatronic = SparkyAnimatronic();
|
||||
await game.ensureAdd(sparkyAnimatronic);
|
||||
|
||||
expect(game.contains(sparkyAnimatronic), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'stops animating after animation completes',
|
||||
(game) async {
|
||||
final sparkyAnimatronic = SparkyAnimatronic();
|
||||
await game.ensureAdd(sparkyAnimatronic);
|
||||
|
||||
sparkyAnimatronic.playing = true;
|
||||
final animationDuration =
|
||||
game.firstChild<SparkyAnimatronic>()!.animation!.totalDuration();
|
||||
game.update(animationDuration);
|
||||
|
||||
expect(sparkyAnimatronic.playing, isFalse);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
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 '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('ControlledSparkyComputer', () {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final flameTester = FlameTester(EmptyPinballTestGame.new);
|
||||
|
||||
flameTester.test('loads correctly', (game) async {
|
||||
final sparkyComputer = ControlledSparkyComputer();
|
||||
await game.ensureAdd(sparkyComputer);
|
||||
expect(game.children, contains(sparkyComputer));
|
||||
});
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'SparkyTurboChargeSensorBallContactCallback turbo charges the ball',
|
||||
setUp: (game, tester) async {
|
||||
final contackCallback = SparkyComputerSensorBallContactCallback();
|
||||
final sparkyTurboChargeSensor = MockSparkyComputerSensor();
|
||||
final ball = MockControlledBall();
|
||||
final controller = MockBallController();
|
||||
|
||||
when(() => ball.controller).thenReturn(controller);
|
||||
when(controller.turboCharge).thenAnswer((_) async {});
|
||||
|
||||
contackCallback.begin(sparkyTurboChargeSensor, ball, MockContact());
|
||||
|
||||
verify(() => ball.controller.turboCharge()).called(1);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|