Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added AlignComponent layout component #2350

Merged
merged 27 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
53f4356
AlignComponent
st-pasha Feb 19, 2023
3aa006f
expand example
st-pasha Feb 19, 2023
890d05a
docs
st-pasha Feb 19, 2023
9714ceb
added tests
st-pasha Feb 19, 2023
94c2d2d
Merge branch 'main' into ps.layout-components
st-pasha Feb 19, 2023
73c15b1
format
st-pasha Feb 19, 2023
fa19f88
use children in the example
st-pasha Feb 19, 2023
1805cb2
a
st-pasha Feb 19, 2023
d2e2e23
Merge branch 'main' into ps.layout-components
st-pasha Feb 19, 2023
560dd4c
capitalize Flutter
st-pasha Feb 19, 2023
a624018
Merge branch 'main' into ps.layout-components
st-pasha Feb 22, 2023
4037bcb
Merge branch 'main' into ps.layout-components
st-pasha Mar 1, 2023
2e92194
game-in-game example
st-pasha Mar 1, 2023
d838f52
trailing commas
st-pasha Mar 1, 2023
d7aa2c1
Added ReadonlySizeProvider interface
st-pasha Mar 1, 2023
6fe90f7
fix mounting
st-pasha Mar 1, 2023
b62481c
melos bs
Feb 27, 2023
f4a6963
docs: Clarify required flutter channel on CONTRIBUTING guidelines (#2…
luanpotter Mar 1, 2023
34a1fd5
refactor: Remove unused variable "tapTimes" from multi_touch_tap_dete…
luanpotter Mar 1, 2023
62e45c7
test: Add test for the intersections test helper method (#2378)
luanpotter Mar 1, 2023
da5ac3b
refactor: Use variable name on toString in component_test.dart (#2377)
luanpotter Mar 1, 2023
4e964af
refactor: Remove unused event "ScoreEventCleared" from flame_block ex…
luanpotter Mar 1, 2023
002383d
chore: Standardize and unify SDK versions across packages (#2374)
luanpotter Mar 1, 2023
10aa5e9
docs: Remove outdated index of contents from packages/README (#2375)
luanpotter Mar 1, 2023
775de23
test: Add test for two subsequent state changes to flame_multi_bloc_p…
luanpotter Mar 2, 2023
b4b8b7c
docs: Break out overlays docs (#2384)
st-pasha Mar 2, 2023
1690d0c
Merge branch 'main' into ps.layout-components
st-pasha Mar 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/flame/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ Every `Component` has a few methods that you can optionally implement, which are
The `onGameResize` method is called whenever the screen is resized, and also when this component
gets added into the component tree, before the `onMount`.

The `onParentResize` method is similar: it is also called when the component is mounted into the
component tree, and also whenever the parent of the current component changes its size.

The `onRemove` method can be overridden to run code before the component is removed from the game,
it is only run once even if the component is removed both by using the parents remove method and
the `Component` remove method.
Expand Down
2 changes: 2 additions & 0 deletions doc/flame/flame.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- [Camera Component](camera_component.md)
- [Inputs](inputs/inputs.md)
- [Rendering](rendering/rendering.md)
- [Layout](layout/layout.md)
- [Other](other/other.md)

```{toctree}
Expand All @@ -29,5 +30,6 @@ Camera & Viewport <camera_and_viewport.md>
Camera Component <camera_component.md>
Inputs <inputs/inputs.md>
Rendering <rendering/rendering.md>
Layout <layout/layout.md>
Other <other/other.md>
```
10 changes: 10 additions & 0 deletions doc/flame/layout/align_component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# AlignComponent

```{dartdoc}
:package: flame
:symbol: AlignComponent
:file: src/layout/align_component.dart

[Align]: https://api.flutter.dev/flutter/widgets/Align-class.html
[Alignment]: https://api.flutter.dev/flutter/painting/Alignment-class.html
```
7 changes: 7 additions & 0 deletions doc/flame/layout/layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Layout

```{toctree}
:hidden:

AlignComponent <align_component.md>
```
2 changes: 2 additions & 0 deletions examples/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:examples/stories/effects/effects.dart';
import 'package:examples/stories/experimental/experimental.dart';
import 'package:examples/stories/games/games.dart';
import 'package:examples/stories/input/input.dart';
import 'package:examples/stories/layout/layout.dart';
import 'package:examples/stories/parallax/parallax.dart';
import 'package:examples/stories/rendering/rendering.dart';
import 'package:examples/stories/sprites/sprites.dart';
Expand Down Expand Up @@ -39,6 +40,7 @@ void main() {
addEffectsStories(dashbook);
addExperimentalStories(dashbook);
addInputStories(dashbook);
addLayoutStories(dashbook);
addParallaxStories(dashbook);
addRenderingStories(dashbook);
addTiledStories(dashbook);
Expand Down
2 changes: 1 addition & 1 deletion examples/lib/stories/components/game_in_game_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class GameChangeTimer extends TimerComponent
void onTick() {
final child = gameRef.draggablesGame.square;
final newParent = child.parent == gameRef.draggablesGame
? gameRef.composedGame.parentSquare
? gameRef.composedGame.parentSquare as Component
: gameRef.draggablesGame;
child.changeParent(newParent);
}
Expand Down
100 changes: 100 additions & 0 deletions examples/lib/stories/layout/align_component.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/game.dart';
import 'package:flame/layout.dart';

class AlignComponentExample extends FlameGame {
static const String description = '''
In this example the AlignComponent is used to arrange the circles
so that there is one in the middle and 8 more surrounding it in
the shape of a diamond.

The arrangement will remain intact if you change the window size.
''';

@override
void onLoad() {
addAll([
AlignComponent(
child: CircleComponent(
radius: 40,
children: [
SizeEffect.by(
Vector2.all(25),
EffectController(
infinite: true,
duration: 0.75,
reverseDuration: 0.5,
),
),
AlignComponent(
alignment: Anchor.topCenter,
child: CircleComponent(
radius: 10,
anchor: Anchor.bottomCenter,
),
keepChildAnchor: true,
),
AlignComponent(
alignment: Anchor.bottomCenter,
child: CircleComponent(
radius: 10,
anchor: Anchor.topCenter,
),
keepChildAnchor: true,
),
AlignComponent(
alignment: Anchor.centerLeft,
child: CircleComponent(
radius: 10,
anchor: Anchor.centerRight,
),
keepChildAnchor: true,
),
AlignComponent(
alignment: Anchor.centerRight,
child: CircleComponent(
radius: 10,
anchor: Anchor.centerLeft,
),
keepChildAnchor: true,
),
],
),
alignment: Anchor.center,
),
AlignComponent(
child: CircleComponent(radius: 30),
alignment: Anchor.topCenter,
),
AlignComponent(
child: CircleComponent(radius: 30),
alignment: Anchor.bottomCenter,
),
AlignComponent(
child: CircleComponent(radius: 30),
alignment: Anchor.centerLeft,
),
AlignComponent(
child: CircleComponent(radius: 30),
alignment: Anchor.centerRight,
),
AlignComponent(
child: CircleComponent(radius: 10),
alignment: const Anchor(0.25, 0.25),
),
AlignComponent(
child: CircleComponent(radius: 10),
alignment: const Anchor(0.25, 0.75),
),
AlignComponent(
child: CircleComponent(radius: 10),
alignment: const Anchor(0.75, 0.25),
),
AlignComponent(
child: CircleComponent(radius: 10),
alignment: const Anchor(0.75, 0.75),
),
]);
}
}
13 changes: 13 additions & 0 deletions examples/lib/stories/layout/layout.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:dashbook/dashbook.dart';
import 'package:examples/commons/commons.dart';
import 'package:examples/stories/layout/align_component.dart';
import 'package:flame/game.dart';

void addLayoutStories(Dashbook dashbook) {
dashbook.storiesOf('Layout').add(
'AlignComponent',
(_) => GameWidget(game: AlignComponentExample()),
codeLink: baseLink('layout/align_component.dart'),
info: AlignComponentExample.description,
);
}
1 change: 1 addition & 0 deletions packages/flame/lib/layout.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'src/layout/align_component.dart' show AlignComponent;
3 changes: 3 additions & 0 deletions packages/flame/lib/src/camera/viewport.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ abstract class Viewport extends Component
camera.viewfinder.onViewportResize();
}
onViewportResize();
if (hasChildren) {
spydon marked this conversation as resolved.
Show resolved Hide resolved
spydon marked this conversation as resolved.
Show resolved Hide resolved
children.forEach((child) => child.onParentResize(_size));
}
}

/// Reference to the parent camera.
Expand Down
12 changes: 12 additions & 0 deletions packages/flame/lib/src/components/core/component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:flame/src/components/core/component_tree_root.dart';
import 'package:flame/src/components/core/position_type.dart';
import 'package:flame/src/components/mixins/coordinate_transform.dart';
import 'package:flame/src/components/mixins/has_game_ref.dart';
import 'package:flame/src/effects/provider_interfaces.dart';
import 'package:flame/src/game/flame_game.dart';
import 'package:flame/src/game/game.dart';
import 'package:flame/src/gestures/events.dart';
Expand Down Expand Up @@ -474,6 +475,14 @@ class Component {
/// [onMount] call before.
void onRemove() {}

/// Called whenever the parent of this component changes size; and also once
/// before [onMount].
///
/// The component may change its own size or perform layout in response to
/// this call. If the component changes size, then it should call
/// [onParentResize] for all its children.
void onParentResize(Vector2 maxSize) {}

/// This method is called periodically by the game engine to request that your
/// component updates itself.
///
Expand Down Expand Up @@ -830,6 +839,9 @@ class Component {
assert(isLoaded && !isLoading);
_setMountingBit();
onGameResize(_parent!.findGame()!.canvasSize);
if (_parent is ReadonlySizeProvider) {
onParentResize((_parent! as ReadonlySizeProvider).size);
}
if (isRemoved) {
_clearRemovedBit();
} else if (isRemoving) {
Expand Down
11 changes: 10 additions & 1 deletion packages/flame/lib/src/components/position_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class PositionComponent extends Component
AngleProvider,
PositionProvider,
ScaleProvider,
SizeProvider,
CoordinateTransform {
PositionComponent({
Vector2? position,
Expand Down Expand Up @@ -184,8 +185,16 @@ class PositionComponent extends Component
/// This property can be reassigned at runtime, although this is not
/// recommended. Instead, in order to make the [PositionComponent] larger
/// or smaller, change its [scale].
@override
NotifyingVector2 get size => _size;
set size(Vector2 size) => _size.setFrom(size);

@override
set size(Vector2 size) {
_size.setFrom(size);
if (hasChildren) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this check optimize anything? The foreach should also just do a check if it is empty right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasChildren avoids instantiating a ComponentSet object in case it is null.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we do that check inside the .children getter instead?
that way we avoid duplicating this all over & everyone gets the performance boost (even end users)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(can be a separate PR of course)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .children property returns a ComponentSet right now, which means it must create a component set in case it is currently missing. We could change it to return a ComponentSet? instead, but that would be a major breaking change I'm afraid -- so perhaps we could add that to our 2.0 todo list.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha I think 3 people commented about this on this PR, maybe it would be better as nullable indeed, let's discuss that for V2 at least. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a note about this in #1938

children.forEach((child) => child.onParentResize(_size));
}
}

/// The width of the component in local coordinates. Note that the object
/// may visually appear larger or smaller due to application of [scale].
Expand Down
9 changes: 7 additions & 2 deletions packages/flame/lib/src/effects/provider_interfaces.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,14 @@ abstract class AnchorProvider {
set anchor(Anchor value);
}

/// Interface for a component that can be affected by size effects.
abstract class SizeProvider {
/// Interface for a class that has [size] property which can be read but not
/// modified.
abstract class ReadonlySizeProvider {
Vector2 get size;
}

/// Interface for a component that can be affected by size effects.
abstract class SizeProvider extends ReadonlySizeProvider {
set size(Vector2 value);
}

Expand Down
6 changes: 5 additions & 1 deletion packages/flame/lib/src/game/flame_game.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:ui';

import 'package:flame/components.dart';
import 'package:flame/src/components/core/component_tree_root.dart';
import 'package:flame/src/effects/provider_interfaces.dart';
import 'package:flame/src/game/camera/camera.dart';
import 'package:flame/src/game/camera/camera_wrapper.dart';
import 'package:flame/src/game/game.dart';
Expand All @@ -15,7 +16,9 @@ import 'package:meta/meta.dart';
///
/// This is the recommended base class to use for most games made with Flame.
/// It is based on the Flame Component System (also known as FCS).
class FlameGame extends ComponentTreeRoot with Game {
class FlameGame extends ComponentTreeRoot
with Game
implements ReadonlySizeProvider {
FlameGame({
super.children,
Camera? camera,
Expand Down Expand Up @@ -111,6 +114,7 @@ class FlameGame extends ComponentTreeRoot with Game {
// there is no way to explicitly call the [Component]'s implementation,
// we propagate the event to [FlameGame]'s children manually.
handleResize(canvasSize);
children.forEach((child) => child.onParentResize(canvasSize));
}

/// Ensure that all pending tree operations finish.
Expand Down
Loading