Skip to content

Commit

Permalink
fix: CameraComponent no longer throws Concurrent modification on stop (
Browse files Browse the repository at this point in the history
…#2997)

When camera is following a component and trying to follow another target
I got an exception:
```
dart:core                                              Iterable.forEach
package:flame/src/camera/camera_component.dart 326:25  CameraComponent.stop
package:flame/src/camera/camera_component.dart 309:5   CameraComponent.follow
test/camera/camera_component_test.dart 69:14           main.<fn>.<fn>
package:flame_test/src/test_flame_game.dart 80:21      testWithGame.<fn>

Concurrent modification during iteration: _Set len:0.
```

Copying viewfinder children before iterating through it and removing
child from parent does the trick, as we are not iterating through the
same list as we are removing items from, but rather a copy of it.

I tried to use camera from `game` provided in the test but it is AFAIK
not mounted and will hence not queue modifications (adds and removes).
Hence I create a new camera and mount it.
  • Loading branch information
lohnn authored Jan 23, 2024
1 parent 3c38ee6 commit 6a1059b
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/.cspell/gamedev_dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ raytracing # rendering techniques that calculates light rays as straight lines
rects # plural of rect
respawned # past tense of respawn
respawn # when the player character dies and is brought back after some time and penalties
retarget # to direct (something) toward a different target
RGBA # red green blue alpha
RGBO # red green blue opacity
rrect # rounded rect
Expand Down
2 changes: 1 addition & 1 deletion packages/flame/lib/src/camera/camera_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ class CameraComponent extends Component {

/// Removes all movement effects or behaviors from the viewfinder.
void stop() {
viewfinder.children.forEach((child) {
viewfinder.children.toList().forEach((child) {
if (child is FollowBehavior || child is MoveEffect) {
child.removeFromParent();
}
Expand Down
14 changes: 14 additions & 0 deletions packages/flame/test/camera/camera_component_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ void main() {
}
});

testWithFlameGame('camera should be able to retarget follow', (game) async {
// Creating new camera as the one included with game is not mounted and
// will therefore not be queued.
final camera = CameraComponent(world: game.world)..addToParent(game);
final player = PositionComponent()..addToParent(game.world);
final player2 = PositionComponent()..addToParent(game.world);
camera.follow(player);
camera.follow(player2);
await game.ready();

expect(camera.viewfinder.children.length, 1);
expect(camera.viewfinder.children.first, isA<FollowBehavior>());
});

testWithFlameGame('follow with snap', (game) async {
final world = World()..addToParent(game);
final player = PositionComponent()
Expand Down

0 comments on commit 6a1059b

Please sign in to comment.