feat(effects): Added SineEffectController (#1262)

An effect controller that represents a single period of the sine function. Use this to create
natural-looking harmonic oscillations. Two perpendicular move effects governed by
SineEffectControllers with different periods, will create a [Lissajous curve].
This commit is contained in:
Pasha Stetsenko
2021-12-27 15:12:39 -08:00
committed by GitHub
parent 7c6ae6def3
commit c888703d6e
5 changed files with 115 additions and 2 deletions

View File

@ -63,6 +63,7 @@ There are multiple effect controllers provided by the Flame framework as well:
- [`SpeedEffectController`](#speedeffectcontroller) - [`SpeedEffectController`](#speedeffectcontroller)
- [`DelayedEffectController`](#delayedeffectcontroller) - [`DelayedEffectController`](#delayedeffectcontroller)
- [`RandomEffectController`](#randomeffectcontroller) - [`RandomEffectController`](#randomeffectcontroller)
- [`SineEffectController`](#sineeffectcontroller)
- [`ZigzagEffectController`](#zigzageffectcontroller) - [`ZigzagEffectController`](#zigzageffectcontroller)
@ -524,6 +525,17 @@ of the produced random durations. Two distributions -- `.uniform` and `.exponent
any other can be implemented by the user. any other can be implemented by the user.
### `SineEffectController`
An effect controller that represents a single period of the sine function. Use this to create
natural-looking harmonic oscillations. Two perpendicular move effects governed by
`SineEffectControllers` with different periods, will create a [Lissajous curve].
```dart
final ec = SineEffectController(period: 1);
```
### `ZigzagEffectController` ### `ZigzagEffectController`
Simple alternating effect controller. Over the course of one `period`, this controller will proceed Simple alternating effect controller. Over the course of one `period`, this controller will proceed
@ -540,4 +552,6 @@ final ec = ZigzagEffectController(period: 2);
* [Examples of various effects](https://examples.flame-engine.org/#/). * [Examples of various effects](https://examples.flame-engine.org/#/).
[tau]: https://en.wikipedia.org/wiki/Tau_(mathematical_constant) [tau]: https://en.wikipedia.org/wiki/Tau_(mathematical_constant)
[Lissajous curve]: https://en.wikipedia.org/wiki/Lissajous_curve

View File

@ -8,10 +8,15 @@ class EffectControllersExample extends FlameGame {
static const description = ''' static const description = '''
This page demonstrates application of various non-standard effect This page demonstrates application of various non-standard effect
controllers. controllers.
The first white square has a ZigzagEffectController with period 1. The The first white square has a ZigzagEffectController with period 1. The
orange square next to it has two move effects, each with a orange square next to it has two move effects, each with a
ZigzagEffectController. ZigzagEffectController.
The lime square has a SineEffectController with the same period of 1s. The
violet square next to it has two move effects, each with a
SineEffectController with periods, but one of the effects is slightly
delayed.
'''; ''';
@override @override
@ -44,5 +49,35 @@ class EffectControllersExample extends FlameGame {
), ),
]), ]),
); );
add(
RectangleComponent.square(
position: Vector2(140, 50),
size: 20,
paint: Paint()..color = const Color(0xffbeff63),
)..add(
MoveEffect.by(
Vector2(0, 20),
InfiniteEffectController(SineEffectController(period: 1)),
),
),
);
add(
RectangleComponent.square(
position: Vector2(190, 50),
size: 10,
paint: Paint()..color = const Color(0xffb663ff),
)..addAll([
MoveEffect.by(
Vector2(0, 20),
InfiniteEffectController(SineEffectController(period: 1))
..advance(0.25),
),
MoveEffect.by(
Vector2(10, 0),
InfiniteEffectController(SineEffectController(period: 1)),
),
]),
);
} }
} }

View File

@ -12,6 +12,7 @@ export 'src/effects/controllers/repeated_effect_controller.dart';
export 'src/effects/controllers/reverse_curved_effect_controller.dart'; export 'src/effects/controllers/reverse_curved_effect_controller.dart';
export 'src/effects/controllers/reverse_linear_effect_controller.dart'; export 'src/effects/controllers/reverse_linear_effect_controller.dart';
export 'src/effects/controllers/sequence_effect_controller.dart'; export 'src/effects/controllers/sequence_effect_controller.dart';
export 'src/effects/controllers/sine_effect_controller.dart';
export 'src/effects/controllers/speed_effect_controller.dart'; export 'src/effects/controllers/speed_effect_controller.dart';
export 'src/effects/controllers/zigzag_effect_controller.dart'; export 'src/effects/controllers/zigzag_effect_controller.dart';
export 'src/effects/effect.dart'; export 'src/effects/effect.dart';

View File

@ -0,0 +1,23 @@
import 'dart:math' as math;
import 'duration_effect_controller.dart';
import 'infinite_effect_controller.dart';
import 'repeated_effect_controller.dart';
/// This effect controller follows a sine wave.
///
/// Use this controller to create effects that exhibit natural-looking harmonic
/// motion.
///
/// Combine with [RepeatedEffectController] or [InfiniteEffectController] in
/// order to create longer waves.
class SineEffectController extends DurationEffectController {
SineEffectController({required double period})
: assert(period > 0, 'Period must be positive: $period'),
super(period);
@override
double get progress {
const tau = math.pi * 2;
return math.sin(tau * timer / duration);
}
}

View File

@ -0,0 +1,40 @@
import 'dart:math';
import 'package:flame/effects.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('SineEffectController', () {
test('general properties', () {
final ec = SineEffectController(period: 1);
expect(ec.duration, 1);
expect(ec.started, true);
expect(ec.completed, false);
expect(ec.progress, 0);
expect(ec.isRandom, false);
});
test('progression', () {
final ec = SineEffectController(period: 3);
final expectedProgress =
List<double>.generate(101, (i) => sin(i * 0.01 * 2 * pi));
for (final p in expectedProgress) {
expect(ec.progress, closeTo(p, 2e-14));
ec.advance(0.01 * 3);
}
expect(ec.completed, true);
});
test('errors', () {
expect(
() => SineEffectController(period: 0),
failsAssert('Period must be positive: 0.0'),
);
expect(
() => SineEffectController(period: -1.1),
failsAssert('Period must be positive: -1.1'),
);
});
});
}