diff --git a/doc/components.md b/doc/components.md index f15125806..43bc61186 100644 --- a/doc/components.md +++ b/doc/components.md @@ -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, diff --git a/doc/other-inputs.md b/doc/other-inputs.md index bd8aa2d7b..4690c887d 100644 --- a/doc/other-inputs.md +++ b/doc/other-inputs.md @@ -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 diff --git a/examples/lib/stories/input/joystick_advanced_example.dart b/examples/lib/stories/input/joystick_advanced_example.dart index f668836f9..fb8dc4ce9 100644 --- a/examples/lib/stories/input/joystick_advanced_example.dart +++ b/examples/lib/stories/input/joystick_advanced_example.dart @@ -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( diff --git a/packages/flame/CHANGELOG.md b/packages/flame/CHANGELOG.md index 5fcbc2a02..98701ea2d 100644 --- a/packages/flame/CHANGELOG.md +++ b/packages/flame/CHANGELOG.md @@ -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 diff --git a/packages/flame/lib/src/components/component.dart b/packages/flame/lib/src/components/component.dart index 7cf6c251c..d3bf14877 100644 --- a/packages/flame/lib/src/components/component.dart +++ b/packages/flame/lib/src/components/component.dart @@ -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. diff --git a/packages/flame/lib/src/components/input/hud_margin_component.dart b/packages/flame/lib/src/components/input/hud_margin_component.dart index a7be9714c..578ea30bb 100644 --- a/packages/flame/lib/src/components/input/hud_margin_component.dart +++ b/packages/flame/lib/src/components/input/hud_margin_component.dart @@ -18,7 +18,7 @@ import '../../../game.dart'; class HudMarginComponent extends PositionComponent with HasGameRef { @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. diff --git a/packages/flame/lib/src/game/camera/camera.dart b/packages/flame/lib/src/game/camera/camera.dart index a94a58fbf..2817d59a9 100644 --- a/packages/flame/lib/src/game/camera/camera.dart +++ b/packages/flame/lib/src/game/camera/camera.dart @@ -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() { diff --git a/packages/flame/lib/src/game/camera/camera_wrapper.dart b/packages/flame/lib/src/game/camera/camera_wrapper.dart index 1e415879e..4733f6dca 100644 --- a/packages/flame/lib/src/game/camera/camera_wrapper.dart +++ b/packages/flame/lib/src/game/camera/camera_wrapper.dart @@ -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; } diff --git a/packages/flame/lib/src/game/flame_game.dart b/packages/flame/lib/src/game/flame_game.dart index bc35faa83..62a692332 100644 --- a/packages/flame/lib/src/game/flame_game.dart +++ b/packages/flame/lib/src/game/flame_game.dart @@ -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 diff --git a/packages/flame/lib/src/game/mixins/game.dart b/packages/flame/lib/src/game/mixins/game.dart index 0c298b7d0..73a0e9f42 100644 --- a/packages/flame/lib/src/game/mixins/game.dart +++ b/packages/flame/lib/src/game/mixins/game.dart @@ -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 diff --git a/packages/flame/test/game/component_rendering_test.dart b/packages/flame/test/game/component_rendering_test.dart index 283bf24a6..f2b0d98ae 100644 --- a/packages/flame/test/game/component_rendering_test.dart +++ b/packages/flame/test/game/component_rendering_test.dart @@ -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), ]);