Skip to content

Commit

Permalink
feat!: Size effects will now work only on components implementing Siz…
Browse files Browse the repository at this point in the history
…eProvider (#1571)
  • Loading branch information
st-pasha authored Apr 27, 2022
1 parent 0c43078 commit 1bfed57
Show file tree
Hide file tree
Showing 13 changed files with 76 additions and 44 deletions.
11 changes: 7 additions & 4 deletions doc/flame/effects.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,13 @@ final effect = SizeEffect.by(Vector2(20, -50), EffectController(duration: 1));
The size of a `PositionComponent` cannot be negative. If an effect attempts to set the size to a
negative value, the size will be clamped at zero.

Note that for this effect to work, the target component must take its own `size` into account when
rendering, and not all of them do. In addition, changing the size of a component does not propagate
to its children, if it has any. An alternative to `SizeEffect` is the `ScaleEffect`, which works
more generally and scales the children components too.
Note that for this effect to work, the target component must implement the `SizeProvider` interface
and take its `size` into account when rendering. Only few of the built-in components implement this
API, but you can always make your own component work with size effects by adding
`implements SizeEffect` to the class declaration.

An alternative to `SizeEffect` is the `ScaleEffect`, which works more generally and scales both the
target component and its children.


### `SizeEffect.to`
Expand Down
28 changes: 11 additions & 17 deletions examples/lib/stories/effects/size_effect_example.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import 'dart:ui';

import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/game.dart';
import 'package:flame/geometry.dart';
import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flutter/animation.dart';
Expand All @@ -15,35 +15,29 @@ class SizeEffectExample extends FlameGame with TapDetector {
down, depending on its current state.
''';

late RectangleComponent square;
late Component shape;
bool grow = true;

@override
Future<void> onLoad() async {
square = RectangleComponent.square(
size: 100,
shape = CircleComponent(
radius: 100,
position: Vector2.all(200),
paint: BasicPalette.white.paint()..style = PaintingStyle.stroke,
);
final childSquare = RectangleComponent.square(
position: Vector2.all(70),
size: 20,
);
square.add(childSquare);
add(square);
children: [
RectangleComponent.square(position: Vector2.all(70), size: 20),
],
)..addToParent(this);
}

@override
void onTap() {
final s = grow ? 300.0 : 100.0;

grow = !grow;

square.add(
shape.add(
SizeEffect.to(
Vector2.all(s),
Vector2.all(grow ? 300.0 : 100.0),
EffectController(duration: 1.5, curve: Curves.bounceInOut),
),
);
grow = !grow;
}
}
7 changes: 6 additions & 1 deletion packages/flame/lib/effects.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ export 'src/effects/move_effect.dart';
export 'src/effects/move_to_effect.dart';
export 'src/effects/opacity_effect.dart';
export 'src/effects/provider_interfaces.dart'
show AnchorProvider, AngleProvider, PositionProvider, ScaleProvider;
show
AnchorProvider,
AngleProvider,
PositionProvider,
ScaleProvider,
SizeProvider;
export 'src/effects/remove_effect.dart';
export 'src/effects/rotate_effect.dart';
export 'src/effects/scale_effect.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import 'dart:ui';
import 'package:meta/meta.dart';

import '../../components.dart';
import '../effects/provider_interfaces.dart';

export '../nine_tile_box.dart';

/// This class is a thin wrapper on top of [NineTileBox] as a component.
class NineTileBoxComponent extends PositionComponent {
class NineTileBoxComponent extends PositionComponent implements SizeProvider {
NineTileBox? nineTileBox;

/// Takes the [NineTileBox] instance to render a box that can grow and shrink
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import 'dart:ui';
import 'package:meta/meta.dart';

import '../../components.dart';
import '../effects/provider_interfaces.dart';

export '../sprite_animation.dart';

class SpriteAnimationComponent extends PositionComponent with HasPaint {
class SpriteAnimationComponent extends PositionComponent
with HasPaint
implements SizeProvider {
/// The animation used by the component.
SpriteAnimation? animation;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import 'dart:ui';
import 'package:meta/meta.dart';

import '../../components.dart';
import '../effects/provider_interfaces.dart';

export '../sprite_animation.dart';

class SpriteAnimationGroupComponent<T> extends PositionComponent with HasPaint {
class SpriteAnimationGroupComponent<T> extends PositionComponent
with HasPaint
implements SizeProvider {
/// Key with the current playing animation
T? current;

Expand Down
5 changes: 4 additions & 1 deletion packages/flame/lib/src/components/sprite_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:ui';
import 'package:meta/meta.dart';

import '../../components.dart';
import '../effects/provider_interfaces.dart';
import '../extensions/image.dart';

export '../sprite.dart';
Expand All @@ -12,7 +13,9 @@ export '../sprite.dart';
/// angle.
///
/// This a commonly used subclass of [Component].
class SpriteComponent extends PositionComponent with HasPaint {
class SpriteComponent extends PositionComponent
with HasPaint
implements SizeProvider {
/// The [sprite] to be rendered by this component.
Sprite? sprite;

Expand Down
8 changes: 5 additions & 3 deletions packages/flame/lib/src/components/sprite_group_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import 'dart:ui';
import 'package:meta/meta.dart';

import '../../components.dart';

import '../effects/provider_interfaces.dart';
export '../sprite_animation.dart';

/// A [PositionComponent] that can have mutiple [Sprite]s and render
/// A [PositionComponent] that can have multiple [Sprite]s and render
/// the one mapped with the [current] key.
class SpriteGroupComponent<T> extends PositionComponent with HasPaint {
class SpriteGroupComponent<T> extends PositionComponent
with HasPaint
implements SizeProvider {
/// Key with the current playing animation
T? current;

Expand Down
6 changes: 6 additions & 0 deletions packages/flame/lib/src/effects/provider_interfaces.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,9 @@ abstract class AnchorProvider {
Anchor get anchor;
set anchor(Anchor value);
}

/// Interface for a component that can be affected by size effects.
abstract class SizeProvider {
Vector2 get size;
set size(Vector2 value);
}
17 changes: 12 additions & 5 deletions packages/flame/lib/src/effects/size_effect.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import '../../components.dart';
import 'component_effect.dart';
import 'controllers/effect_controller.dart';
import 'effect.dart';
import 'effect_target.dart';
import 'provider_interfaces.dart';

/// Change the size of a component over time.
///
/// This effect applies incremental changes to the component's size, and
/// requires that any other effect or update logic applied to the same component
/// also used incremental updates.
class SizeEffect extends ComponentEffect<PositionComponent> {
class SizeEffect extends Effect with EffectTarget<SizeProvider> {
/// This constructor will create an effect that sets the size in relation to
/// the [PositionComponent]'s current size, for example if the [offset] is
/// set to `Vector2(10, -10)` and the size of the affected component is
/// `Vector2(100, 100)` at the start of the affected the effect will peak when
/// the size is `Vector2(110, 90)`, if there is nothing else affecting the
/// size at the same time.
SizeEffect.by(Vector2 offset, EffectController controller)
: _offset = offset.clone(),
super(controller);
SizeEffect.by(
Vector2 offset,
EffectController controller, {
SizeProvider? target,
}) : _offset = offset.clone(),
super(controller) {
this.target = target;
}

/// This constructor will create an effect that sets the size to the absolute
/// size that is defined by [targetSize].
Expand Down
5 changes: 4 additions & 1 deletion packages/flame/lib/src/experimental/viewport.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import 'camera_component.dart';
/// There are several implementations of [Viewport], which differ by their
/// shape, and also by their behavior in response to changes to the canvas size.
/// Users may also create their own implementations.
abstract class Viewport extends Component implements PositionProvider {
abstract class Viewport extends Component
implements PositionProvider, SizeProvider {
Viewport({Iterable<Component>? children}) : super(children: children);

/// Position of the viewport's center in the parent's coordinate frame.
Expand All @@ -40,8 +41,10 @@ abstract class Viewport extends Component implements PositionProvider {
///
/// Changing the size at runtime triggers the [handleResize] event. The size
/// cannot be negative.
@override
Vector2 get size => _size;
final Vector2 _size = Vector2.zero();
@override
set size(Vector2 value) {
assert(
value.x >= 0 && value.y >= 0,
Expand Down
3 changes: 2 additions & 1 deletion packages/flame/lib/src/geometry/circle_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import 'dart:ui';
import '../../components.dart';
import '../../extensions.dart';
import '../../geometry.dart';
import '../effects/provider_interfaces.dart';

class CircleComponent extends ShapeComponent {
class CircleComponent extends ShapeComponent implements SizeProvider {
/// With this constructor you can create your [CircleComponent] from a radius
/// and a position. It will also calculate the bounding rectangle [size] for
/// the [CircleComponent].
Expand Down
17 changes: 9 additions & 8 deletions packages/flame/test/effects/size_effect_test.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import 'dart:math';

import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/game.dart';
import 'package:flame/src/effects/controllers/effect_controller.dart';
import 'package:flame/src/effects/size_effect.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('SizeEffect', () {
flameGame.test('relative', (game) async {
final component = PositionComponent();
final component = ResizableComponent();
await game.ensureAdd(component);

component.size = Vector2.all(1.0);
Expand All @@ -32,7 +31,7 @@ void main() {
});

flameGame.test('absolute', (game) async {
final component = PositionComponent();
final component = ResizableComponent();
await game.ensureAdd(component);

component.size = Vector2.all(1.0);
Expand All @@ -54,7 +53,7 @@ void main() {
});

flameGame.test('reset relative', (game) async {
final component = PositionComponent();
final component = ResizableComponent();
await game.ensureAdd(component);

final effect = SizeEffect.by(
Expand All @@ -74,7 +73,7 @@ void main() {
});

flameGame.test('reset absolute', (game) {
final component = PositionComponent();
final component = ResizableComponent();
game.ensureAdd(component);

final effect = SizeEffect.to(
Expand All @@ -93,7 +92,7 @@ void main() {
});

flameGame.test('size composition', (game) async {
final component = PositionComponent();
final component = ResizableComponent();
await game.ensureAdd(component);

await component.add(
Expand Down Expand Up @@ -123,7 +122,7 @@ void main() {

testRandom('a very long size change', (Random rng) async {
final game = FlameGame()..onGameResize(Vector2(1, 1));
final component = PositionComponent();
final component = ResizableComponent();
await game.ensureAdd(component);

final effect = SizeEffect.by(
Expand All @@ -149,3 +148,5 @@ void main() {
});
});
}

class ResizableComponent extends PositionComponent implements SizeProvider {}

0 comments on commit 1bfed57

Please sign in to comment.