feat: defined ContactCallbacks2

pull/202/head
alestiago 3 years ago
parent 2d076cceca
commit 6d92eb3f1d

@ -2,4 +2,5 @@ library pinball_flame;
export 'src/blueprint.dart';
export 'src/component_controller.dart';
export 'src/contact_callbacks.dart';
export 'src/keyboard_input_controller.dart';

@ -0,0 +1,86 @@
// TODO(alestiago): Remove once the below is merged and updated.
// https://github.com/flame-engine/flame/pull/1547
// ignore_for_file: public_member_api_docs
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame_forge2d/flame_forge2d.dart';
abstract class ContactCallbacks2 {
void beginContact(Object other, Contact contact) {}
void endContact(Object other, Contact contact) {}
void preSolve(Object other, Contact contact, Manifold oldManifold) {}
void postSolve(Object other, Contact contact, ContactImpulse impulse) {}
}
/// Listens to the entire [World] contacts events.
///
/// It propagates contact events ([beginContact], [endContact], [preSolve],
/// [postSolve]) to other [ContactCallbacks]s when a [Body] or at least one of
/// its fixtures `userData` is set to a [ContactCallbacks].
///
/// If the [Body] `userData` is set to a [ContactCallbacks] the contact events
/// of this will be called to when any [Body]'s fixture contacts another
/// [Fixture].
///
/// If instead you wish to be more specific and only trigger contact events
/// when a specific [Body]'s fixture contacts with another [Fixture] you can
/// set the fixture `userData` to a [ContactCallbacks].
///
/// The described behaviour is a simple out of the box solution to propagate
/// contact events. If you wish to implement your own logic you can subclass
/// [ContactListener] and provide it to your [Forge2DGame].
class WorldContactListener extends ContactListener {
void _callback(
Contact contact,
void Function(ContactCallbacks2 contactCallback, Object other) callback,
) {
final userDatas = {
contact.bodyA.userData,
contact.fixtureA.userData,
contact.bodyB.userData,
contact.fixtureB.userData,
}.whereType<Object>();
for (final contactCallback in userDatas.whereType<ContactCallbacks2>()) {
for (final object in userDatas) {
if (object != contactCallback) {
callback(contactCallback, object);
}
}
}
}
@override
void beginContact(Contact contact) {
_callback(
contact,
(contactCallback, other) => contactCallback.beginContact(other, contact),
);
}
@override
void endContact(Contact contact) {
_callback(
contact,
(contactCallback, other) => contactCallback.endContact(other, contact),
);
}
@override
void preSolve(Contact contact, Manifold oldManifold) {
_callback(
contact,
(contactCallback, other) =>
contactCallback.preSolve(other, contact, oldManifold),
);
}
@override
void postSolve(Contact contact, ContactImpulse contactImpulse) {
_callback(
contact,
(contactCallback, other) =>
contactCallback.postSolve(other, contact, contactImpulse),
);
}
}

@ -0,0 +1,364 @@
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:test/scaffolding.dart';
class MockContactCallback extends Mock implements ContactCallbacks2 {}
class MockContact extends Mock implements Contact {}
class MockBody extends Mock implements Body {}
class MockFixture extends Mock implements Fixture {}
class MockManifold extends Mock implements Manifold {}
class MockContactImpulse extends Mock implements ContactImpulse {}
void main() {
group(
'WorldContactListener',
() {
late ContactCallbacks2 contactCallback;
late Contact contact;
late Body bodyA;
late Body bodyB;
late Fixture fixtureA;
late Fixture fixtureB;
setUp(() {
contactCallback = MockContactCallback();
contact = MockContact();
bodyA = MockBody();
bodyB = MockBody();
fixtureA = MockFixture();
fixtureB = MockFixture();
when(() => contact.bodyA).thenReturn(bodyA);
when(() => contact.bodyB).thenReturn(bodyB);
when(() => contact.fixtureA).thenReturn(fixtureA);
when(() => contact.fixtureB).thenReturn(fixtureB);
});
setUpAll(() {
registerFallbackValue(Object());
});
group(
'preSolve',
() {
late Manifold manifold;
setUp(() {
manifold = MockManifold();
});
test(
"doesn't callback if userData are null",
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(null);
when(() => fixtureA.userData).thenReturn(null);
when(() => fixtureB.userData).thenReturn(null);
contactListener.preSolve(contact, manifold);
verifyNever<void>(
() => contactCallback.preSolve(any(), contact, manifold),
);
},
);
test(
'callbacks for userData when not null',
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(Object());
when(() => fixtureA.userData).thenReturn(Object());
when(() => fixtureB.userData).thenReturn(Object());
contactListener.preSolve(contact, manifold);
verify<void>(
() => contactCallback.preSolve(
bodyB.userData!,
contact,
manifold,
),
).called(1);
verify<void>(
() => contactCallback.preSolve(
fixtureA.userData!,
contact,
manifold,
),
).called(1);
verify<void>(
() => contactCallback.preSolve(
fixtureB.userData!,
contact,
manifold,
),
).called(1);
verify<void>(
() => contactCallback.preSolve(
any(),
contact,
manifold,
),
).called(3);
},
);
test(
"doesn't callback itself",
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(contactCallback);
when(() => fixtureA.userData).thenReturn(null);
when(() => fixtureB.userData).thenReturn(null);
contactListener.preSolve(contact, manifold);
verifyNever<void>(
() => contactCallback.preSolve(any(), contact, manifold),
);
},
);
},
);
group(
'beginContact',
() {
test(
"doesn't callback if userData are null",
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(null);
when(() => fixtureA.userData).thenReturn(null);
when(() => fixtureB.userData).thenReturn(null);
contactListener.beginContact(contact);
verifyNever<void>(
() => contactCallback.beginContact(any(), contact),
);
},
);
test(
'callbacks for userData when not null',
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(Object());
when(() => fixtureA.userData).thenReturn(Object());
when(() => fixtureB.userData).thenReturn(Object());
contactListener.beginContact(contact);
verify<void>(
() => contactCallback.beginContact(bodyB.userData!, contact),
).called(1);
verify<void>(
() => contactCallback.beginContact(fixtureA.userData!, contact),
).called(1);
verify<void>(
() => contactCallback.beginContact(fixtureB.userData!, contact),
).called(1);
verify<void>(
() => contactCallback.beginContact(any(), contact),
).called(3);
},
);
test(
"doesn't callback itself",
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(contactCallback);
when(() => fixtureA.userData).thenReturn(null);
when(() => fixtureB.userData).thenReturn(null);
contactListener.beginContact(contact);
verifyNever<void>(
() => contactCallback.beginContact(any(), contact),
);
},
);
},
);
group(
'endContact',
() {
test(
"doesn't callback if userData are null",
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(null);
when(() => fixtureA.userData).thenReturn(null);
when(() => fixtureB.userData).thenReturn(null);
contactListener.endContact(contact);
verifyNever<void>(
() => contactCallback.endContact(any(), contact),
);
},
);
test(
'callbacks for userData when not null',
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(Object());
when(() => fixtureA.userData).thenReturn(Object());
when(() => fixtureB.userData).thenReturn(Object());
contactListener.endContact(contact);
verify<void>(
() => contactCallback.endContact(bodyB.userData!, contact),
).called(1);
verify<void>(
() => contactCallback.endContact(fixtureA.userData!, contact),
).called(1);
verify<void>(
() => contactCallback.endContact(fixtureB.userData!, contact),
).called(1);
verify<void>(
() => contactCallback.endContact(any(), contact),
).called(3);
},
);
test(
"doesn't callback itself",
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(contactCallback);
when(() => fixtureA.userData).thenReturn(null);
when(() => fixtureB.userData).thenReturn(null);
contactListener.endContact(contact);
verifyNever<void>(
() => contactCallback.endContact(any(), contact),
);
},
);
},
);
group(
'postSolve',
() {
late ContactImpulse contactImpulse;
setUp(() {
contactImpulse = MockContactImpulse();
});
test(
"doesn't callback if userData are null",
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(null);
when(() => fixtureA.userData).thenReturn(null);
when(() => fixtureB.userData).thenReturn(null);
contactListener.postSolve(contact, contactImpulse);
verifyNever<void>(
() => contactCallback.postSolve(any(), contact, contactImpulse),
);
},
);
test(
'callbacks for userData when not null',
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(Object());
when(() => fixtureA.userData).thenReturn(Object());
when(() => fixtureB.userData).thenReturn(Object());
contactListener.postSolve(contact, contactImpulse);
verify<void>(
() => contactCallback.postSolve(
bodyB.userData!,
contact,
contactImpulse,
),
).called(1);
verify<void>(
() => contactCallback.postSolve(
fixtureA.userData!,
contact,
contactImpulse,
),
).called(1);
verify<void>(
() => contactCallback.postSolve(
fixtureB.userData!,
contact,
contactImpulse,
),
).called(1);
verify<void>(
() => contactCallback.postSolve(
any(),
contact,
contactImpulse,
),
).called(3);
},
);
test(
"doesn't callback itself",
() {
final contactListener = WorldContactListener();
when(() => bodyA.userData).thenReturn(contactCallback);
when(() => bodyB.userData).thenReturn(contactCallback);
when(() => fixtureA.userData).thenReturn(null);
when(() => fixtureB.userData).thenReturn(null);
contactListener.postSolve(contact, contactImpulse);
verifyNever<void>(
() => contactCallback.postSolve(any(), contact, contactImpulse),
);
},
);
},
);
},
);
}
Loading…
Cancel
Save