mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 10:38:17 +08:00
feat: Add collision completed listener (#2896)
Add a new variable to the `CollisionDetection` class. Call `notifyListeners` at the end of the collision detection step, so that users can add a listener to their code to know when this step has finished. The variable is a basic implementation of a `Listenable` class, since it needs no more complexity. Closes #2849
This commit is contained in:
committed by
GitHub
parent
16a45b27a2
commit
957db3c1ed
@ -120,6 +120,43 @@ other way around. If there are no intersections with the edges on a solid hitbox
|
||||
position is instead returned.
|
||||
|
||||
|
||||
### Collision order
|
||||
|
||||
If a `Hitbox` collides with more than one other `Hitbox` within a given time step, then
|
||||
the `onCollision` callbacks will be called in an essentially random order. In some cases this can
|
||||
be a problem, such as in a bouncing ball game where the trajectory of the ball can differ depending
|
||||
on which other object was hit first. To help resolve this the `collisionsCompletedNotifier`
|
||||
listener can be used - this triggers at the end of the collision detection process.
|
||||
|
||||
An example of how this might be used is to add a local variable in your `PositionComponent` to save
|
||||
the other components with which it's colliding:
|
||||
`List<PositionComponent> collisionComponents = [];`. The `onCollision` callback is then used to
|
||||
save all the other `PositionComponent`s to this list:
|
||||
|
||||
```dart
|
||||
@override
|
||||
void onCollision(Set<Vector2> intersectionPoints, PositionComponent other) {
|
||||
collisionComponents.add(other);
|
||||
super.onCollision(intersectionPoints, other);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Finally, one adds a listener to the `onLoad` method of the `PositionComponent` to call a function
|
||||
which will resolve how the collisions should be dealt with:
|
||||
|
||||
```dart
|
||||
(game as HasCollisionDetection)
|
||||
.collisionDetection
|
||||
.collisionsCompletedNotifier
|
||||
.addListener(() {
|
||||
resolveCollisions();
|
||||
});
|
||||
```
|
||||
|
||||
The list `collisionComponents` would need to be cleared in each call to `update`.
|
||||
|
||||
|
||||
## ShapeHitbox
|
||||
|
||||
The `ShapeHitbox`s are normal components, so you add them to the component that you want to add
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flame/collisions.dart';
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/geometry.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// [CollisionDetection] is the foundation of the collision detection system in
|
||||
/// Flame.
|
||||
@ -13,6 +14,7 @@ abstract class CollisionDetection<T extends Hitbox<T>,
|
||||
|
||||
List<T> get items => broadphase.items;
|
||||
final _lastPotentials = <CollisionProspect<T>>[];
|
||||
final collisionsCompletedNotifier = CollisionDetectionCompletionNotifier();
|
||||
|
||||
CollisionDetection({required this.broadphase});
|
||||
|
||||
@ -62,6 +64,9 @@ abstract class CollisionDetection<T extends Hitbox<T>,
|
||||
}
|
||||
}
|
||||
_updateLastPotentials(potentials);
|
||||
|
||||
// Let all listeners know that the collision detection step has completed
|
||||
collisionsCompletedNotifier.notifyListeners();
|
||||
}
|
||||
|
||||
final _lastPotentialsPool = <CollisionProspect<T>>[];
|
||||
@ -161,3 +166,10 @@ abstract class CollisionDetection<T extends Hitbox<T>,
|
||||
List<RaycastResult<T>>? out,
|
||||
});
|
||||
}
|
||||
|
||||
/// A class to handle callbacks for when the collision detection is done each
|
||||
/// tick.
|
||||
class CollisionDetectionCompletionNotifier extends ChangeNotifier {
|
||||
@override
|
||||
void notifyListeners() => super.notifyListeners();
|
||||
}
|
||||
|
||||
@ -1756,6 +1756,21 @@ void main() {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
group('collisionsCompletedNotifier', () {
|
||||
runCollisionTestRegistry({
|
||||
'collisionsCompletedNotifier calls listeners': (game) async {
|
||||
var calledTimes = 0;
|
||||
final listeners = List.generate(10, (_) => () => calledTimes++);
|
||||
for (final listener in listeners) {
|
||||
game.collisionDetection.collisionsCompletedNotifier
|
||||
.addListener(listener);
|
||||
}
|
||||
game.update(0);
|
||||
expect(calledTimes, listeners.length);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class _CollisionDetectionGame extends FlameGame with HasCollisionDetection {}
|
||||
|
||||
Reference in New Issue
Block a user