From 130994e1f109071c690d4a8a970b28e106d9625e Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 6 Aug 2024 21:13:02 +0200 Subject: [PATCH] refactor: Don't use static for Distance and Collision (#92) # Description This removes the use of `static` for `Collision` and `Distance` so that multiple worlds can be used simultaneously and old stuff is GCed properly. ## Checklist - [x] The title of my PR starts with a [Conventional Commit] prefix (`fix:`, `feat:`, `docs:` etc). - [x] I have read the [Contributor Guide] and followed the process outlined for submitting PRs. - [x] I have updated/added tests for ALL new/updated/fixed functionality. - [x] I have updated/added relevant documentation in `docs` and added dartdoc comments with `///`. - [x] I have updated/added relevant examples in `examples`. ## Breaking Change - [x] No, this is *not* a breaking change. ## Related Issues [issue database]: https://github.com/flame-engine/flame/issues [Contributor Guide]: https://github.com/flame-engine/flame/blob/main/CONTRIBUTING.md [Flame Style Guide]: https://github.com/flame-engine/flame/blob/main/STYLEGUIDE.md [Conventional Commit]: https://conventionalcommits.org --- packages/forge2d/example/web/ball_cage.dart | 13 ++---- packages/forge2d/example/web/blob_test.dart | 13 ++---- packages/forge2d/example/web/box_test.dart | 13 ++---- .../forge2d/example/web/domino_tower.dart | 13 ++---- .../example/web/friction_joint_test.dart | 15 +++---- packages/forge2d/example/web/particles.dart | 13 ++---- packages/forge2d/example/web/racer.dart | 18 +++----- .../forge2d/lib/src/collision/collision.dart | 3 +- .../lib/src/collision/time_of_impact.dart | 4 +- .../lib/src/dynamics/contact_manager.dart | 13 +++++- .../contacts/chain_and_circle_contact.dart | 4 +- .../contacts/chain_and_polygon_contact.dart | 4 +- .../src/dynamics/contacts/circle_contact.dart | 12 +++-- .../lib/src/dynamics/contacts/contact.dart | 44 ++++++++++++++++--- .../contacts/edge_and_circle_contact.dart | 4 +- .../contacts/edge_and_polygon_contact.dart | 4 +- .../contacts/polygon_and_circle_contact.dart | 12 +++-- .../dynamics/contacts/polygon_contact.dart | 12 +++-- packages/forge2d/lib/src/dynamics/world.dart | 11 +++-- .../test/dynamics/contacts/contact_test.dart | 10 +++++ 20 files changed, 135 insertions(+), 100 deletions(-) diff --git a/packages/forge2d/example/web/ball_cage.dart b/packages/forge2d/example/web/ball_cage.dart index faebc9f..6d8513a 100644 --- a/packages/forge2d/example/web/ball_cage.dart +++ b/packages/forge2d/example/web/ball_cage.dart @@ -16,14 +16,6 @@ class BallCage extends Demo { /// Constructs a new BallCage. BallCage() : super('Ball cage'); - /// Entrypoint. - static void main() { - final cage = BallCage(); - cage.initialize(); - cage.initializeAnimation(); - cage.runAnimation(); - } - @override void initialize() { // Define the circle shape. @@ -91,5 +83,8 @@ class BallCage extends Demo { } void main() { - BallCage.main(); + final cage = BallCage(); + cage.initialize(); + cage.initializeAnimation(); + cage.runAnimation(); } diff --git a/packages/forge2d/example/web/blob_test.dart b/packages/forge2d/example/web/blob_test.dart index bc26723..fbca10c 100644 --- a/packages/forge2d/example/web/blob_test.dart +++ b/packages/forge2d/example/web/blob_test.dart @@ -8,14 +8,6 @@ class BlobTest extends Demo { /// Constructs a new BlobTest. BlobTest() : super('Blob test'); - /// Entrypoint. - static void main() { - final blob = BlobTest(); - blob.initialize(); - blob.initializeAnimation(); - blob.runAnimation(); - } - @override void initialize() { Body ground; @@ -79,5 +71,8 @@ class BlobTest extends Demo { } void main() { - BlobTest.main(); + final blob = BlobTest(); + blob.initialize(); + blob.initializeAnimation(); + blob.runAnimation(); } diff --git a/packages/forge2d/example/web/box_test.dart b/packages/forge2d/example/web/box_test.dart index 29edfcc..2f80cab 100644 --- a/packages/forge2d/example/web/box_test.dart +++ b/packages/forge2d/example/web/box_test.dart @@ -8,14 +8,6 @@ class BoxTest extends Demo { /// Constructs a new BoxTest. BoxTest() : super('Box test'); - /// Entrypoint. - static void main() { - final boxTest = BoxTest(); - boxTest.initialize(); - boxTest.initializeAnimation(); - boxTest.runAnimation(); - } - @override void initialize() { _createGround(); @@ -69,5 +61,8 @@ class BoxTest extends Demo { } void main() { - BoxTest.main(); + final boxTest = BoxTest(); + boxTest.initialize(); + boxTest.initializeAnimation(); + boxTest.runAnimation(); } diff --git a/packages/forge2d/example/web/domino_tower.dart b/packages/forge2d/example/web/domino_tower.dart index d22dc71..1322c12 100644 --- a/packages/forge2d/example/web/domino_tower.dart +++ b/packages/forge2d/example/web/domino_tower.dart @@ -17,14 +17,6 @@ class DominoTower extends Demo { /// Construct a DominoTower. DominoTower() : super('Domino tower'); - /// Entrypoint. - static void main() { - final tower = DominoTower(); - tower.initialize(); - tower.initializeAnimation(); - tower.runAnimation(); - } - void makeDomino(double x, double y, {required bool horizontal}) { final shape = PolygonShape() ..setAsBoxXY(.5 * dominoWidth, .5 * dominoHeight); @@ -142,5 +134,8 @@ class DominoTower extends Demo { } void main() { - DominoTower.main(); + final tower = DominoTower(); + tower.initialize(); + tower.initializeAnimation(); + tower.runAnimation(); } diff --git a/packages/forge2d/example/web/friction_joint_test.dart b/packages/forge2d/example/web/friction_joint_test.dart index 3f67afa..2dcae2f 100644 --- a/packages/forge2d/example/web/friction_joint_test.dart +++ b/packages/forge2d/example/web/friction_joint_test.dart @@ -7,15 +7,6 @@ import 'demo.dart'; class FrictionJointTest extends Demo { FrictionJointTest() : super('FrictionJoint test'); - /// Entrypoint. - static void main() { - final test = FrictionJointTest(); - test.initialize(); - test.initializeAnimation(); - test.debugDraw.appendFlags(DebugDraw.jointBit); - test.runAnimation(); - } - late Body _ground; late FixtureDef _boxFixture; @@ -100,5 +91,9 @@ class FrictionJointTest extends Demo { } void main() { - FrictionJointTest.main(); + final test = FrictionJointTest(); + test.initialize(); + test.initializeAnimation(); + test.debugDraw.appendFlags(DebugDraw.jointBit); + test.runAnimation(); } diff --git a/packages/forge2d/example/web/particles.dart b/packages/forge2d/example/web/particles.dart index c9dc22c..5e3dbf7 100644 --- a/packages/forge2d/example/web/particles.dart +++ b/packages/forge2d/example/web/particles.dart @@ -15,14 +15,6 @@ class Particles extends Demo { /// Constructs a new Particles example. Particles() : super('Particles'); - - static void main() { - Particles() - ..initialize() - ..initializeAnimation() - ..runAnimation(); - } - @override void initialize() { // Define the circle shape. @@ -103,5 +95,8 @@ class Particles extends Demo { } void main() { - Particles.main(); + Particles() + ..initialize() + ..initializeAnimation() + ..runAnimation(); } diff --git a/packages/forge2d/example/web/racer.dart b/packages/forge2d/example/web/racer.dart index ac79eb2..0036e46 100644 --- a/packages/forge2d/example/web/racer.dart +++ b/packages/forge2d/example/web/racer.dart @@ -10,16 +10,6 @@ import 'racer/ground_area.dart'; import 'racer/tire.dart'; class Racer extends Demo implements ContactListener { - static void main() { - final racer = Racer(); - racer.initialize(); - racer.initializeAnimation(); - final paragraph = ParagraphElement() - ..innerText = 'Use the arrow keys to drive the car'; - document.body?.nodes.add(paragraph); - racer.runAnimation(); - } - Racer() : super('Racer', Vector2.zero(), 2.5); late int _controlState; @@ -186,5 +176,11 @@ class Racer extends Demo implements ContactListener { } void main() { - Racer.main(); + final racer = Racer(); + racer.initialize(); + racer.initializeAnimation(); + final paragraph = ParagraphElement() + ..innerText = 'Use the arrow keys to drive the car'; + document.body?.nodes.add(paragraph); + racer.runAnimation(); } diff --git a/packages/forge2d/lib/src/collision/collision.dart b/packages/forge2d/lib/src/collision/collision.dart index 5422a04..e2003cc 100644 --- a/packages/forge2d/lib/src/collision/collision.dart +++ b/packages/forge2d/lib/src/collision/collision.dart @@ -98,6 +98,7 @@ class Collision { int indexB, Transform xfA, Transform xfB, + Distance distance, ) { _input.proxyA.set(shapeA, indexA); _input.proxyB.set(shapeB, indexB); @@ -107,7 +108,7 @@ class Collision { _cache.count = 0; - World.distance.compute(_output, _cache, _input); + distance.compute(_output, _cache, _input); // djm note: anything significant about 10.0f? return _output.distance < 10.0 * settings.epsilon; } diff --git a/packages/forge2d/lib/src/collision/time_of_impact.dart b/packages/forge2d/lib/src/collision/time_of_impact.dart index 95cd40b..dd85b32 100644 --- a/packages/forge2d/lib/src/collision/time_of_impact.dart +++ b/packages/forge2d/lib/src/collision/time_of_impact.dart @@ -53,7 +53,7 @@ class TimeOfImpact { /// If you change the time interval, you should call this function again. /// Note: use Distance to compute the contact point and normal at the time of /// impact. - void timeOfImpact(TOIOutput output, TOIInput input) { + void timeOfImpact(TOIOutput output, TOIInput input, Distance distance) { // CCD via the local separating axis method. This seeks progression // by computing the largest time at which separation is maintained. @@ -100,7 +100,7 @@ class TimeOfImpact { // to get a separating axis _distanceInput.transformA = _xfA; _distanceInput.transformB = _xfB; - World.distance.compute(_distanceOutput, _cache, _distanceInput); + distance.compute(_distanceOutput, _cache, _distanceInput); // If the shapes are overlapped, we give up on continuous collision. if (_distanceOutput.distance <= 0.0) { diff --git a/packages/forge2d/lib/src/dynamics/contact_manager.dart b/packages/forge2d/lib/src/dynamics/contact_manager.dart index fbd0f94..20b8cc4 100644 --- a/packages/forge2d/lib/src/dynamics/contact_manager.dart +++ b/packages/forge2d/lib/src/dynamics/contact_manager.dart @@ -3,11 +3,13 @@ import 'package:forge2d/forge2d.dart'; /// Delegate of World. class ContactManager implements PairCallback { BroadPhase broadPhase; + final Collision collision; + final Distance distance; final List contacts = []; ContactFilter? contactFilter; ContactListener? contactListener; - ContactManager(this.broadPhase) { + ContactManager(this.broadPhase, this.collision, this.distance) { contactFilter = ContactFilter(); } @@ -51,7 +53,14 @@ class ContactManager implements PairCallback { return; } - final contact = Contact.init(fixtureA, indexA, fixtureB, indexB); + final contact = Contact.fromPair( + fixtureA, + indexA, + fixtureB, + indexB, + collision, + distance, + ); // Insert into the world. contacts.add(contact); diff --git a/packages/forge2d/lib/src/dynamics/contacts/chain_and_circle_contact.dart b/packages/forge2d/lib/src/dynamics/contacts/chain_and_circle_contact.dart index 9d97cbe..dbe77f5 100644 --- a/packages/forge2d/lib/src/dynamics/contacts/chain_and_circle_contact.dart +++ b/packages/forge2d/lib/src/dynamics/contacts/chain_and_circle_contact.dart @@ -6,6 +6,8 @@ class ChainAndCircleContact extends Contact { super.indexA, super.fixtureB, super.indexB, + super.collision, + super.distance, ) : assert(fixtureA.type == ShapeType.chain), assert(fixtureB.type == ShapeType.circle); @@ -13,7 +15,7 @@ class ChainAndCircleContact extends Contact { void evaluate(Manifold manifold, Transform xfA, Transform xfB) { final chain = fixtureA.shape as ChainShape; final edge = chain.childEdge(indexA); - World.collision.collideEdgeAndCircle( + collision.collideEdgeAndCircle( manifold, edge, xfA, diff --git a/packages/forge2d/lib/src/dynamics/contacts/chain_and_polygon_contact.dart b/packages/forge2d/lib/src/dynamics/contacts/chain_and_polygon_contact.dart index 9fb7db1..7379e6f 100644 --- a/packages/forge2d/lib/src/dynamics/contacts/chain_and_polygon_contact.dart +++ b/packages/forge2d/lib/src/dynamics/contacts/chain_and_polygon_contact.dart @@ -6,6 +6,8 @@ class ChainAndPolygonContact extends Contact { super.indexA, super.fixtureB, super.indexB, + super.collision, + super.distance, ) : assert(fixtureA.type == ShapeType.chain), assert(fixtureB.type == ShapeType.polygon); @@ -13,7 +15,7 @@ class ChainAndPolygonContact extends Contact { void evaluate(Manifold manifold, Transform xfA, Transform xfB) { final chain = fixtureA.shape as ChainShape; final edge = chain.childEdge(indexA); - World.collision.collideEdgeAndPolygon( + collision.collideEdgeAndPolygon( manifold, edge, xfA, diff --git a/packages/forge2d/lib/src/dynamics/contacts/circle_contact.dart b/packages/forge2d/lib/src/dynamics/contacts/circle_contact.dart index b97bb85..8ea28fc 100644 --- a/packages/forge2d/lib/src/dynamics/contacts/circle_contact.dart +++ b/packages/forge2d/lib/src/dynamics/contacts/circle_contact.dart @@ -1,14 +1,18 @@ import 'package:forge2d/forge2d.dart'; class CircleContact extends Contact { - CircleContact(Fixture fixtureA, Fixture fixtureB) - : assert(fixtureA.type == ShapeType.circle), + CircleContact( + Fixture fixtureA, + Fixture fixtureB, + Collision collision, + Distance distance, + ) : assert(fixtureA.type == ShapeType.circle), assert(fixtureB.type == ShapeType.circle), - super(fixtureA, 0, fixtureB, 0); + super(fixtureA, 0, fixtureB, 0, collision, distance); @override void evaluate(Manifold manifold, Transform xfA, Transform xfB) { - World.collision.collideCircles( + collision.collideCircles( manifold, fixtureA.shape as CircleShape, xfA, diff --git a/packages/forge2d/lib/src/dynamics/contacts/contact.dart b/packages/forge2d/lib/src/dynamics/contacts/contact.dart index 6a66455..1783fdc 100644 --- a/packages/forge2d/lib/src/dynamics/contacts/contact.dart +++ b/packages/forge2d/lib/src/dynamics/contacts/contact.dart @@ -21,6 +21,8 @@ abstract class Contact { static const int toiFlag = 0x0020; int flags = 0; + final Collision collision; + final Distance distance; final Fixture fixtureA; final Fixture fixtureB; @@ -48,7 +50,14 @@ abstract class Contact { double tangentSpeed = 0.0; - Contact(this.fixtureA, this.indexA, this.fixtureB, this.indexB) { + Contact( + this.fixtureA, + this.indexA, + this.fixtureB, + this.indexB, + this.collision, + this.distance, + ) { flags = enabledFlag; manifold.pointCount = 0; _friction = Contact.mixFriction( @@ -61,11 +70,13 @@ abstract class Contact { ); } - static Contact init( + factory Contact.fromPair( Fixture fixtureA, int indexA, Fixture fixtureB, int indexB, + Collision collision, + Distance distance, ) { // Remember that we use the order in the enum here to determine in which // order the arguments should come in the different contact classes. @@ -83,17 +94,24 @@ abstract class Contact { final secondFixture = fixtureB.type == typeB ? fixtureB : temp; if (typeA == ShapeType.circle && typeB == ShapeType.circle) { - return CircleContact(firstFixture, secondFixture); + return CircleContact(firstFixture, secondFixture, collision, distance); } else if (typeA == ShapeType.polygon && typeB == ShapeType.polygon) { - return PolygonContact(firstFixture, secondFixture); + return PolygonContact(firstFixture, secondFixture, collision, distance); } else if (typeA == ShapeType.circle && typeB == ShapeType.polygon) { - return PolygonAndCircleContact(secondFixture, firstFixture); + return PolygonAndCircleContact( + secondFixture, + firstFixture, + collision, + distance, + ); } else if (typeA == ShapeType.circle && typeB == ShapeType.edge) { return EdgeAndCircleContact( secondFixture, secondIndex, firstFixture, firstIndex, + collision, + distance, ); } else if (typeA == ShapeType.edge && typeB == ShapeType.polygon) { return EdgeAndPolygonContact( @@ -101,6 +119,8 @@ abstract class Contact { firstIndex, secondFixture, secondIndex, + collision, + distance, ); } else if (typeA == ShapeType.circle && typeB == ShapeType.chain) { return ChainAndCircleContact( @@ -108,6 +128,8 @@ abstract class Contact { secondIndex, firstFixture, firstIndex, + collision, + distance, ); } else if (typeA == ShapeType.polygon && typeB == ShapeType.chain) { return ChainAndPolygonContact( @@ -115,10 +137,17 @@ abstract class Contact { secondIndex, firstFixture, firstIndex, + collision, + distance, ); } else { assert(false, 'Not compatible contact type'); - return CircleContact(firstFixture, secondFixture); + return CircleContact( + firstFixture, + secondFixture, + collision, + distance, + ); } } @@ -217,13 +246,14 @@ abstract class Contact { if (sensor) { final shapeA = fixtureA.shape; final shapeB = fixtureB.shape; - touching = World.collision.testOverlap( + touching = collision.testOverlap( shapeA, indexA, shapeB, indexB, xfA, xfB, + distance, ); // Sensors don't generate manifolds. diff --git a/packages/forge2d/lib/src/dynamics/contacts/edge_and_circle_contact.dart b/packages/forge2d/lib/src/dynamics/contacts/edge_and_circle_contact.dart index 1372afd..414ba84 100644 --- a/packages/forge2d/lib/src/dynamics/contacts/edge_and_circle_contact.dart +++ b/packages/forge2d/lib/src/dynamics/contacts/edge_and_circle_contact.dart @@ -6,12 +6,14 @@ class EdgeAndCircleContact extends Contact { super.indexA, super.fixtureB, super.indexB, + super.collision, + super.distance, ) : assert(fixtureA.type == ShapeType.edge), assert(fixtureB.type == ShapeType.circle); @override void evaluate(Manifold manifold, Transform xfA, Transform xfB) { - World.collision.collideEdgeAndCircle( + collision.collideEdgeAndCircle( manifold, fixtureA.shape as EdgeShape, xfA, diff --git a/packages/forge2d/lib/src/dynamics/contacts/edge_and_polygon_contact.dart b/packages/forge2d/lib/src/dynamics/contacts/edge_and_polygon_contact.dart index 5a68301..be8e0e3 100644 --- a/packages/forge2d/lib/src/dynamics/contacts/edge_and_polygon_contact.dart +++ b/packages/forge2d/lib/src/dynamics/contacts/edge_and_polygon_contact.dart @@ -6,12 +6,14 @@ class EdgeAndPolygonContact extends Contact { super.indexA, super.fixtureB, super.indexB, + super.collision, + super.distance, ) : assert(fixtureA.type == ShapeType.edge), assert(fixtureB.type == ShapeType.polygon); @override void evaluate(Manifold manifold, Transform xfA, Transform xfB) { - World.collision.collideEdgeAndPolygon( + collision.collideEdgeAndPolygon( manifold, fixtureA.shape as EdgeShape, xfA, diff --git a/packages/forge2d/lib/src/dynamics/contacts/polygon_and_circle_contact.dart b/packages/forge2d/lib/src/dynamics/contacts/polygon_and_circle_contact.dart index c3f7184..229f65c 100644 --- a/packages/forge2d/lib/src/dynamics/contacts/polygon_and_circle_contact.dart +++ b/packages/forge2d/lib/src/dynamics/contacts/polygon_and_circle_contact.dart @@ -1,14 +1,18 @@ import 'package:forge2d/forge2d.dart'; class PolygonAndCircleContact extends Contact { - PolygonAndCircleContact(Fixture fixtureA, Fixture fixtureB) - : assert(fixtureA.type == ShapeType.polygon), + PolygonAndCircleContact( + Fixture fixtureA, + Fixture fixtureB, + Collision collision, + Distance distance, + ) : assert(fixtureA.type == ShapeType.polygon), assert(fixtureB.type == ShapeType.circle), - super(fixtureA, 0, fixtureB, 0); + super(fixtureA, 0, fixtureB, 0, collision, distance); @override void evaluate(Manifold manifold, Transform xfA, Transform xfB) { - World.collision.collidePolygonAndCircle( + collision.collidePolygonAndCircle( manifold, fixtureA.shape as PolygonShape, xfA, diff --git a/packages/forge2d/lib/src/dynamics/contacts/polygon_contact.dart b/packages/forge2d/lib/src/dynamics/contacts/polygon_contact.dart index 3fe4c6d..91b714d 100644 --- a/packages/forge2d/lib/src/dynamics/contacts/polygon_contact.dart +++ b/packages/forge2d/lib/src/dynamics/contacts/polygon_contact.dart @@ -1,14 +1,18 @@ import 'package:forge2d/forge2d.dart'; class PolygonContact extends Contact { - PolygonContact(Fixture fixtureA, Fixture fixtureB) - : assert(fixtureA.type == ShapeType.polygon), + PolygonContact( + Fixture fixtureA, + Fixture fixtureB, + Collision collision, + Distance distance, + ) : assert(fixtureA.type == ShapeType.polygon), assert(fixtureB.type == ShapeType.polygon), - super(fixtureA, 0, fixtureB, 0); + super(fixtureA, 0, fixtureB, 0, collision, distance); @override void evaluate(Manifold manifold, Transform xfA, Transform xfB) { - World.collision.collidePolygons( + collision.collidePolygons( manifold, fixtureA.shape as PolygonShape, xfA, diff --git a/packages/forge2d/lib/src/dynamics/world.dart b/packages/forge2d/lib/src/dynamics/world.dart index 5e566bf..9ec531b 100644 --- a/packages/forge2d/lib/src/dynamics/world.dart +++ b/packages/forge2d/lib/src/dynamics/world.dart @@ -16,10 +16,9 @@ class World { static const int locked = 0x0002; static const int clearForcesBit = 0x0004; - // TODO(spydon): Don't have these fields as static - static final Distance distance = Distance(); - static final Collision collision = Collision(); - static final TimeOfImpact toi = TimeOfImpact(); + final Distance distance = Distance(); + final Collision collision = Collision(); + final TimeOfImpact toi = TimeOfImpact(); int flags = 0; @@ -85,7 +84,7 @@ class World { _invDt0 = 0.0; - contactManager = ContactManager(broadPhase); + contactManager = ContactManager(broadPhase, collision, distance); _profile = Profile(); particleSystem = ParticleSystem(this); @@ -775,7 +774,7 @@ class World { input.sweepB.setFrom(bodyB.sweep); input.tMax = 1.0; - toi.timeOfImpact(_toiOutput, input); + toi.timeOfImpact(_toiOutput, input, distance); // Beta is the fraction of the remaining portion of the . final beta = _toiOutput.t; diff --git a/packages/forge2d/test/dynamics/contacts/contact_test.dart b/packages/forge2d/test/dynamics/contacts/contact_test.dart index 21dbccc..3900981 100644 --- a/packages/forge2d/test/dynamics/contacts/contact_test.dart +++ b/packages/forge2d/test/dynamics/contacts/contact_test.dart @@ -10,6 +10,8 @@ class _TestContact extends Contact { super.indexA, super.fixtureB, super.indexB, + super.collision, + super.distance, ); @override @@ -22,6 +24,8 @@ void main() { late int indexA; late Fixture fixtureB; late int indexB; + final collision = Collision(); + final distance = Distance(); setUp(() { fixtureA = _MockFixture(); @@ -43,6 +47,8 @@ void main() { indexA, fixtureB, indexB, + collision, + distance, ), isA(), ); @@ -55,6 +61,8 @@ void main() { indexA, fixtureB, indexB, + collision, + distance, ); expect(contact.isEnabled, isTrue); @@ -66,6 +74,8 @@ void main() { indexA, fixtureB, indexB, + collision, + distance, ); final newIsEnabled = !contact.isEnabled;