Skip to content

Commit

Permalink
The viewfinder should pre-scale
Browse files Browse the repository at this point in the history
  • Loading branch information
spydon committed Oct 5, 2023
1 parent 97fe67f commit 635a1f0
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 27 deletions.
59 changes: 40 additions & 19 deletions doc/flame/camera_component.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,14 @@ With this mindset, we can now understand how camera-as-a-component works.

First, there is the [](#world) class, which contains all components that are
inside your game world. The `World` component can be mounted anywhere, for
example at the root of your game class.
example at the root of your game class, like the built-in `World` is.

Then, a [](#cameracomponent) class that "looks at" the `World`. The
`CameraComponent` has a `Viewport` and a `Viewfinder` inside, allowing both the
flexibility of rendering the world at any place on the screen, and also control
the viewing location and angle.

If you add children to the `Viewport` they will appear as static HUDs in
front of the world. If you on the other hand add children to the `Viewfinder`
they will appear non-statically, with the same camera transformations as are
applied to the children in the `World`. The `Viewfinder`'s children will
appear in front of the world, but behind the viewport.

To add static components behind the world you can add them to the `backdrop`
component, or replace the `backdrop` component. This is for example useful if
you want to have a static `ParallaxComponent` beneath a world that you can move
around it.
Then, a [](#cameracomponent) class that "looks at" the [](#world). The
`CameraComponent` has a [](#viewport) and a [](#viewfinder) inside, allowing
both the flexibility of rendering the world at any place on the screen, and
also control the viewing location and angle. The `CameraComponent` also
contains a [](#backdrop) component which is statically rendered below the
world.


## World
Expand Down Expand Up @@ -160,6 +151,12 @@ The following viewports are available:
- `CircularViewport` -- a viewport in the shape of a circle, fixed size.


If you add children to the `Viewport` they will also appear as static HUDs in
front of the world, but they won't have the `Viewfinder`'s pre-scaling applied
to it, which is used to size the HUDs according to the fixed resolution that
has been specified in `CameraComponent.withFixedResolution`, if any.


## Viewfinder

This part of the camera is responsible for knowing which location in the
Expand All @@ -173,9 +170,33 @@ main character who is displayed not in the center of the screen but closer to
the lower-left corner. This off-center position would be the "logical center"
of the camera, controlled by the viewfinder's `anchor`.

Components added to the `Viewfinder` as children will be rendered as if they
were part of the world (but on top). It is more useful to add behavioral
components to the viewfinder, for example [](effects.md) or other controllers.
If you on add children to the `Viewfinder` they will appear will
appear in front of the world, but behind the viewport and with any scaling that
needs to be done to fulfill the fixed resolution specified in
`CameraComponent.withFixedResolution`, if any.

You can also add behavioral components as children to the viewfinder, for
example [](effects.md) or other controllers. If you for example would add a
`ScaleEffect` you would be able to achieve a smooth zoom in your game.


## Backdrop

To add static components behind the world you can add them to the `backdrop`
component, or replace the `backdrop` component. This is for example useful if
you want to have a static `ParallaxComponent` beneath a world that you can move
around it.

Example:
```dart
camera.backdrop.add(MyStaticBackground());
```

or

```dart
camera.backdrop = MyStaticBackground();
```


## Camera controls
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'dart:ui';
import 'dart:ui' hide TextStyle;

import 'package:flame/components.dart';
import 'package:flame/events.dart';
import 'package:flame/game.dart';
import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flame/text.dart';

class FixedResolutionExample extends FlameGame
with ScrollDetector, ScaleDetector {
Expand All @@ -17,15 +18,38 @@ class FixedResolutionExample extends FlameGame
If you tap once you will set the zoom to 2 and if you tap again it goes back
to 1, so that you can test how it works with a zoom level.
''';
Vector2 viewportResolution;

FixedResolutionExample({required Vector2 viewportResolution})
FixedResolutionExample({required this.viewportResolution})
: super(
camera: CameraComponent.withFixedResolution(
width: viewportResolution.x,
height: viewportResolution.y,
),
world: FixedResolutionWorld(),
);

@override
Future<void> onLoad() async {
final textRenderer = TextPaint(
style: TextStyle(fontSize: 25, color: BasicPalette.black.color),
);
camera.viewport.add(
TextComponent(
text: 'Viewport component\n(always same size)',
position: Vector2.all(10),
textRenderer: textRenderer,
),
);
camera.viewfinder.add(
TextComponent(
text: 'Viewfinder component\n(scales with fixed resolution)',
position: viewportResolution - Vector2.all(10),
textRenderer: textRenderer,
anchor: Anchor.bottomRight,
),
);
}
}

class FixedResolutionWorld extends World with HasGameReference, TapCallbacks {
Expand Down
4 changes: 3 additions & 1 deletion packages/flame/lib/camera.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ export 'src/camera/behaviors/bounded_position_behavior.dart'
show BoundedPositionBehavior;
export 'src/camera/behaviors/follow_behavior.dart' show FollowBehavior;
export 'src/camera/camera_component.dart' show CameraComponent;
export 'src/camera/viewfinder.dart' show Viewfinder;
export 'src/camera/viewfinders/fixed_resolution_viewfinder.dart'
show FixedResolutionViewfinder;
export 'src/camera/viewfinders/viewfinder.dart' show Viewfinder;
export 'src/camera/viewport.dart' show Viewport;
export 'src/camera/viewports/circular_viewport.dart' show CircularViewport;
export 'src/camera/viewports/fixed_aspect_ratio_viewport.dart'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:flame/src/camera/viewfinder.dart';
import 'package:flame/src/camera/viewfinders/viewfinder.dart';
import 'package:flame/src/camera/viewport.dart';
import 'package:flame/src/components/core/component.dart';
import 'package:flame/src/components/position_component.dart';
Expand Down
12 changes: 8 additions & 4 deletions packages/flame/lib/src/camera/camera_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import 'dart:ui';

import 'package:flame/src/camera/behaviors/bounded_position_behavior.dart';
import 'package:flame/src/camera/behaviors/follow_behavior.dart';
import 'package:flame/src/camera/viewfinder.dart';
import 'package:flame/src/camera/viewfinders/fixed_resolution_viewfinder.dart';
import 'package:flame/src/camera/viewfinders/viewfinder.dart';
import 'package:flame/src/camera/viewport.dart';
import 'package:flame/src/camera/viewports/fixed_aspect_ratio_viewport.dart';
import 'package:flame/src/camera/viewports/max_viewport.dart';
Expand Down Expand Up @@ -186,14 +186,18 @@ class CameraComponent extends Component {
currentCameras.add(this);
canvas.transform(viewfinder.transform.transformMatrix.storage);
world!.renderFromCamera(canvas);
// Render the viewfinder elements, which will be in front of the world,
// but with the same base transform applied to them.
viewfinder.renderTree(canvas);
} finally {
currentCameras.removeLast();
}
canvas.restore();
}
canvas.save();
canvas.scale(viewfinder.preScale);
// Render the viewfinder elements in front of the world and only with the
// pre-scaling applied, so that the viewfinder components are resized
// according to the fixed resolution that has been set.
viewfinder.renderTree(canvas);
canvas.restore();
// Render the viewport elements, which will be in front of the world.
viewport.renderTree(canvas);
canvas.restore();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class FixedResolutionViewfinder extends Viewfinder {

/// The extra scaling that is being done to achieve the fixed resolution
/// requested by the user.
@override
double get preScale => _preScale;
double _preScale = 1.0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:math';
import 'package:flame/extensions.dart';
import 'package:flame/src/anchor.dart';
import 'package:flame/src/camera/camera_component.dart';
import 'package:flame/src/camera/viewfinders/fixed_resolution_viewfinder.dart';
import 'package:flame/src/components/core/component.dart';
import 'package:flame/src/effects/provider_interfaces.dart';
import 'package:flame/src/game/transform2d.dart';
Expand All @@ -25,6 +26,11 @@ class Viewfinder extends Component
@internal
Transform2D get transform => _transform;

/// The [preScale] is used if the viewfinder should do some scaling before the
/// [zoom] is applied, for example if you are using something like the
/// [FixedResolutionViewfinder].
double get preScale => 1.0;

/// The game coordinates of a point that is to be positioned at the center
/// of the viewport.
@override
Expand Down

0 comments on commit 635a1f0

Please sign in to comment.