Skip to content

Commit

Permalink
feat(effects): Added SineEffectController (#1262)
Browse files Browse the repository at this point in the history
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].
  • Loading branch information
st-pasha authored Dec 27, 2021
1 parent 7c6ae6d commit c888703
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 2 deletions.
14 changes: 14 additions & 0 deletions doc/effects.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ There are multiple effect controllers provided by the Flame framework as well:
- [`SpeedEffectController`](#speedeffectcontroller)
- [`DelayedEffectController`](#delayedeffectcontroller)
- [`RandomEffectController`](#randomeffectcontroller)
- [`SineEffectController`](#sineeffectcontroller)
- [`ZigzagEffectController`](#zigzageffectcontroller)


Expand Down Expand Up @@ -524,6 +525,17 @@ of the produced random durations. Two distributions -- `.uniform` and `.exponent
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`

Simple alternating effect controller. Over the course of one `period`, this controller will proceed
Expand All @@ -540,4 +552,6 @@ final ec = ZigzagEffectController(period: 2);

* [Examples of various effects](https://examples.flame-engine.org/#/).


[tau]: https://en.wikipedia.org/wiki/Tau_(mathematical_constant)
[Lissajous curve]: https://en.wikipedia.org/wiki/Lissajous_curve
39 changes: 37 additions & 2 deletions examples/lib/stories/effects/effect_controllers_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ class EffectControllersExample extends FlameGame {
static const description = '''
This page demonstrates application of various non-standard effect
controllers.
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.
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
Expand Down Expand Up @@ -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)),
),
]),
);
}
}
1 change: 1 addition & 0 deletions packages/flame/lib/effects.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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_linear_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/zigzag_effect_controller.dart';
export 'src/effects/effect.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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'),
);
});
});
}

0 comments on commit c888703

Please sign in to comment.