Rename isHud to respectCamera (#1159)

* Camera as an internal component

* Correct respectCamera

* Added changelog entry

* Add super call that will later be removed

* Rename callRender -> callOwnRender

* Don't call render when root

* Remove callOwnRender

* Skip CameraWrapper in own renderTree

* Too complex to have camera as component

* Revert test

* Update docs for respectCamera

* Move down field

* Fix changelog
This commit is contained in:
Lukas Klingsbo
2021-12-04 14:41:51 +01:00
committed by GitHub
parent 0835741e63
commit 4b40479563
11 changed files with 58 additions and 37 deletions

View File

@ -24,8 +24,9 @@ component before the next update loop. It will then no longer be rendered or upd
`game.remove(Component c)` and `component.removeFromParent()` also can be used to remove components
from its parent.
The `isHUD` variable can be overridden or set to true (defaults to `false`) to make the `FlameGame`
ignore the `camera` for this element, making it static in relation to the screen that is.
The `respectCamera` variable can be overridden or set to `false` (defaults to `true`) to make the
`FlameGame` ignore the `camera` for this component, making it static in relation to the viewport
that is.
Do note that this currently only works if the component is added directly to the root `FlameGame`.
The `onRemove` method can be overridden to run code before the component is removed from the game,

View File

@ -111,7 +111,7 @@ you handle this through the `button` component.
As the name suggests this button is a hud by default, which means that it will be static on your
screen even if the camera for the game moves around. You can also use this component as a non-hud by
setting `hudButtonComponent.isHud = false;`.
setting `hudButtonComponent.respectCamera = true;`.
If you want to act upon the button being pressed (which would be the common thing to do) you can either pass in
a callback function as the `onPressed` argument, or you extend the component and override

View File

@ -163,11 +163,11 @@ class JoystickAdvancedExample extends FlameGame
speedText = TextComponent(
text: 'Speed: 0',
textRenderer: _regular,
)..isHud = true;
)..respectCamera = false;
directionText = TextComponent(
text: 'Direction: idle',
textRenderer: _regular,
)..isHud = true;
)..respectCamera = false;
final speedWithMargin = HudMarginComponent(
margin: const EdgeInsets.only(

View File

@ -7,6 +7,7 @@
- Export new effects system
- Introduce `updateTree` to follow the `renderTree` convention
- Fix `Parallax.load` with different loading times
- `isHud` renamed to `respectCamera`
## [1.0.0-releasecandidate.18]
- Forcing portrait and landscape mode is now supported on web

View File

@ -18,11 +18,15 @@ import 'cache/value_cache.dart';
/// called automatically once the component is added to the component tree in
/// your game (with `game.add`).
class Component with Loadable {
/// Whether this component is a HUD (Heads-up display) object or not.
/// Whether this component should respect the camera or not.
///
/// HUD objects ignore the FlameGame.camera when rendered (so their position
/// coordinates are considered relative to the device screen).
bool isHud = false;
/// Components that have this property set to false will ignore the
/// `FlameGame.camera` when rendered (so their position coordinates are
/// considered relative only to the viewport instead).
///
/// Do note that this currently only works if the component is added directly
/// to the root `FlameGame`.
bool respectCamera = true;
/// Whether this component has been prepared and is ready to be added to the
/// game loop.
@ -153,7 +157,9 @@ class Component with Loadable {
@protected
Vector2 eventPosition(PositionInfo info) {
return isHud ? info.eventPosition.viewportOnly : info.eventPosition.game;
return respectCamera
? info.eventPosition.game
: info.eventPosition.viewportOnly;
}
/// Remove the component from its parent in the next tick.

View File

@ -18,7 +18,7 @@ import '../../../game.dart';
class HudMarginComponent<T extends FlameGame> extends PositionComponent
with HasGameRef<T> {
@override
bool isHud = true;
bool respectCamera = false;
/// Instead of setting a position of the [HudMarginComponent] a margin
/// from the edges of the viewport can be used instead.

View File

@ -38,7 +38,7 @@ import '../projector.dart';
///
/// Note: in the context of the FlameGame, the camera effectively translates
/// the position where components are rendered with relation to the Viewport.
/// Components marked as `isHud = true` are always rendered in screen
/// Components marked as `respectCamera = false` are always rendered in screen
/// coordinates, bypassing the camera altogether.
class Camera extends Projector {
Camera() : _viewport = DefaultViewport() {

View File

@ -1,5 +1,7 @@
import 'dart:ui';
import 'package:meta/meta.dart';
import '../../../components.dart';
import 'camera.dart';
@ -7,6 +9,7 @@ import 'camera.dart';
/// converted into a proper Component in a future release, but until then
/// using it in any code other than the FlameGame class is unsafe and
/// not recommended.
@internal
class CameraWrapper {
// TODO(st-pasha): extend from Component
CameraWrapper(this.camera, this.world);
@ -19,16 +22,14 @@ class CameraWrapper {
}
void render(Canvas canvas) {
// TODO(st-pasha): it would be easier to keep the world and the
// HUD as two separate component trees.
camera.viewport.render(canvas, (_canvas) {
var hasCamera = false; // so we don't apply unecessary transformations
var hasCamera = false; // so we don't apply unnecessary transformations
world.forEach((component) {
if (!component.isHud && !hasCamera) {
if (component.respectCamera && !hasCamera) {
canvas.save();
camera.apply(canvas);
hasCamera = true;
} else if (component.isHud && hasCamera) {
} else if (!component.respectCamera && hasCamera) {
canvas.restore();
hasCamera = false;
}

View File

@ -21,13 +21,11 @@ class FlameGame extends Component with Game {
_cameraWrapper = CameraWrapper(camera ?? Camera(), children);
}
late final CameraWrapper _cameraWrapper;
/// The camera translates the coordinate space after the viewport is applied.
Camera get camera => _cameraWrapper.camera;
// When the Game becomes a Component (#906), this could be added directly
// into the component tree.
late final CameraWrapper _cameraWrapper;
/// This is overwritten to consider the viewport transformation.
///
/// Which means that this is the logical size of the game screen area as
@ -64,7 +62,17 @@ class FlameGame extends Component with Game {
/// interfering with each others rendering.
@override
@mustCallSuper
void render(Canvas canvas) => _cameraWrapper.render(canvas);
void render(Canvas canvas) {
if (parent == null) {
renderTree(canvas);
}
}
@override
void renderTree(Canvas canvas) {
// Don't call super.renderTree, since the tree is rendered by the camera
_cameraWrapper.render(canvas);
}
/// This updates every component in the tree.
///
@ -75,10 +83,11 @@ class FlameGame extends Component with Game {
@override
@mustCallSuper
void update(double dt) {
super.update(dt);
_cameraWrapper.update(dt);
if (parent == null) {
super.updateTree(dt, callOwnUpdate: false);
}
_cameraWrapper.update(dt);
}
/// This passes the new size along to every component in the tree via their

View File

@ -116,7 +116,7 @@ mixin Game on Loadable {
onAttach();
}
/// Called when the game has been attached. This can be overriden
/// Called when the game has been attached. This can be overridden
/// to add logic that requires the game to already be attached
/// to the widget tree.
void onAttach() {}
@ -131,7 +131,7 @@ mixin Game on Loadable {
}
/// Called after the game has left the widget tree.
/// This can be overriden to add logic that requires the game
/// This can be overridden to add logic that requires the game
/// not be on the flutter widget tree anymore.
void onDetach() {}
@ -157,12 +157,14 @@ mixin Game on Loadable {
return _gameRenderBox!.localToGlobal(point.toOffset()).toVector2();
}
/// This is the projector used by non-isHUD components.
/// This can be overriden on your [Game] implementation.
/// This is the projector used by all components that respect the camera
/// (`respectCamera = true`).
/// This can be overridden on your [Game] implementation.
Projector projector = IdentityProjector();
/// This is the projector used by isHUD components.
/// This can be overriden on your [Game] implementation.
/// This is the projector used by components that don't respect the camera
/// (`respectCamera = false`).
/// This can be overridden on your [Game] implementation.
Projector viewportProjector = IdentityProjector();
/// Utility method to load and cache the image for a sprite based on its

View File

@ -11,9 +11,10 @@ import 'package:flutter_test/flutter_test.dart';
class _MyComponent extends Component {
@override
bool isHud;
bool respectCamera;
_MyComponent(int priority, {this.isHud = false}) : super(priority: priority);
_MyComponent(int priority, {this.respectCamera = true})
: super(priority: priority);
@override
void render(Canvas canvas) {
@ -54,9 +55,9 @@ void main() {
'only HUD components',
(game) async {
await game.ensureAddAll([
_MyComponent(4, isHud: true),
_MyComponent(1, isHud: true),
_MyComponent(2, isHud: true),
_MyComponent(4, respectCamera: false),
_MyComponent(1, respectCamera: false),
_MyComponent(2, respectCamera: false),
]);
final canvas = MockCanvas();
game.camera.snapTo(Vector2(12.0, 18.0));
@ -78,9 +79,9 @@ void main() {
await game.ensureAddAll([
_MyComponent(4),
_MyComponent(1),
_MyComponent(2, isHud: true),
_MyComponent(5, isHud: true),
_MyComponent(3, isHud: true),
_MyComponent(2, respectCamera: false),
_MyComponent(5, respectCamera: false),
_MyComponent(3, respectCamera: false),
_MyComponent(0),
]);