Skip to content

Commit

Permalink
feat: Add globalToLocal and localToGlobal methods to viewport, viewfi…
Browse files Browse the repository at this point in the history
…nder and camera (#2720)

Add `globalToLocal` and `localToGlobal` methods to viewport, viewfinder and camera.
  • Loading branch information
luanpotter committed Sep 10, 2023
1 parent d460b84 commit 00185a3
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 5 deletions.
21 changes: 17 additions & 4 deletions packages/flame/lib/src/camera/camera_component.dart
Expand Up @@ -159,15 +159,28 @@ class CameraComponent extends Component {
canvas.restore();
}

/// Converts from the global (canvas) coordinate space to
/// local (camera = viewport + viewfinder).
///
/// Opposite of [localToGlobal].
Vector2 globalToLocal(Vector2 point) {
return viewfinder.globalToLocal(viewport.globalToLocal(point));
}

/// Converts from the local (camera = viewport + viewfinder) coordinate space
/// to global (canvas).
///
/// Opposite of [globalToLocal].
Vector2 localToGlobal(Vector2 position) {
return viewport.localToGlobal(viewfinder.localToGlobal(position));
}

@override
Iterable<Component> componentsAtPoint(
Vector2 point, [
List<Vector2>? nestedPoints,
]) sync* {
final viewportPoint = Vector2(
point.x - viewport.position.x + viewport.anchor.x * viewport.size.x,
point.y - viewport.position.y + viewport.anchor.y * viewport.size.y,
);
final viewportPoint = viewport.globalToLocal(point);
yield* viewport.componentsAtPoint(viewportPoint, nestedPoints);
if ((world?.isMounted ?? false) &&
currentCameras.length < maxCamerasDepth) {
Expand Down
16 changes: 16 additions & 0 deletions packages/flame/lib/src/camera/viewfinder.dart
Expand Up @@ -81,6 +81,22 @@ class Viewfinder extends Component
/// Reference to the parent camera.
CameraComponent get camera => parent! as CameraComponent;

/// Convert a point from the global coordinate system to the viewfinder's
/// coordinate system.
///
/// Opposite of [localToGlobal].
Vector2 globalToLocal(Vector2 point) {
return _transform.globalToLocal(point);
}

/// Convert a point from the viewfinder's coordinate system to the global
/// coordinate system.
///
/// Opposite of [globalToLocal].
Vector2 localToGlobal(Vector2 point) {
return _transform.localToGlobal(point);
}

/// How much of a game world ought to be visible through the viewport.
///
/// When this property is non-null, the viewfinder will automatically select
Expand Down
22 changes: 22 additions & 0 deletions packages/flame/lib/src/camera/viewport.dart
Expand Up @@ -111,4 +111,26 @@ abstract class Viewport extends Component
'A Viewport may only be attached to a CameraComponent',
);
}

/// Converts a point from the global coordinate system to the local
/// coordinate system of the viewport.
///
/// Opposite of [localToGlobal].
Vector2 globalToLocal(Vector2 point) {
return Vector2(
point.x - position.x + anchor.x * size.x,
point.y - position.y + anchor.y * size.y,
);
}

/// Converts a point from the local coordinate system of the viewport to the
/// global coordinate system.
///
/// Opposite of [globalToLocal].
Vector2 localToGlobal(Vector2 point) {
return Vector2(
point.x + position.x - anchor.x * size.x,
point.y + position.y - anchor.y * size.y,
);
}
}
1 change: 0 additions & 1 deletion packages/flame/lib/src/game/transform2d.dart
Expand Up @@ -195,7 +195,6 @@ class Transform2D extends ChangeNotifier {
///
/// If the current transform is degenerate due to one of the scale
/// factors being 0, then this method will return a zero vector.
///
Vector2 globalToLocal(Vector2 point) {
// Here we rely on the fact that in the transform matrix only elements
// `m[0]`, `m[1]`, `m[4]`, `m[5]`, `m[12]`, and `m[13]` are modified.
Expand Down
28 changes: 28 additions & 0 deletions packages/flame/test/camera/camera_component_test.dart
Expand Up @@ -369,6 +369,34 @@ void main() {
// can't see when the player world is known.
expect(camera.canSee(player, componentWorld: world1), false);
});

testWithFlameGame('coordinate transformations', (game) async {
game.onGameResize(Vector2.all(1000.0));

final size = Vector2.all(100.0);
final world = World();
final camera = CameraComponent.withFixedResolution(
width: size.x,
height: size.y,
world: world,
);

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

camera.moveBy(size / 2);
game.update(0);

expect(camera.globalToLocal(Vector2.zero()), Vector2.zero());
expect(camera.globalToLocal(Vector2.all(100.0)), Vector2.all(10.0));
expect(camera.globalToLocal(Vector2.all(500.0)), Vector2.all(50.0));
expect(camera.globalToLocal(Vector2.all(1000.0)), Vector2.all(100.0));

expect(camera.localToGlobal(Vector2.zero()), Vector2.zero());
expect(camera.localToGlobal(Vector2.all(10.0)), Vector2.all(100.0));
expect(camera.localToGlobal(Vector2.all(50.0)), Vector2.all(500.0));
expect(camera.localToGlobal(Vector2.all(100.0)), Vector2.all(1000.0));
});
});
}

Expand Down

0 comments on commit 00185a3

Please sign in to comment.