Files
flame/doc/effects.md
Lukas Klingsbo a1b6ffa04a Game as a Component (#906)
* Game as a component

* Fix component stories

* Effects are now components

* Update effects docs

* Handle swap of parent

* Fix reAddChildren

* Wait for children to be added

* BaseComponent and PositionComponent to be non-abstract

* Simplify HasGameRef

* Revert so that onLoad can be null

* Fix example description

* Effects as components

* Remove gameRef from addChildren

* Fix hasGameRef

* Start migrating effects

* Updated comments of effect fields

* Fix comments

* Continue to fix sequence and combined effects

* Upgrade ordered_set

* Fix position_component_test

* BaseComponent -> Component

* Fix combined and sequence effects

* Await components to be added in tests

* Remove unnecessary game.update in tests

* Fix some tests related to composition

* BaseGame should be used in examples

* Fix CombinedEffect test

* Keyboard code to be based on Component

* Fix keyboard tests

* Fix analyze problems

* Fix sequence_effect

* Fix combined_effect_test

* Store peak state instead of end state

* Fix sequence_effect tests

* Update tutorial

* Fix tutorial 1

* Remove SimplePositionComponentEffect

* Remove unused test variable

* Update docs

* Removed onMount

* Remove onMount

* Add missing dartdoc

* Fix dart docs

* Add super.update where needed

* Move reAddChildren to component

* Reorganize method order in game widget

* preOffset -> initialDelay, postOffset -> peakDelay

* Introduce component.onParentChange

* Remove tests in wrong file

* Fix composed component test

* Add game lifecycle test

* Use BaseGame for mouse cursor test

* Oxygen should (?) not call super.update

* Use BaseGame in keyboard_test

* Fix onLoad to be properly cached

* Re-add unintentionally removed override

* Fix info for collision detection tests

* Add test for correct lifecycle on parent change

* Fix particles example

* Add component lifecycle diagram to the docs

* Add docs for the game lifecycle

* onRemove should be called when a game is removed from the widget

* Fix analyze errors

* prepare should be called from the component itself, not its parent

* Fix dartdoc

* onParentChange -> onMount

* onMount should have void as return type

* Simplify the loaderFuture in GameWidget

* Fix mock_canvas

* Fix rebase problem

* Remove asComponent

* Less complex _loaderFuture

* Add super.update to no_fcs parallax example

* Fix async tests

* Revert _loaderFuture

* Fix analyze issues

* await gameWithCollidables

* Keep epsilon small where it can be

* tappable methods should return bool

* Game lifecycle is now the same as for Component

* Remove mustCallSuper from component.update

* Make onLoadCache protected

* @internal on onLoadCache

* Cache/Memoize debugPaint and debugTextPaint

* Fix imports

* Fix comments

* Always call super.onLoad so that mixins can override it

* Add forgotten super.onLoad

* Bump coverage percentage

* HasCollidables should override update

* Fix Game comments

* Fix some dartdoc

* Apply suggestions from code review

Co-authored-by: Erick <erickzanardoo@gmail.com>

* Game + Loadable as mixins

* Update packages/flame/lib/src/game/game_widget/game_widget.dart

Co-authored-by: Luan Nico <luanpotter27@gmail.com>

* Update loadable docs

* Fix comments

* Move fps_counter

* Fix keyboard example

* Fix dartdoc

* Remove tutorials temporarily

* Fix game lowlevel graph

* Fix resize issue

Co-authored-by: Erick <erickzanardoo@gmail.com>
Co-authored-by: Luan Nico <luanpotter27@gmail.com>
2021-09-15 00:17:49 +02:00

9.6 KiB

Effects

An effect can be added to any Component that the effect supports.

If you want to create an effect for another component than the ones that already exist, just extend the ComponentEffect class and add your created effect to the component by calling component.add(yourEffect).

Common for all effects

All effects can be paused and resumed with effect.pause() and effect.resume(), and you can check whether it is paused with effect.isPaused.

More advanced effects

Then there are two optional boolean parameters called isInfinite and isAlternating, by combining them you can get different effects.

  • If both of them are true the effect will be infinite and when it is done with going through the curve it will go back in reverse through the curve.
  • If isInfinite is true and isAlternating is false the effect will be applied from start to end of the curve and then restart from the start, for an infinite amount of time.
  • If isInfinite is false and isAlternating is true the effect will go from the beginning of the curve and then back again but after that stop.
  • isInfinite and isAlternating are false by default and then the effect is just applied once, from the beginning of the curve until the end.

isRelative is another parameter used on some effects, it is false by default. If it is set to true it means that the effect starts at the PositionComponent's value and adds whatever value you give to it. If it is false it will treat the value you give it as absolute and it will go to the value you give it no matter where it started.

When an effect is completed the callback onComplete will be called, it can be set as an optional argument to your effect.

Common for MoveEffect, ScaleEffect, SizeEffect and RotateEffect (SimplePositionComponentEffects)

A common thing for MoveEffect, ScaleEffect, SizeEffect and RotateEffect is that it takes duration and speed as arguments, but only use one of them at a time.

  • Duration means the time it takes for one iteration from beginning to end, with alternation taken into account (but not isInfinite).
  • Speed is the speed of the effect
    • pixels per second for MoveEffect
    • pixels per second for SizeEffect
    • radians per second for RotateEffect
    • percentage/100 per second for ScaleEffect

One of these two needs to be defined, if both are defined duration takes precedence.

If we have a MoveEffect that should move between its start position and Vector2(200, 300) for infinity and the time it should take from the start position to get back to the start position again is 5 seconds, the effect would look like this:

MoveEffect(
  path: [Vector2(200, 300)],
  duration: 5,
  isInfinite: true,
  isAlternating: true,
)

MoveEffect

Applied to PositionComponents, this effect can be used to move the component to new positions, using an animation curve.

The speed is measured in pixels per second, and remember that you can give duration as an argument instead of speed.

Usage example:

import 'package:flame/effects.dart';

// Square is a PositionComponent
square.add(MoveEffect(
  path: [Vector2(200, 200), Vector2(200, 100), Vector(0, 50)],
  speed: 250.0,
  curve: Curves.bounceInOut,
));

If you want the positions in the path list to be relative to the components last position, and not absolute values on the screen, then you can set isRelative = true.

When you use that, the next position in the list will be relative to the previous position in the list, or if it is the first element of the list it is relative to the components position. So if you have a component which is positioned at Vector2(100, 100) and you use isRelative = true with the following path list path: [Vector(20, 0), Vector(0, 50)], then the component will first move to (120, 0) and then to (120, 100).

ScaleEffect

Applied to PositionComponents, this effect can be used to change the scale with which the component and its children is rendered on the canvas with, using an animation curve.

This also affects the scaledSize property of the component.

The speed is measured in percentage/100 per second, and remember that you can give duration as an argument instead of speed.

Usage example:

import 'package:flame/effects.dart';

// Square is a PositionComponent
square.add(ScaleEffect(
  scale: Vector2.all(2.0),
  speed: 1.0,
  curve: Curves.bounceInOut,
));

SizeEffect

Applied to PositionComponents, this effect can be used to change the width and height of the component, using an animation curve.

The speed is measured in pixels per second, and remember that you can give duration as an argument instead of speed.

Usage example:

import 'package:flame/effects.dart';

// Square is a PositionComponent
square.add(SizeEffect(
  size: Vector2.all(300),
  speed: 250.0,
  curve: Curves.bounceInOut,
));

RotateEffect

Applied to PositionComponents, this effect can be used to rotate the component, using an animation curve.

The radians argument defines the angle in radians and the speed argument is how fast it will rotate in radians per second, so if you for example want to turn 180° in 2 seconds you set radians: pi and speed: 0.25.

Remember that you can give duration as an argument instead of speed to say how long the effect should last for instead of its speed, which would be much simpler for this example.

Usage example:

import 'dart:math';

import 'package:flame/effects.dart';

// Square is a PositionComponent
square.add(RotateEffect(
  radians: 2 * pi, // In radians
  speed: 1.0, // Radians per second
  curve: Curves.easeInOut,
));

SequenceEffect

This effect is a combination of other effects. You provide it with a list of your predefined effects.

The effects in the list should only be passed to the SequenceEffect, never added to a PositionComponent with add.

Note: No effect (except the last) added to the sequence should have their isInfinite property set to true, because then naturally the sequence will get stuck once it gets to that effect.

You can make the sequence go in a loop by setting both isInfinite: true and isAlternating: true.

Usage example:

final sequence = SequenceEffect(
    effects: [move1, size, move2, rotate],
    isInfinite: true,
    isAlternating: true);
myComponent.add(sequence);

An example of how to use the SequenceEffect can be found here.

CombinedEffect

This effect runs several different type of effects simultaneously on the component that it is added to. You provide it with a list of your predefined effects and if you don't want them to start or end at the same time you can utilize the initialDelay and peakDelay to add time before or after the effect runs.

The effects in the list should only be passed to the CombinedEffect, never added to a PositionComponent with add at the same time.

Note: No effects should be of the same type since they will clash when trying to modify for example a PositionComponent.

Usage example:

final combination = CombinedEffect(effects: [move, size, rotate]);
myComponent.add(combination);

An example of how to use the CombinedEffect can be found here.

Common for paint effects

Flame provides an useful mixin called HasPaint that adds paint variables to your components. It adds a default Paint attribute to the class and an additional collection of paints in case your component requires more than a single Paint. In addition to those attributes, a few methods to make it easy to manipulate the paints will became available, for example setOpacity.

This mixin is used by some of Flame's own components like SpriteComponent, SpriteAnimationComponent, but you can also use it on you any of your custom components. Any component that uses this mixin can have paint effects applied to it.

By default, all effects will animate the main paint of the component. That can be changed by passing a paintId to the effect contructor. This can be useful for when you have a component that have multiple paints; for example a component which has a foreground and background layer.

Below is a list of the available paint effects.

OpacityEffect

This effect allows you animate the opacity of your paint. It receives a double for the opacity, which must be between 0 and 1 (including), and a duration in seconds, represented by a double as well.

Usage example:

myComponent.add(
  OpacityEffect(
    opacity: 0,
    duration: 0.5,
  ),
);

An example of how to use the OpacityEffect can be found here.

ColorEffect

This effect will change the base color of the paint, causing the rendered component to be tinted by the provided color.

Usage example:

myComponent.add(
  ColorEffect(
    color: const Color(0xFF00FF00),
    duration: 0.5,
  ),
);

A more in-depth example can be found here.

Examples

Full examples can be found here.