mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 20:13:50 +08:00
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:
@ -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);
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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]) {
|
||||
|
||||
@ -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));
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user