mirror of https://github.com/flutter/pinball.git
parent
3b41771bdf
commit
552bf040c4
@ -1,3 +1,5 @@
|
|||||||
export 'plunger_jointing_behavior.dart';
|
export 'plunger_jointing_behavior.dart';
|
||||||
export 'plunger_key_controlling_behavior.dart';
|
export 'plunger_key_controlling_behavior.dart';
|
||||||
export 'plunger_noise_behavior.dart';
|
export 'plunger_noise_behavior.dart';
|
||||||
|
export 'plunger_pulling_behavior.dart';
|
||||||
|
export 'plunger_releasing_behavior.dart';
|
||||||
|
@ -1,20 +1,27 @@
|
|||||||
import 'package:flame/components.dart';
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame_bloc/flame_bloc.dart';
|
||||||
import 'package:pinball_audio/pinball_audio.dart';
|
import 'package:pinball_audio/pinball_audio.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
import 'package:pinball_flame/pinball_flame.dart';
|
import 'package:pinball_flame/pinball_flame.dart';
|
||||||
|
|
||||||
/// Plays the [PinballAudio.launcher] sound.
|
/// Plays the [PinballAudio.launcher] sound.
|
||||||
///
|
///
|
||||||
/// It is attached when the plunger is released.
|
/// It is attached when the plunger is released.
|
||||||
class PlungerNoiseBehavior extends Component {
|
class PlungerNoiseBehavior extends Component
|
||||||
|
with FlameBlocListenable<PlungerCubit, PlungerState> {
|
||||||
|
late final PinballAudioPlayer _audioPlayer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onLoad() async {
|
void onNewState(PlungerState state) {
|
||||||
await super.onLoad();
|
super.onNewState(state);
|
||||||
readProvider<PinballAudioPlayer>().play(PinballAudio.launcher);
|
if (state.isReleasing) {
|
||||||
|
_audioPlayer.play(PinballAudio.launcher);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void update(double dt) {
|
Future<void> onLoad() async {
|
||||||
super.update(dt);
|
await super.onLoad();
|
||||||
removeFromParent();
|
_audioPlayer = readProvider<PinballAudioPlayer>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame_bloc/flame_bloc.dart';
|
||||||
|
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:pinball_flame/pinball_flame.dart';
|
||||||
|
|
||||||
|
class PlungerPullingBehavior extends Component
|
||||||
|
with ParentIsA<Plunger>, FlameBlocReader<PlungerCubit, PlungerState> {
|
||||||
|
PlungerPullingBehavior({
|
||||||
|
required double strength,
|
||||||
|
}) : _strength = strength;
|
||||||
|
|
||||||
|
final double _strength;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(double dt) {
|
||||||
|
if (bloc.state.isPulling) {
|
||||||
|
parent.body.linearVelocity = Vector2(0, _strength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlungerAutoPullingBehavior extends PlungerPullingBehavior {
|
||||||
|
PlungerAutoPullingBehavior({
|
||||||
|
required double strength,
|
||||||
|
}) : super(strength: strength);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(double dt) {
|
||||||
|
super.update(dt);
|
||||||
|
|
||||||
|
final joint = parent.body.joints.whereType<PrismaticJoint>().single;
|
||||||
|
final reachedBottom = joint.getJointTranslation() <= joint.getLowerLimit();
|
||||||
|
if (reachedBottom) {
|
||||||
|
bloc.released();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
class PlungerReleasingBehavior extends Component
|
||||||
|
with ParentIsA<Plunger>, FlameBlocListenable<PlungerCubit, PlungerState> {
|
||||||
|
PlungerReleasingBehavior({
|
||||||
|
required double strength,
|
||||||
|
}) : _strength = strength;
|
||||||
|
|
||||||
|
final double _strength; // 11
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onNewState(PlungerState state) {
|
||||||
|
super.onNewState(state);
|
||||||
|
if (state.isReleasing) {
|
||||||
|
final velocity =
|
||||||
|
(parent.initialPosition.y - parent.body.position.y) * _strength;
|
||||||
|
parent.body.linearVelocity = Vector2(0, velocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
|
||||||
|
part 'plunger_state.dart';
|
||||||
|
|
||||||
|
class PlungerCubit extends Cubit<PlungerState> {
|
||||||
|
PlungerCubit() : super(PlungerState.releasing);
|
||||||
|
|
||||||
|
void pulled() {
|
||||||
|
emit(PlungerState.pulling);
|
||||||
|
}
|
||||||
|
|
||||||
|
void released() {
|
||||||
|
emit(PlungerState.releasing);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
part of 'plunger_cubit.dart';
|
||||||
|
|
||||||
|
enum PlungerState {
|
||||||
|
pulling,
|
||||||
|
|
||||||
|
releasing,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PlungerStateX on PlungerState {
|
||||||
|
bool get isPulling => this == PlungerState.pulling;
|
||||||
|
bool get isReleasing => this == PlungerState.releasing;
|
||||||
|
}
|
@ -0,0 +1,181 @@
|
|||||||
|
// 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';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final flameTester = FlameTester(Forge2DGame.new);
|
||||||
|
|
||||||
|
group('PlungerJointingBehavior', () {
|
||||||
|
test('can be instantiated', () {
|
||||||
|
expect(
|
||||||
|
PlungerJointingBehavior(compressionDistance: 0),
|
||||||
|
isA<PlungerJointingBehavior>(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test('can be loaded', (game) async {
|
||||||
|
final parent = Plunger.test();
|
||||||
|
final behavior = PlungerJointingBehavior(compressionDistance: 0);
|
||||||
|
await game.ensureAdd(parent);
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
expect(parent.children, contains(behavior));
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test('can be loaded', (game) async {
|
||||||
|
final parent = Plunger.test();
|
||||||
|
final behavior = PlungerJointingBehavior(compressionDistance: 0);
|
||||||
|
await game.ensureAdd(parent);
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
expect(parent.children, contains(behavior));
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test('creates a joint', (game) async {
|
||||||
|
final behavior = PlungerJointingBehavior(compressionDistance: 0);
|
||||||
|
final parent = Plunger.test();
|
||||||
|
await game.ensureAdd(parent);
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
expect(parent.body.joints, isNotEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// group('PlungerAnchorPrismaticJointDef', () {
|
||||||
|
// const compressionDistance = 10.0;
|
||||||
|
// late Plunger plunger;
|
||||||
|
|
||||||
|
// setUp(() {
|
||||||
|
// plunger = Plunger(
|
||||||
|
// compressionDistance: compressionDistance,
|
||||||
|
// );
|
||||||
|
// anchor = PlungerAnchor(plunger: plunger);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// group('initializes with', () {
|
||||||
|
// flameTester.test(
|
||||||
|
// 'plunger body as bodyA',
|
||||||
|
// (game) async {
|
||||||
|
// await game.ensureAdd(plunger);
|
||||||
|
// await game.ensureAdd(anchor);
|
||||||
|
|
||||||
|
// final jointDef = PlungerAnchorPrismaticJointDef(
|
||||||
|
// plunger: plunger,
|
||||||
|
// anchor: anchor,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// expect(jointDef.bodyA, equals(plunger.body));
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
|
||||||
|
// flameTester.test(
|
||||||
|
// 'anchor body as bodyB',
|
||||||
|
// (game) async {
|
||||||
|
// await game.ensureAdd(plunger);
|
||||||
|
// await game.ensureAdd(anchor);
|
||||||
|
|
||||||
|
// final jointDef = PlungerAnchorPrismaticJointDef(
|
||||||
|
// plunger: plunger,
|
||||||
|
// anchor: anchor,
|
||||||
|
// );
|
||||||
|
// game.world.createJoint(PrismaticJoint(jointDef));
|
||||||
|
|
||||||
|
// expect(jointDef.bodyB, equals(anchor.body));
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
|
||||||
|
// flameTester.test(
|
||||||
|
// 'limits enabled',
|
||||||
|
// (game) async {
|
||||||
|
// await game.ensureAdd(plunger);
|
||||||
|
// await game.ensureAdd(anchor);
|
||||||
|
|
||||||
|
// final jointDef = PlungerAnchorPrismaticJointDef(
|
||||||
|
// plunger: plunger,
|
||||||
|
// anchor: anchor,
|
||||||
|
// );
|
||||||
|
// game.world.createJoint(PrismaticJoint(jointDef));
|
||||||
|
|
||||||
|
// expect(jointDef.enableLimit, isTrue);
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
|
||||||
|
// flameTester.test(
|
||||||
|
// 'lower translation limit as negative infinity',
|
||||||
|
// (game) async {
|
||||||
|
// await game.ensureAdd(plunger);
|
||||||
|
// await game.ensureAdd(anchor);
|
||||||
|
|
||||||
|
// final jointDef = PlungerAnchorPrismaticJointDef(
|
||||||
|
// plunger: plunger,
|
||||||
|
// anchor: anchor,
|
||||||
|
// );
|
||||||
|
// game.world.createJoint(PrismaticJoint(jointDef));
|
||||||
|
|
||||||
|
// expect(jointDef.lowerTranslation, equals(double.negativeInfinity));
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
|
||||||
|
// flameTester.test(
|
||||||
|
// 'connected body collision enabled',
|
||||||
|
// (game) async {
|
||||||
|
// await game.ensureAdd(plunger);
|
||||||
|
// await game.ensureAdd(anchor);
|
||||||
|
|
||||||
|
// final jointDef = PlungerAnchorPrismaticJointDef(
|
||||||
|
// plunger: plunger,
|
||||||
|
// anchor: anchor,
|
||||||
|
// );
|
||||||
|
// game.world.createJoint(PrismaticJoint(jointDef));
|
||||||
|
|
||||||
|
// expect(jointDef.collideConnected, isTrue);
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
|
||||||
|
// flameTester.testGameWidget(
|
||||||
|
// 'plunger cannot go below anchor',
|
||||||
|
// setUp: (game, tester) async {
|
||||||
|
// await game.ensureAdd(plunger);
|
||||||
|
// await game.ensureAdd(anchor);
|
||||||
|
|
||||||
|
// // Giving anchor a shape for the plunger to collide with.
|
||||||
|
// anchor.body.createFixtureFromShape(PolygonShape()..setAsBoxXY(2, 1));
|
||||||
|
|
||||||
|
// final jointDef = PlungerAnchorPrismaticJointDef(
|
||||||
|
// plunger: plunger,
|
||||||
|
// anchor: anchor,
|
||||||
|
// );
|
||||||
|
// game.world.createJoint(PrismaticJoint(jointDef));
|
||||||
|
|
||||||
|
// await tester.pump(const Duration(seconds: 1));
|
||||||
|
// },
|
||||||
|
// verify: (game, tester) async {
|
||||||
|
// expect(plunger.body.position.y < anchor.body.position.y, isTrue);
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
|
||||||
|
// flameTester.testGameWidget(
|
||||||
|
// 'plunger cannot excessively exceed starting position',
|
||||||
|
// setUp: (game, tester) async {
|
||||||
|
// await game.ensureAdd(plunger);
|
||||||
|
// await game.ensureAdd(anchor);
|
||||||
|
|
||||||
|
// final jointDef = PlungerAnchorPrismaticJointDef(
|
||||||
|
// plunger: plunger,
|
||||||
|
// anchor: anchor,
|
||||||
|
// );
|
||||||
|
// game.world.createJoint(PrismaticJoint(jointDef));
|
||||||
|
|
||||||
|
// plunger.body.setTransform(Vector2(0, -1), 0);
|
||||||
|
|
||||||
|
// await tester.pump(const Duration(seconds: 1));
|
||||||
|
// },
|
||||||
|
// verify: (game, tester) async {
|
||||||
|
// expect(plunger.body.position.y < 1, isTrue);
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
@ -1,5 +1,71 @@
|
|||||||
|
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
class _MockRawKeyDownEvent extends Mock implements RawKeyDownEvent {
|
||||||
|
@override
|
||||||
|
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||||
|
return super.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MockRawKeyUpEvent extends Mock implements RawKeyUpEvent {
|
||||||
|
@override
|
||||||
|
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||||
|
return super.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('PlungerKeyControllingBehavior', () {});
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final flameTester = FlameTester(Forge2DGame.new);
|
||||||
|
|
||||||
|
group('PlungerKeyControllingBehavior', () {
|
||||||
|
test('can be instantiated', () {
|
||||||
|
expect(
|
||||||
|
PlungerKeyControllingBehavior(),
|
||||||
|
isA<PlungerKeyControllingBehavior>(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test('can be loaded', (game) async {
|
||||||
|
final parent = Plunger.test();
|
||||||
|
final behavior = PlungerKeyControllingBehavior();
|
||||||
|
await game.ensureAdd(parent);
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
expect(parent.children, contains(behavior));
|
||||||
|
});
|
||||||
|
|
||||||
|
group('onKeyEvent', () {
|
||||||
|
late Plunger plunger;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
plunger = Plunger.test();
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test(
|
||||||
|
'pulls when down arrow is pressed',
|
||||||
|
(game) async {
|
||||||
|
final plunger = Plunger.test();
|
||||||
|
await game.ensureAdd(plunger);
|
||||||
|
final behavior = PlungerKeyControllingBehavior();
|
||||||
|
await plunger.ensureAdd(behavior);
|
||||||
|
|
||||||
|
final event = _MockRawKeyDownEvent();
|
||||||
|
when(() => event.logicalKey).thenReturn(
|
||||||
|
LogicalKeyboardKey.arrowDown,
|
||||||
|
);
|
||||||
|
|
||||||
|
behavior.onKeyEvent(event, {});
|
||||||
|
|
||||||
|
// expect(plunger.body.linearVelocity.y, isPositive);
|
||||||
|
// expect(plunger.body.linearVelocity.x, isZero);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
// ignore_for_file: cascade_invocations
|
||||||
|
|
||||||
|
import 'package:flame/components.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_audio/pinball_audio.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:pinball_flame/pinball_flame.dart';
|
||||||
|
|
||||||
|
class _TestGame extends Forge2DGame {
|
||||||
|
Future<void> pump(
|
||||||
|
Component child, {
|
||||||
|
PinballAudioPlayer? pinballAudioPlayer,
|
||||||
|
}) {
|
||||||
|
return ensureAdd(
|
||||||
|
FlameProvider<PinballAudioPlayer>.value(
|
||||||
|
pinballAudioPlayer ?? _MockPinballAudioPlayer(),
|
||||||
|
children: [
|
||||||
|
FlameBlocProvider<PlungerCubit, PlungerState>.value(
|
||||||
|
value: PlungerCubit(),
|
||||||
|
children: [child],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MockPinballAudioPlayer extends Mock implements PinballAudioPlayer {}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final flameTester = FlameTester(_TestGame.new);
|
||||||
|
|
||||||
|
group('PlungerNoiseBehavior', () {
|
||||||
|
late PinballAudioPlayer audioPlayer;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
audioPlayer = _MockPinballAudioPlayer();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can be instantiated', () {
|
||||||
|
expect(
|
||||||
|
PlungerNoiseBehavior(),
|
||||||
|
isA<PlungerNoiseBehavior>(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test('can be loaded', (game) async {
|
||||||
|
final parent = Component();
|
||||||
|
final behavior = PlungerNoiseBehavior();
|
||||||
|
await game.pump(parent);
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
expect(parent.children, contains(behavior));
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test('plays the correct sound on when released', (game) async {
|
||||||
|
final parent = Component();
|
||||||
|
final behavior = PlungerNoiseBehavior();
|
||||||
|
await game.pump(
|
||||||
|
parent,
|
||||||
|
pinballAudioPlayer: audioPlayer,
|
||||||
|
);
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
|
||||||
|
behavior.onNewState(PlungerState.releasing);
|
||||||
|
|
||||||
|
verify(() => audioPlayer.play(PinballAudio.launcher)).called(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('PlungerPullingBehavior', () {
|
||||||
|
test('can be instantiated', () {
|
||||||
|
expect(
|
||||||
|
PlungerPullingBehavior(strength: 0),
|
||||||
|
isA<PlungerPullingBehavior>(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('PlungerAutoPullingBehavior', () {
|
||||||
|
test('can be instantiated', () {
|
||||||
|
expect(
|
||||||
|
PlungerAutoPullingBehavior(strength: 0),
|
||||||
|
isA<PlungerAutoPullingBehavior>(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('PlungerReleasingBehavior', () {});
|
||||||
|
}
|
Loading…
Reference in new issue