From 683f2746f31ebaa7f75106802ba478da7f00851f Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Thu, 28 Apr 2022 17:23:28 +0100 Subject: [PATCH] feat: defined `BumpingBehavior` (#248) * feat: defined BumpingBehavior * refactor: removed unused import * docs: fixed typo Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * feat: made strength private Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> --- .../lib/src/components/bumping_behavior.dart | 25 ++++++ .../src/components/bumping_behavior_test.dart | 84 +++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 packages/pinball_components/lib/src/components/bumping_behavior.dart create mode 100644 packages/pinball_components/test/src/components/bumping_behavior_test.dart diff --git a/packages/pinball_components/lib/src/components/bumping_behavior.dart b/packages/pinball_components/lib/src/components/bumping_behavior.dart new file mode 100644 index 00000000..654f96b4 --- /dev/null +++ b/packages/pinball_components/lib/src/components/bumping_behavior.dart @@ -0,0 +1,25 @@ +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +/// {@template bumping_behavior} +/// Makes any [BodyComponent] that contacts with [parent] bounce off. +/// {@endtemplate} +class BumpingBehavior extends ContactBehavior { + /// {@macro bumping_behavior} + BumpingBehavior({required double strength}) : _strength = strength; + + /// Determines how strong the bump is. + final double _strength; + + @override + void postSolve(Object other, Contact contact, ContactImpulse impulse) { + super.postSolve(other, contact, impulse); + if (other is! BodyComponent) return; + + other.body.applyLinearImpulse( + contact.manifold.localPoint + ..normalize() + ..multiply(Vector2.all(other.body.mass * _strength)), + ); + } +} diff --git a/packages/pinball_components/test/src/components/bumping_behavior_test.dart b/packages/pinball_components/test/src/components/bumping_behavior_test.dart new file mode 100644 index 00000000..d346a0ae --- /dev/null +++ b/packages/pinball_components/test/src/components/bumping_behavior_test.dart @@ -0,0 +1,84 @@ +// 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:mocktail/mocktail.dart'; +import 'package:pinball_components/src/components/bumping_behavior.dart'; + +import '../../helpers/helpers.dart'; +import 'layer_test.dart'; + +class MockContactImpulse extends Mock implements ContactImpulse {} + +class MockManifold extends Mock implements Manifold {} + +class TestHeavyBodyComponent extends BodyComponent { + @override + Body createBody() { + final shape = CircleShape(); + return world.createBody( + BodyDef( + type: BodyType.dynamic, + ), + )..createFixtureFromShape(shape, 20); + } +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(TestGame.new); + + group('BumpingBehavior', () { + flameTester.test('can be added', (game) async { + final behavior = BumpingBehavior(strength: 0); + final component = TestBodyComponent(); + await component.add(behavior); + await game.ensureAdd(component); + }); + + flameTester.testGameWidget( + 'the bump is greater when the strengh is greater', + setUp: (game, tester) async { + final component1 = TestBodyComponent(); + final behavior1 = BumpingBehavior(strength: 1); + await component1.add(behavior1); + + final component2 = TestBodyComponent(); + final behavior2 = BumpingBehavior(strength: 2); + await component2.add(behavior2); + + final dummy1 = TestHeavyBodyComponent(); + final dummy2 = TestHeavyBodyComponent(); + + await game.ensureAddAll([ + component1, + component2, + dummy1, + dummy2, + ]); + + expect(dummy1.body.inverseMass, greaterThan(0)); + expect(dummy2.body.inverseMass, greaterThan(0)); + + final contact = MockContact(); + final manifold = MockManifold(); + final contactImpulse = MockContactImpulse(); + when(() => manifold.localPoint).thenReturn(Vector2.all(1)); + when(() => contact.manifold).thenReturn(manifold); + + behavior1.postSolve(dummy1, contact, contactImpulse); + behavior2.postSolve(dummy2, contact, contactImpulse); + + expect( + dummy2.body.linearVelocity.x, + greaterThan(dummy1.body.linearVelocity.x), + ); + expect( + dummy2.body.linearVelocity.y, + greaterThan(dummy1.body.linearVelocity.y), + ); + }, + ); + }); +}