diff --git a/doc/flame/components.md b/doc/flame/components.md index 551031f13a6..fa06354df9b 100644 --- a/doc/flame/components.md +++ b/doc/flame/components.md @@ -375,12 +375,48 @@ Right/East| pi/2 | 90 ### Anchor +```{flutter-app} +:sources: ../flame/examples +:page: anchor +:show: widget code infobox +This example shows effect of changing `anchor` point of parent (red) and child (blue) +components. Tap on them to cycle through the anchor points. Note that the local +position of the child component is (0, 0) at all times. +``` + The `anchor` is where on the component that the position and rotation should be defined from (the default is `Anchor.topLeft`). So if you have the anchor set as `Anchor.center` the component's position on the screen will be in the center of the component and if an `angle` is applied, it is rotated around the anchor, so in this case around the center of the component. You can think of it as the point within the component by which Flame "grabs" it. +When `position` or `absolutePosition` of a component is queried, the returned coordinates are that of +the `anchor` of the component. In case if you want to find the position of a specific anchor point +of a component which is not actually the `anchor` of that component, you can use the `positionOfAnchor` +and `absolutePositionOfAnchor` method. + +```dart +final comp = PositionComponent( + size: Vector2.all(20), + anchor: Anchor.center, +); + +// Returns (0,0) +final p1 = component.position; + +// Returns (10, 10) +final p2 = component.positionOfAnchor(Anchor.bottomRight); +``` + +A common pitfall when using `anchor` is confusing it for as being the attachment point for children +components. For example, setting `anchor` to `Anchor.center` for a parent component does not mean +that the children components will be placed w.r.t the center of parent. + +```{note} +Local origin for a child component is always the top-left corner of its parent component, +irrespective of their `anchor` values. +``` + ### PositionComponent children diff --git a/doc/flame/examples/lib/anchor.dart b/doc/flame/examples/lib/anchor.dart new file mode 100644 index 00000000000..ac813941ae7 --- /dev/null +++ b/doc/flame/examples/lib/anchor.dart @@ -0,0 +1,69 @@ +import 'dart:async'; + +import 'package:flame/components.dart'; +import 'package:flame/events.dart'; +import 'package:flame/game.dart'; +import 'package:flame/palette.dart'; + +class AnchorGame extends FlameGame { + final _parentAnchorText = TextComponent(position: Vector2.all(5)); + final _childAnchorText = TextComponent(position: Vector2(5, 30)); + + late _AnchoredRectangle _redComponent; + late _AnchoredRectangle _blueComponent; + + @override + Future onLoad() async { + _redComponent = _AnchoredRectangle( + size: size / 4, + position: size / 2, + paint: BasicPalette.red.paint(), + ); + + _blueComponent = _AnchoredRectangle( + size: size / 8, + paint: BasicPalette.blue.paint(), + ); + + await _redComponent.addAll([ + _blueComponent, + CircleComponent(radius: 2, anchor: Anchor.center), + ]); + + await addAll([ + _redComponent, + _parentAnchorText, + _childAnchorText, + CircleComponent( + radius: 4, + position: size / 2, + anchor: Anchor.center, + ) + ]); + } + + @override + void update(double dt) { + _parentAnchorText.text = 'Parent: ${_redComponent.anchor}'; + _childAnchorText.text = 'Child: ${_blueComponent.anchor}'; + super.update(dt); + } +} + +class _AnchoredRectangle extends RectangleComponent with TapCallbacks { + _AnchoredRectangle({ + super.position, + super.size, + super.paint, + }); + + @override + void onTapDown(TapDownEvent event) { + var index = Anchor.values.indexOf(anchor) + 1; + if (index == Anchor.values.length) { + index = 0; + } + anchor = Anchor.values.elementAt(index); + super.onTapDown(event); + } +} diff --git a/doc/flame/examples/lib/main.dart b/doc/flame/examples/lib/main.dart index c987a44e239..fbb646c7a32 100644 --- a/doc/flame/examples/lib/main.dart +++ b/doc/flame/examples/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:html'; // ignore: avoid_web_libraries_in_flutter +import 'package:doc_flame_examples/anchor.dart'; import 'package:doc_flame_examples/anchor_by_effect.dart'; import 'package:doc_flame_examples/anchor_to_effect.dart'; import 'package:doc_flame_examples/collision_detection.dart'; @@ -73,6 +74,7 @@ void main() { 'remove_effect': RemoveEffectGame.new, 'color_effect': ColorEffectExample.new, 'time_scale': TimeScaleGame.new, + 'anchor': AnchorGame.new, }; final game = routes[page]?.call(); if (game != null) {