diff --git a/doc/camera_and_viewport.md b/doc/camera_and_viewport.md index 2d657b25b..fd983448d 100644 --- a/doc/camera_and_viewport.md +++ b/doc/camera_and_viewport.md @@ -29,18 +29,18 @@ your needs): When using `BaseGame`, the operations performed by the viewport are done automatically to every render operation, and the `size` property in the game, instead of the logical widget size, becomes -the size as seen through the viewport. If for some reason you need to access the original real -logical pixel size, you can use `canvasSize`. For a more in depth description on what each Viewport -does and how it operates, check the documentation on its class. +the size as seen through the viewport together with the zoom of the camera. If for some reason you +need to access the original real logical pixel size, you can use `canvasSize`. For a more in depth +description on what each `Viewport` does and how it operates, check the documentation on its class. ## Camera -Unlike the Viewport, the Camera is a more dynamic Canvas transformation that is normally dependent -on: +Unlike the `Viewport`, the `Camera` is a more dynamic `Canvas` transformation that is normally +dependent on: - * world coordinates that do not match screen coordinates 1:1 - * centering or following the player around the game world (if it's bigger than the screen) - * user controlled zooming in and out + * World coordinates that do not match screen coordinates 1:1. + * Centering or following the player around the game world (if the world is bigger than the screen). + * User controlled zooming in and out. There is only one Camera implementation but it allows for many different configurations. Again, you can use it standalone on your `Game` but it's already included and wired into `BaseGame`. diff --git a/examples/lib/stories/collision_detection/multiple_shapes.dart b/examples/lib/stories/collision_detection/multiple_shapes.dart index 36e257647..20c16f11a 100644 --- a/examples/lib/stories/collision_detection/multiple_shapes.dart +++ b/examples/lib/stories/collision_detection/multiple_shapes.dart @@ -228,9 +228,7 @@ class MultipleShapes extends BaseGame lastToAdd = createRandomCollidable(lastToAdd, screenCollidable); final lastBottomRight = lastToAdd.toAbsoluteRect().bottomRight.toVector2(); - final screenSize = size / camera.zoom; - if (lastBottomRight.x < screenSize.x && - lastBottomRight.y < screenSize.y) { + if (lastBottomRight.x < size.x && lastBottomRight.y < size.y) { add(lastToAdd); totalAdded++; } else { diff --git a/packages/flame/CHANGELOG.md b/packages/flame/CHANGELOG.md index f136d21dd..345d6918c 100644 --- a/packages/flame/CHANGELOG.md +++ b/packages/flame/CHANGELOG.md @@ -18,6 +18,8 @@ - Refactor TextBoxComponent - Fix bugs with TextBoxComponent - Improve error message for composed components + - Fix `game.size` to take zoom into consideration + - Fix `camera.followComponent` when `zoom != 1` - Add `anchor` for `ShapeComponent` constructor - Fix rendering of polygons in `ShapeComponent` - Add `SpriteAnimation` support to parallax diff --git a/packages/flame/lib/src/components/mixins/collidable.dart b/packages/flame/lib/src/components/mixins/collidable.dart index cc27857d4..44fee5765 100644 --- a/packages/flame/lib/src/components/mixins/collidable.dart +++ b/packages/flame/lib/src/components/mixins/collidable.dart @@ -26,28 +26,18 @@ class ScreenCollidable extends PositionComponent @override CollidableType collidableType = CollidableType.passive; - final Vector2 _effectiveSize = Vector2.zero(); - double _zoom = 1.0; - @override Future onLoad() async { await super.onLoad(); - _updateSize(); + size = gameRef.size; addShape(HitboxRectangle()); } + final _zeroVector = Vector2.zero(); @override void update(double dt) { super.update(dt); - _updateSize(); - } - - void _updateSize() { - if (_effectiveSize != gameRef.viewport.effectiveSize || - _zoom != gameRef.camera.zoom) { - _effectiveSize.setFrom(gameRef.viewport.effectiveSize); - _zoom = gameRef.camera.zoom; - size = _effectiveSize / _zoom; - } + position = gameRef.camera.unprojectVector(_zeroVector); + size = gameRef.size; } } diff --git a/packages/flame/lib/src/game/base_game.dart b/packages/flame/lib/src/game/base_game.dart index 9a641b99e..2307d58a0 100644 --- a/packages/flame/lib/src/game/base_game.dart +++ b/packages/flame/lib/src/game/base_game.dart @@ -65,15 +65,20 @@ class BaseGame extends Game with FPSCounter { late Projector _combinedProjector; + final Vector2 _sizeBuffer = Vector2.zero(); + /// This is overwritten to consider the viewport transformation. /// /// Which means that this is the logical size of the game screen area as - /// exposed to the canvas after viewport transformations. + /// exposed to the canvas after viewport transformations and camera zooming. + /// /// This does not match the Flutter widget size; for that see [canvasSize]. @override Vector2 get size { assertHasLayout(); - return viewport.effectiveSize; + return _sizeBuffer + ..setFrom(viewport.effectiveSize) + ..scale(1 / camera.zoom); } /// This is the original Flutter widget size, without any transformation. diff --git a/packages/flame/lib/src/game/camera.dart b/packages/flame/lib/src/game/camera.dart index 5bd9bac2d..5054d5166 100644 --- a/packages/flame/lib/src/game/camera.dart +++ b/packages/flame/lib/src/game/camera.dart @@ -203,6 +203,18 @@ class Camera extends Projector { return worldCoordinates * zoom; } + /// Takes coordinates in the screen space and returns their counter-part in + /// the world space. + Vector2 screenToWorld(Vector2 screenCoordinates) { + return unprojectVector(screenCoordinates); + } + + /// Takes coordinates in the world space and returns their counter-part in + /// the screen space. + Vector2 worldToScreen(Vector2 worldCoordinates) { + return projectVector(worldCoordinates); + } + /// This is the (current) absolute target of the camera, i.e., the /// coordinate that should be on the top left, regardless of relative /// offset, world boundaries or shake. diff --git a/packages/flame/test/game/projections_test.dart b/packages/flame/test/game/projections_test.dart index 1d7e1985b..c0209c52f 100644 --- a/packages/flame/test/game/projections_test.dart +++ b/packages/flame/test/game/projections_test.dart @@ -123,9 +123,11 @@ void main() { // that means that the center would be -100, -100 if the zoom was 1 // meaning the topLeft will be -105, -105 (regardless of zoom) - expect(game.unprojectVector(Vector2.zero()), Vector2.all(-105)); - // and with 2x zoom the center will actually be -95, -95 - expect(game.unprojectVector(Vector2.all(5)), Vector2.all(-102.5)); + // but since the offset is set to center, topLeft will be -102.5, -102.5 + expect(game.unprojectVector(Vector2.zero()), Vector2.all(-102.5)); + // and with 2x zoom the center will actually be -100, -100 since the + // relative offset is set to center. + expect(game.unprojectVector(Vector2.all(5)), Vector2.all(-100)); // TODO(luan) we might want to change the behaviour so that the zoom // is applied w.r.t. the relativeOffset and not topLeft