refactor: remove arrow blinking from this pr

pull/416/head
RuiAlonso 3 years ago
parent 8817038f6a
commit f587911651

@ -34,7 +34,7 @@ class RampProgressBehavior extends Component with ParentIsA<SpaceshipRamp> {
if (spaceshipCubit.state.fullArrowLit && if (spaceshipCubit.state.fullArrowLit &&
!gameBloc.state.isMaxMultiplier) { !gameBloc.state.isMaxMultiplier) {
spaceshipCubit.onAnimate(); spaceshipCubit.onProgressed();
} }
}, },
), ),

@ -1,2 +1 @@
export 'ramp_arrow_blinking_behavior.dart';
export 'ramp_ball_ascending_contact_behavior.dart'; export 'ramp_ball_ascending_contact_behavior.dart';

@ -1,83 +0,0 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template ramp_arrow_blinking_behavior}
/// Makes a [SpaceshipRampArrowSpriteComponent] blink between
/// [ArrowLightState.values].
/// {@endtemplate}
class RampArrowBlinkingBehavior extends TimerComponent
with ParentIsA<SpaceshipRamp> {
/// {@macro ramp_arrow_blinking_behavior}
RampArrowBlinkingBehavior() : super(period: 0.05);
final _maxBlinks = 20;
int _blinksCounter = 0;
bool _isAnimating = false;
void _onNewState(SpaceshipRampState state) {
final animationEnabled =
state.animationState == ArrowAnimationState.blinking;
final canBlink = _blinksCounter < _maxBlinks;
if (animationEnabled && canBlink) {
_start();
} else {
_stop();
}
}
void _start() {
if (!_isAnimating) {
_isAnimating = true;
timer
..reset()
..start();
_animate();
}
}
void _animate() {
readBloc<SpaceshipRampCubit, SpaceshipRampState>().onBlink();
_blinksCounter++;
}
void _stop() {
if (_isAnimating) {
_isAnimating = false;
timer.stop();
_blinksCounter = 0;
readBloc<SpaceshipRampCubit, SpaceshipRampState>().onStop();
}
}
@override
Future<void> onLoad() async {
await super.onLoad();
await add(
FlameBlocListener<SpaceshipRampCubit, SpaceshipRampState>(
onNewState: _onNewState,
),
);
}
@override
void onTick() {
super.onTick();
if (!_isAnimating) {
timer.stop();
} else {
if (_blinksCounter < _maxBlinks) {
_animate();
timer
..reset()
..start();
} else {
timer.stop();
}
}
}
}

@ -19,7 +19,6 @@ class SpaceshipRampCubit extends Cubit<SpaceshipRampState> {
state.copyWith( state.copyWith(
lightState: lightState:
ArrowLightState.values[(index + 1) % ArrowLightState.values.length], ArrowLightState.values[(index + 1) % ArrowLightState.values.length],
animationState: ArrowAnimationState.idle,
), ),
); );
} }
@ -29,58 +28,7 @@ class SpaceshipRampCubit extends Cubit<SpaceshipRampState> {
const SpaceshipRampState( const SpaceshipRampState(
hits: 0, hits: 0,
lightState: ArrowLightState.inactive, lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
), ),
); );
} }
void onAnimate() {
emit(
state.copyWith(animationState: ArrowAnimationState.blinking),
);
}
void onStop() {
emit(
state.copyWith(
lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
),
);
}
void onBlink() {
switch (state.lightState) {
case ArrowLightState.inactive:
emit(
state.copyWith(lightState: ArrowLightState.active1),
);
break;
case ArrowLightState.active1:
emit(
state.copyWith(lightState: ArrowLightState.active2),
);
break;
case ArrowLightState.active2:
emit(
state.copyWith(lightState: ArrowLightState.active3),
);
break;
case ArrowLightState.active3:
emit(
state.copyWith(lightState: ArrowLightState.active4),
);
break;
case ArrowLightState.active4:
emit(
state.copyWith(lightState: ArrowLightState.active5),
);
break;
case ArrowLightState.active5:
emit(
state.copyWith(lightState: ArrowLightState.inactive),
);
break;
}
}
} }

@ -6,38 +6,31 @@ class SpaceshipRampState extends Equatable {
const SpaceshipRampState({ const SpaceshipRampState({
required this.hits, required this.hits,
required this.lightState, required this.lightState,
required this.animationState,
}) : assert(hits >= 0, "Hits can't be negative"); }) : assert(hits >= 0, "Hits can't be negative");
const SpaceshipRampState.initial() const SpaceshipRampState.initial()
: this( : this(
hits: 0, hits: 0,
lightState: ArrowLightState.inactive, lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
); );
final int hits; final int hits;
final ArrowLightState lightState; final ArrowLightState lightState;
final ArrowAnimationState animationState;
bool get fullArrowLit => bool get fullArrowLit => lightState == ArrowLightState.active5;
lightState == ArrowLightState.active5 &&
animationState == ArrowAnimationState.idle;
SpaceshipRampState copyWith({ SpaceshipRampState copyWith({
int? hits, int? hits,
ArrowLightState? lightState, ArrowLightState? lightState,
ArrowAnimationState? animationState,
}) { }) {
return SpaceshipRampState( return SpaceshipRampState(
hits: hits ?? this.hits, hits: hits ?? this.hits,
lightState: lightState ?? this.lightState, lightState: lightState ?? this.lightState,
animationState: animationState ?? this.animationState,
); );
} }
@override @override
List<Object?> get props => [hits, lightState, animationState]; List<Object?> get props => [hits, lightState];
} }
/// Indicates the state of the arrow on the [SpaceshipRamp]. /// Indicates the state of the arrow on the [SpaceshipRamp].
@ -60,9 +53,3 @@ enum ArrowLightState {
/// Arrow with all 5 lights lit up. /// Arrow with all 5 lights lit up.
active5, active5,
} }
// Indicates if the blinking animation is running.
enum ArrowAnimationState {
idle,
blinking,
}

@ -39,7 +39,6 @@ class SpaceshipRamp extends Component {
SpaceshipRampBase()..initialPosition = Vector2(3.4, -42.5), SpaceshipRampBase()..initialPosition = Vector2(3.4, -42.5),
_SpaceshipRampBackgroundRailingSpriteComponent(), _SpaceshipRampBackgroundRailingSpriteComponent(),
SpaceshipRampArrowSpriteComponent(), SpaceshipRampArrowSpriteComponent(),
RampArrowBlinkingBehavior(),
...?children, ...?children,
], ],
); );

@ -1,219 +0,0 @@
// ignore_for_file: prefer_const_constructors, cascade_invocations
import 'dart:async';
import 'package:bloc_test/bloc_test.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/spaceship_ramp/behavior/ramp_arrow_blinking_behavior.dart';
import 'package:pinball_flame/pinball_flame.dart';
class _TestGame extends Forge2DGame {
@override
Future<void> onLoad() async {
images.prefix = '';
await images.loadAll([
Assets.images.multiball.lit.keyName,
Assets.images.android.ramp.railingForeground.keyName,
Assets.images.android.ramp.railingBackground.keyName,
Assets.images.android.ramp.main.keyName,
Assets.images.android.ramp.arrow.inactive.keyName,
Assets.images.android.ramp.arrow.active1.keyName,
Assets.images.android.ramp.arrow.active2.keyName,
Assets.images.android.ramp.arrow.active3.keyName,
Assets.images.android.ramp.arrow.active4.keyName,
Assets.images.android.ramp.arrow.active5.keyName,
]);
}
Future<void> pump(
SpaceshipRamp child, {
required SpaceshipRampCubit spaceshipRampCubit,
}) async {
await ensureAdd(
FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>.value(
value: spaceshipRampCubit,
children: [
ZCanvasComponent(children: [child]),
],
),
);
}
}
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(_TestGame.new);
group(
'RampArrowBlinkingBehavior',
() {
flameTester.testGameWidget(
'calls onBlink every 0.05 seconds when animation state is animated',
setUp: (game, tester) async {
final behavior = RampArrowBlinkingBehavior();
final bloc = _MockSpaceshipRampCubit();
final streamController = StreamController<SpaceshipRampState>();
whenListen(
bloc,
streamController.stream,
initialState: SpaceshipRampState.initial(),
);
final spaceshipRamp = SpaceshipRamp.test();
await game.pump(
spaceshipRamp,
spaceshipRampCubit: bloc,
);
await spaceshipRamp.add(behavior);
streamController.add(
SpaceshipRampState(
hits: 1,
animationState: ArrowAnimationState.blinking,
lightState: ArrowLightState.active1,
),
);
await tester.pump();
game.update(0);
verify(bloc.onBlink).called(1);
await tester.pump();
game.update(0.05);
await streamController.close();
verify(bloc.onBlink).called(1);
},
);
flameTester.testGameWidget(
'calls onStop when animation state is stopped',
setUp: (game, tester) async {
final behavior = RampArrowBlinkingBehavior();
final bloc = _MockSpaceshipRampCubit();
final streamController = StreamController<SpaceshipRampState>();
whenListen(
bloc,
streamController.stream,
initialState: SpaceshipRampState.initial(),
);
when(bloc.onBlink).thenAnswer((_) async {});
final spaceshipRamp = SpaceshipRamp.test();
await game.pump(
spaceshipRamp,
spaceshipRampCubit: bloc,
);
await spaceshipRamp.add(behavior);
streamController.add(
SpaceshipRampState(
hits: 1,
animationState: ArrowAnimationState.blinking,
lightState: ArrowLightState.active1,
),
);
await tester.pump();
streamController.add(
SpaceshipRampState(
hits: 1,
animationState: ArrowAnimationState.idle,
lightState: ArrowLightState.active1,
),
);
await streamController.close();
await game.ready();
verify(bloc.onStop).called(1);
},
);
flameTester.testGameWidget(
'onTick stops when there is no animation',
setUp: (game, tester) async {
final behavior = RampArrowBlinkingBehavior();
final bloc = _MockSpaceshipRampCubit();
final streamController = StreamController<SpaceshipRampState>();
whenListen(
bloc,
streamController.stream,
initialState: SpaceshipRampState.initial(),
);
when(bloc.onBlink).thenAnswer((_) async {});
final spaceshipRamp = SpaceshipRamp.test();
await game.pump(
spaceshipRamp,
spaceshipRampCubit: bloc,
);
await spaceshipRamp.add(behavior);
streamController.add(
SpaceshipRampState(
hits: 1,
animationState: ArrowAnimationState.idle,
lightState: ArrowLightState.active1,
),
);
await tester.pump();
behavior.onTick();
await game.ready();
expect(behavior.timer.isRunning(), false);
},
);
flameTester.testGameWidget(
'onTick stops after 10 blinks repetitions',
setUp: (game, tester) async {
final behavior = RampArrowBlinkingBehavior();
final bloc = _MockSpaceshipRampCubit();
final streamController = StreamController<SpaceshipRampState>();
whenListen(
bloc,
streamController.stream,
initialState: SpaceshipRampState.initial(),
);
when(bloc.onBlink).thenAnswer((_) async {});
final spaceshipRamp = SpaceshipRamp.test();
await game.pump(
spaceshipRamp,
spaceshipRampCubit: bloc,
);
await spaceshipRamp.add(behavior);
streamController.add(
SpaceshipRampState(
hits: 1,
animationState: ArrowAnimationState.blinking,
lightState: ArrowLightState.inactive,
),
);
await tester.pump();
for (var i = 0; i < 10; i++) {
behavior.onTick();
}
await game.ready();
expect(behavior.timer.isRunning(), false);
},
);
},
);
}

@ -29,7 +29,6 @@ void main() {
seed: () => SpaceshipRampState( seed: () => SpaceshipRampState(
hits: 100, hits: 100,
lightState: ArrowLightState.active3, lightState: ArrowLightState.active3,
animationState: ArrowAnimationState.blinking,
), ),
act: (bloc) => bloc.onReset(), act: (bloc) => bloc.onReset(),
expect: () => [ expect: () => [
@ -39,111 +38,9 @@ void main() {
(state) => state.lightState, (state) => state.lightState,
'lightState', 'lightState',
ArrowLightState.inactive, ArrowLightState.inactive,
)
..having(
(state) => state.animationState,
'animationState',
ArrowAnimationState.idle,
), ),
], ],
); );
}); });
group('onAnimate', () {
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
'emits animationState blinking',
build: SpaceshipRampCubit.new,
seed: () => SpaceshipRampState(
hits: 100,
lightState: ArrowLightState.active3,
animationState: ArrowAnimationState.idle,
),
act: (bloc) => bloc.onAnimate(),
expect: () => [
isA<SpaceshipRampState>().having(
(state) => state.animationState,
'animationState',
ArrowAnimationState.blinking,
),
],
);
});
group('onStop', () {
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
'emits animationState idle and lightState inactive',
build: SpaceshipRampCubit.new,
seed: () => SpaceshipRampState(
hits: 100,
lightState: ArrowLightState.active3,
animationState: ArrowAnimationState.blinking,
),
act: (bloc) => bloc.onStop(),
expect: () => [
isA<SpaceshipRampState>()
..having(
(state) => state.lightState,
'lightState',
ArrowLightState.inactive,
)
..having(
(state) => state.animationState,
'animationState',
ArrowAnimationState.idle,
),
],
);
});
group('onBlink', () {
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
'emits next lit state at lightState',
build: SpaceshipRampCubit.new,
seed: () => SpaceshipRampState(
hits: 100,
lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.blinking,
),
act: (bloc) => bloc
..onBlink()
..onBlink()
..onBlink()
..onBlink()
..onBlink()
..onBlink(),
expect: () => [
isA<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.active1,
),
isA<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.active2,
),
isA<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.active3,
),
isA<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.active4,
),
isA<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.active5,
),
isA<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.inactive,
),
],
);
});
}); });
} }

@ -10,13 +10,11 @@ void main() {
SpaceshipRampState( SpaceshipRampState(
hits: 0, hits: 0,
lightState: ArrowLightState.inactive, lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
), ),
equals( equals(
SpaceshipRampState( SpaceshipRampState(
hits: 0, hits: 0,
lightState: ArrowLightState.inactive, lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
), ),
), ),
); );
@ -28,7 +26,6 @@ void main() {
SpaceshipRampState( SpaceshipRampState(
hits: 0, hits: 0,
lightState: ArrowLightState.inactive, lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
), ),
isNotNull, isNotNull,
); );
@ -43,7 +40,6 @@ void main() {
() => SpaceshipRampState( () => SpaceshipRampState(
hits: -1, hits: -1,
lightState: ArrowLightState.inactive, lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
), ),
throwsAssertionError, throwsAssertionError,
); );
@ -58,7 +54,6 @@ void main() {
const rampState = SpaceshipRampState( const rampState = SpaceshipRampState(
hits: 0, hits: 0,
lightState: ArrowLightState.inactive, lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
); );
expect( expect(
() => rampState.copyWith(hits: rampState.hits - 1), () => rampState.copyWith(hits: rampState.hits - 1),
@ -74,7 +69,6 @@ void main() {
const rampState = SpaceshipRampState( const rampState = SpaceshipRampState(
hits: 0, hits: 0,
lightState: ArrowLightState.inactive, lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
); );
expect( expect(
rampState.copyWith(), rampState.copyWith(),
@ -90,12 +84,10 @@ void main() {
const rampState = SpaceshipRampState( const rampState = SpaceshipRampState(
hits: 0, hits: 0,
lightState: ArrowLightState.inactive, lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
); );
final otherRampState = SpaceshipRampState( final otherRampState = SpaceshipRampState(
hits: rampState.hits + 1, hits: rampState.hits + 1,
lightState: ArrowLightState.active1, lightState: ArrowLightState.active1,
animationState: ArrowAnimationState.blinking,
); );
expect(rampState, isNot(equals(otherRampState))); expect(rampState, isNot(equals(otherRampState)));
@ -103,7 +95,6 @@ void main() {
rampState.copyWith( rampState.copyWith(
hits: otherRampState.hits, hits: otherRampState.hits,
lightState: otherRampState.lightState, lightState: otherRampState.lightState,
animationState: otherRampState.animationState,
), ),
equals(otherRampState), equals(otherRampState),
); );

@ -138,28 +138,6 @@ void main() {
); );
}, },
); );
flameTester.test(
'a RampArrowBlinkingBehavior',
(game) async {
final bloc = _MockSpaceshipRampCubit();
final streamController = StreamController<SpaceshipRampState>();
whenListen(
bloc,
streamController.stream,
initialState: SpaceshipRampState.initial(),
);
final ramp = SpaceshipRamp();
await game.pump(
ramp,
spaceshipRampCubit: bloc,
);
expect(
game.descendants().whereType<RampArrowBlinkingBehavior>().length,
equals(1),
);
},
);
}); });
group('renders correctly', () { group('renders correctly', () {

@ -188,7 +188,7 @@ void main() {
); );
flameTester.test( flameTester.test(
'adds onAnimate ' 'adds again onProgressed to dimmed all '
'when arrow is full lit after hit and multiplier is less than 6', 'when arrow is full lit after hit and multiplier is less than 6',
(game) async { (game) async {
final bloc = _MockSpaceshipRampCubit(); final bloc = _MockSpaceshipRampCubit();
@ -224,12 +224,12 @@ void main() {
await game.ready(); await game.ready();
verify(bloc.onAnimate).called(1); verify(bloc.onProgressed).called(2);
}, },
); );
flameTester.test( flameTester.test(
"doesn't add onAnimate " "doesn't add again onProgressed to dimmed all "
'when arrow is not full lit after hit', 'when arrow is not full lit after hit',
(game) async { (game) async {
final bloc = _MockSpaceshipRampCubit(); final bloc = _MockSpaceshipRampCubit();
@ -265,12 +265,12 @@ void main() {
await game.ready(); await game.ready();
verifyNever(bloc.onAnimate); verify(bloc.onProgressed).called(1);
}, },
); );
flameTester.test( flameTester.test(
"doesn't add onAnimate " "doesn't add again onProgressed to dimmed all "
'when multiplier is 6 after hit', 'when multiplier is 6 after hit',
(game) async { (game) async {
final bloc = _MockSpaceshipRampCubit(); final bloc = _MockSpaceshipRampCubit();
@ -303,7 +303,7 @@ void main() {
await game.ready(); await game.ready();
verifyNever(bloc.onAnimate); verify(bloc.onProgressed).called(1);
}, },
); );
}); });

Loading…
Cancel
Save