fix: Viewport should recieve events before the world (#2630)

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.
This commit is contained in:
Lukas Klingsbo
2023-08-07 13:57:21 +02:00
committed by GitHub
parent ff59aa152c
commit e852064e49
7 changed files with 77 additions and 18 deletions

View File

@ -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),
@ -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);
}

View File

@ -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());
}

View File

@ -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.

View File

@ -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);

View File

@ -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);
@ -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;

View File

@ -71,9 +71,10 @@ 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));
@ -81,7 +82,9 @@ void main() {
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]) {

View File

@ -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));
},
);
});
}