Make game.size take zoom into consideration (#836)

* Make game.size take zoom into consideration

* Fix formatting

* Fix formatting

* Apply suggestions from code review

Co-authored-by: Jochum van der Ploeg <jochum@vdploeg.net>

* Move _sizeBuffer

* Apply suggestions from code review

Co-authored-by: Jochum van der Ploeg <jochum@vdploeg.net>

* Update list from wolfenrains suggestion

* Fix formatting

* Update CHANGELOG.md

Co-authored-by: Jochum van der Ploeg <jochum@vdploeg.net>
This commit is contained in:
Lukas Klingsbo
2021-06-09 16:39:45 +02:00
committed by GitHub
parent 607c4bbdcd
commit a7fd05f2b5
7 changed files with 39 additions and 30 deletions

View File

@ -29,18 +29,18 @@ your needs):
When using `BaseGame`, the operations performed by the viewport are done automatically to every 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 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 the size as seen through the viewport together with the zoom of the camera. If for some reason you
logical pixel size, you can use `canvasSize`. For a more in depth description on what each Viewport need to access the original real logical pixel size, you can use `canvasSize`. For a more in depth
does and how it operates, check the documentation on its class. description on what each `Viewport` does and how it operates, check the documentation on its class.
## Camera ## Camera
Unlike the Viewport, the Camera is a more dynamic Canvas transformation that is normally dependent Unlike the `Viewport`, the `Camera` is a more dynamic `Canvas` transformation that is normally
on: dependent on:
* world coordinates that do not match screen coordinates 1:1 * 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) * Centering or following the player around the game world (if the world is bigger than the screen).
* user controlled zooming in and out * User controlled zooming in and out.
There is only one Camera implementation but it allows for many different configurations. Again, you 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`. can use it standalone on your `Game` but it's already included and wired into `BaseGame`.

View File

@ -228,9 +228,7 @@ class MultipleShapes extends BaseGame
lastToAdd = createRandomCollidable(lastToAdd, screenCollidable); lastToAdd = createRandomCollidable(lastToAdd, screenCollidable);
final lastBottomRight = final lastBottomRight =
lastToAdd.toAbsoluteRect().bottomRight.toVector2(); lastToAdd.toAbsoluteRect().bottomRight.toVector2();
final screenSize = size / camera.zoom; if (lastBottomRight.x < size.x && lastBottomRight.y < size.y) {
if (lastBottomRight.x < screenSize.x &&
lastBottomRight.y < screenSize.y) {
add(lastToAdd); add(lastToAdd);
totalAdded++; totalAdded++;
} else { } else {

View File

@ -18,6 +18,8 @@
- Refactor TextBoxComponent - Refactor TextBoxComponent
- Fix bugs with TextBoxComponent - Fix bugs with TextBoxComponent
- Improve error message for composed components - 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 - Add `anchor` for `ShapeComponent` constructor
- Fix rendering of polygons in `ShapeComponent` - Fix rendering of polygons in `ShapeComponent`
- Add `SpriteAnimation` support to parallax - Add `SpriteAnimation` support to parallax

View File

@ -26,28 +26,18 @@ class ScreenCollidable extends PositionComponent
@override @override
CollidableType collidableType = CollidableType.passive; CollidableType collidableType = CollidableType.passive;
final Vector2 _effectiveSize = Vector2.zero();
double _zoom = 1.0;
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
_updateSize(); size = gameRef.size;
addShape(HitboxRectangle()); addShape(HitboxRectangle());
} }
final _zeroVector = Vector2.zero();
@override @override
void update(double dt) { void update(double dt) {
super.update(dt); super.update(dt);
_updateSize(); position = gameRef.camera.unprojectVector(_zeroVector);
} size = gameRef.size;
void _updateSize() {
if (_effectiveSize != gameRef.viewport.effectiveSize ||
_zoom != gameRef.camera.zoom) {
_effectiveSize.setFrom(gameRef.viewport.effectiveSize);
_zoom = gameRef.camera.zoom;
size = _effectiveSize / _zoom;
}
} }
} }

View File

@ -65,15 +65,20 @@ class BaseGame extends Game with FPSCounter {
late Projector _combinedProjector; late Projector _combinedProjector;
final Vector2 _sizeBuffer = Vector2.zero();
/// This is overwritten to consider the viewport transformation. /// This is overwritten to consider the viewport transformation.
/// ///
/// Which means that this is the logical size of the game screen area as /// 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]. /// This does not match the Flutter widget size; for that see [canvasSize].
@override @override
Vector2 get size { Vector2 get size {
assertHasLayout(); assertHasLayout();
return viewport.effectiveSize; return _sizeBuffer
..setFrom(viewport.effectiveSize)
..scale(1 / camera.zoom);
} }
/// This is the original Flutter widget size, without any transformation. /// This is the original Flutter widget size, without any transformation.

View File

@ -203,6 +203,18 @@ class Camera extends Projector {
return worldCoordinates * zoom; 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 /// This is the (current) absolute target of the camera, i.e., the
/// coordinate that should be on the top left, regardless of relative /// coordinate that should be on the top left, regardless of relative
/// offset, world boundaries or shake. /// offset, world boundaries or shake.

View File

@ -123,9 +123,11 @@ void main() {
// that means that the center would be -100, -100 if the zoom was 1 // that means that the center would be -100, -100 if the zoom was 1
// meaning the topLeft will be -105, -105 (regardless of zoom) // meaning the topLeft will be -105, -105 (regardless of zoom)
expect(game.unprojectVector(Vector2.zero()), Vector2.all(-105)); // but since the offset is set to center, topLeft will be -102.5, -102.5
// and with 2x zoom the center will actually be -95, -95 expect(game.unprojectVector(Vector2.zero()), Vector2.all(-102.5));
expect(game.unprojectVector(Vector2.all(5)), 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 // TODO(luan) we might want to change the behaviour so that the zoom
// is applied w.r.t. the relativeOffset and not topLeft // is applied w.r.t. the relativeOffset and not topLeft