Skip to content

Commit

Permalink
feat: Add optional world input to CameraComponent.canSee (#2616)
Browse files Browse the repository at this point in the history
CameraComponent.canSee wasn't performing any kind of sanity checks on the given components world or mounted-ness. This PR adds these checks to correctly return false if the component is not mounted or if the optional world is not the same as camera's current target world.
  • Loading branch information
ufrshubham authored Jul 21, 2023
1 parent 8e0a787 commit 1cad0b2
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 13 deletions.
20 changes: 19 additions & 1 deletion packages/flame/lib/src/camera/camera_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,25 @@ class CameraComponent extends Component {
}

/// Returns true if this camera is able to see the [component].
bool canSee(PositionComponent component) {
/// Will always return false if
/// - [world] is null or
/// - [world] is not mounted or
/// - [component] is not mounted or
/// - [componentWorld] is non-null and does not match with [world]
///
/// If [componentWorld] is null, this method does not take into consideration
/// the world to which the given [component] belongs (if any). This means, in
/// such cases, any component overlapping the [visibleWorldRect] will be
/// reported as visible, even if it is not part of the [world] this camera is
/// currently looking at. This can be changed by passing the the component's
/// world as [componentWorld].
bool canSee(PositionComponent component, {World? componentWorld}) {
if (!(world?.isMounted ?? false) ||
!component.isMounted ||
(componentWorld != null && componentWorld != world)) {
return false;
}

return visibleWorldRect.overlaps(component.toAbsoluteRect());
}
}
81 changes: 69 additions & 12 deletions packages/flame/test/camera/camera_component_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -280,39 +280,96 @@ void main() {
});

testWithFlameGame('component is in view for the camera', (game) async {
final world = World();
final component = PositionComponent(
size: Vector2(10, 10),
position: Vector2(0, 0),
);
final world = World(children: [component]);
final camera = CameraComponent(
world: world,
viewport: FixedSizeViewport(60, 40),
);
game.addAll([world, camera]);
await game.ready();

final component = PositionComponent(
size: Vector2(10, 10),
position: Vector2(0, 0),
);

expect(camera.canSee(component), isTrue);
});

testWithFlameGame('component is out of view for the camera', (game) async {
final world = World();
final component = PositionComponent(
size: Vector2(10, 10),
position: Vector2(100, 100),
);
final world = World(children: [component]);
final camera = CameraComponent(
world: world,
viewport: FixedSizeViewport(60, 40),
);
game.addAll([world, camera]);
await game.ready();

final component = PositionComponent(
size: Vector2(10, 10),
position: Vector2(100, 100),
);

expect(camera.canSee(component), isFalse);
});
});

group('CameraComponent.canSee', () {
testWithFlameGame('null world', (game) async {
final player = PositionComponent();
final world = World(children: [player]);
final camera = CameraComponent();

await game.addAll([camera, world]);
await game.ready();
expect(camera.canSee(player), false);

camera.world = world;
expect(camera.canSee(player), true);
});

testWithFlameGame('unmounted world', (game) async {
final player = PositionComponent();
final world = World(children: [player]);
final camera = CameraComponent(world: world);

await game.addAll([camera]);
await game.ready();
expect(camera.canSee(player), false);

await game.add(world);
await game.ready();
expect(camera.canSee(player), true);
});

testWithFlameGame('unmounted component', (game) async {
final player = PositionComponent();
final world = World();
final camera = CameraComponent(world: world);

await game.addAll([camera, world]);
await game.ready();
expect(camera.canSee(player), false);

await world.add(player);
await game.ready();
expect(camera.canSee(player), true);
});

testWithFlameGame('component from another world', (game) async {
final player = PositionComponent();
final world1 = World(children: [player]);
final world2 = World();
final camera = CameraComponent(world: world2);

await game.addAll([camera, world1, world2]);
await game.ready();

// can see when player world is not known.
expect(camera.canSee(player), true);

// can't see when the player world is known.
expect(camera.canSee(player, componentWorld: world1), false);
});
});
}

class _SolidBackground extends Component with HasPaint {
Expand Down

0 comments on commit 1cad0b2

Please sign in to comment.