Skip to content

Commit

Permalink
fix: Viewport should recieve events before the world (#2630)
Browse files Browse the repository at this point in the history
The viewport should receive events before the world, otherwise all huds will get the events after the world components, if there are any world components underneath them.
  • Loading branch information
spydon authored Aug 7, 2023
1 parent ff59aa1 commit e852064
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 18 deletions.
16 changes: 8 additions & 8 deletions examples/lib/stories/input/joystick_advanced_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class JoystickAdvancedExample extends FlameGame with HasCollisionDetection {
columns: 6,
rows: 1,
);
add(ScreenHitbox());
world.add(ScreenHitbox()..anchor = cameraComponent.viewfinder.anchor);
joystick = JoystickComponent(
knob: SpriteComponent(
sprite: sheet.getSpriteById(1),
Expand Down Expand Up @@ -184,13 +184,13 @@ class JoystickAdvancedExample extends FlameGame with HasCollisionDetection {
),
)..add(directionText);

add(player);
add(joystick);
add(flipButton);
add(flopButton);
add(buttonComponent);
add(spriteButtonComponent);
add(shapeButton);
world.add(player);
cameraComponent.viewport.add(joystick);
cameraComponent.viewport.add(flipButton);
cameraComponent.viewport.add(flopButton);
cameraComponent.viewport.add(buttonComponent);
cameraComponent.viewport.add(spriteButtonComponent);
cameraComponent.viewport.add(shapeButton);
cameraComponent.viewport.add(speedWithMargin);
cameraComponent.viewport.add(directionWithMargin);
}
Expand Down
1 change: 0 additions & 1 deletion examples/lib/stories/input/joystick_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class JoystickPlayer extends SpriteComponent
@override
Future<void> onLoad() async {
sprite = await gameRef.loadSprite('layers/player.png');
position = gameRef.size / 2;
add(RectangleHitbox());
}

Expand Down
4 changes: 2 additions & 2 deletions packages/flame/lib/src/camera/camera_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,17 @@ class CameraComponent extends Component {
point.x - viewport.position.x + viewport.anchor.x * viewport.size.x,
point.y - viewport.position.y + viewport.anchor.y * viewport.size.y,
);
yield* viewport.componentsAtPoint(viewportPoint, nestedPoints);
if ((world?.isMounted ?? false) &&
currentCameras.length < maxCamerasDepth) {
if (viewport.containsLocalPoint(viewportPoint)) {
currentCameras.add(this);
final worldPoint = viewfinder.transform.globalToLocal(viewportPoint);
yield* world!.componentsAtPoint(worldPoint, nestedPoints);
yield* viewfinder.componentsAtPoint(worldPoint, nestedPoints);
yield* world!.componentsAtPoint(worldPoint, nestedPoints);
currentCameras.removeLast();
}
}
yield* viewport.componentsAtPoint(viewportPoint, nestedPoints);
}

/// A camera that currently performs rendering.
Expand Down
6 changes: 3 additions & 3 deletions packages/flame/test/camera/camera_component_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,15 @@ void main() {
final nested = <Vector2>[];
final it = game.componentsAtPoint(Vector2(400, 300), nested).iterator;
expect(it.moveNext(), true);
expect(it.current, camera.viewport);
expect(nested, [Vector2(400, 300), Vector2(300, 200)]);
expect(it.moveNext(), true);
expect(it.current, component);
expect(nested, [Vector2(400, 300), Vector2(100, 50), Vector2(50, 20)]);
expect(it.moveNext(), true);
expect(it.current, world);
expect(nested, [Vector2(400, 300), Vector2(100, 50)]);
expect(it.moveNext(), true);
expect(it.current, camera.viewport);
expect(nested, [Vector2(400, 300), Vector2(300, 200)]);
expect(it.moveNext(), true);
expect(it.current, game);
expect(nested, [Vector2(400, 300)]);
expect(it.moveNext(), false);
Expand Down
10 changes: 8 additions & 2 deletions packages/flame/test/camera/viewports/circular_viewport_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,18 @@ void main() {

testWithFlameGame('hit testing', (game) async {
final world = _MyWorld();
final viewport = CircularViewport.ellipse(80, 20)
..position = Vector2(20, 30);
final camera = CameraComponent(
world: world,
viewport: CircularViewport.ellipse(80, 20)..position = Vector2(20, 30),
viewport: viewport,
);
game.addAll([world, camera]);
await game.ready();

bool hit(double x, double y) {
return game.componentsAtPoint(Vector2(x, y)).first == world;
final components = game.componentsAtPoint(Vector2(x, y)).toList();
return components.first == viewport && components[1] == world;
}

expect(hit(10, 20), false);
Expand All @@ -118,6 +121,9 @@ void main() {
final nestedPoints = <Vector2>[];
final center = Vector2(100, 50);
for (final component in game.componentsAtPoint(center, nestedPoints)) {
if (component == viewport) {
continue;
}
expect(component, world);
expect(nestedPoints.last, Vector2.zero());
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,20 @@ void main() {

testWithFlameGame('hit testing', (game) async {
final world = World();
final viewport = FixedAspectRatioViewport(aspectRatio: 1);
final camera = CameraComponent(
world: world,
viewport: FixedAspectRatioViewport(aspectRatio: 1),
viewport: viewport,
);
game.addAll([world, camera]);
game.onGameResize(Vector2(100, 200));
await game.ready();

bool hit(double x, double y) {
final components = game.componentsAtPoint(Vector2(x, y)).toList();
return components.isNotEmpty && components.first == world;
return components.isNotEmpty &&
components.first == viewport &&
components[1] == world;
}

for (final x in [0.0, 5.0, 50.0, 100.0]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,57 @@ void main() {
expect(game.tapCancelEvent, equals(0));
},
);

testWithFlameGame(
'viewport components should get events before world',
(game) async {
final component = _TapCallbacksComponent()
..x = 10
..y = 10
..width = 10
..height = 10;
final hudComponent = _TapCallbacksComponent()
..x = 10
..y = 10
..width = 10
..height = 10;
final world = World();
final cameraComponent = CameraComponent(world: world)
..viewfinder.anchor = Anchor.topLeft;

await game.ensureAddAll([world, cameraComponent]);
await world.ensureAdd(component);
await cameraComponent.viewport.ensureAdd(hudComponent);
final dispatcher = game.firstChild<MultiTapDispatcher>()!;

dispatcher.onTapDown(
createTapDownEvents(
game: game,
localPosition: const Offset(12, 12),
globalPosition: const Offset(12, 12),
),
);

expect(hudComponent.tapDownEvent, equals(1));
expect(hudComponent.tapUpEvent, equals(0));
expect(hudComponent.tapCancelEvent, equals(0));

expect(component.tapDownEvent, equals(0));
expect(component.tapUpEvent, equals(0));
expect(component.tapCancelEvent, equals(0));

dispatcher.onTapUp(
createTapUpEvents(
game: game,
localPosition: const Offset(12, 12),
globalPosition: const Offset(12, 12),
),
);

expect(hudComponent.tapUpEvent, equals(1));
expect(component.tapUpEvent, equals(0));
},
);
});
}

Expand Down

0 comments on commit e852064

Please sign in to comment.