mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 11:43:19 +08:00
feat: Added NoiseEffectController (#1356)
This commit is contained in:
@ -62,6 +62,7 @@ There are multiple effect controllers provided by the Flame framework as well:
|
|||||||
- [`SequenceEffectController`](#sequenceeffectcontroller)
|
- [`SequenceEffectController`](#sequenceeffectcontroller)
|
||||||
- [`SpeedEffectController`](#speedeffectcontroller)
|
- [`SpeedEffectController`](#speedeffectcontroller)
|
||||||
- [`DelayedEffectController`](#delayedeffectcontroller)
|
- [`DelayedEffectController`](#delayedeffectcontroller)
|
||||||
|
- [`NoiseEffectController`](#noiseffectcontroller)
|
||||||
- [`RandomEffectController`](#randomeffectcontroller)
|
- [`RandomEffectController`](#randomeffectcontroller)
|
||||||
- [`SineEffectController`](#sineeffectcontroller)
|
- [`SineEffectController`](#sineeffectcontroller)
|
||||||
- [`ZigzagEffectController`](#zigzageffectcontroller)
|
- [`ZigzagEffectController`](#zigzageffectcontroller)
|
||||||
@ -506,6 +507,16 @@ final ec = DelayedEffectController(LinearEffectController(1), delay: 5);
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### `NoiseEffectController`
|
||||||
|
|
||||||
|
This effect controller exhibits noisy behavior, i.e. it oscillates randomly around zero. Such effect
|
||||||
|
controller can be used to implement a variety of shake effects.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
final ec = NoiseEffectController(duration: 0.6, frequency: 10);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### `RandomEffectController`
|
### `RandomEffectController`
|
||||||
|
|
||||||
This controller wraps another controller and makes its duration random. The actual value for the
|
This controller wraps another controller and makes its duration random. The actual value for the
|
||||||
|
|||||||
@ -14,6 +14,8 @@ class MoveEffectExample extends FlameGame {
|
|||||||
The middle green square has a combination of two movement effects: a
|
The middle green square has a combination of two movement effects: a
|
||||||
`MoveEffect.to` and a `MoveEffect.by` which forces it to periodically jump.
|
`MoveEffect.to` and a `MoveEffect.by` which forces it to periodically jump.
|
||||||
|
|
||||||
|
The purple square executes a sequence of shake effects.
|
||||||
|
|
||||||
At the bottom there are 60 more components which demonstrate movement along
|
At the bottom there are 60 more components which demonstrate movement along
|
||||||
an arbitrary path using `MoveEffect.along`.
|
an arbitrary path using `MoveEffect.along`.
|
||||||
''';
|
''';
|
||||||
@ -31,7 +33,9 @@ class MoveEffectExample extends FlameGame {
|
|||||||
..style = PaintingStyle.stroke
|
..style = PaintingStyle.stroke
|
||||||
..strokeWidth = 5.0
|
..strokeWidth = 5.0
|
||||||
..color = Colors.greenAccent;
|
..color = Colors.greenAccent;
|
||||||
|
final paint3 = Paint()..color = const Color(0xffb372dc);
|
||||||
|
|
||||||
|
// Red square, moving back and forth
|
||||||
add(
|
add(
|
||||||
RectangleComponent.square(
|
RectangleComponent.square(
|
||||||
position: Vector2(20, 50),
|
position: Vector2(20, 50),
|
||||||
@ -49,6 +53,8 @@ class MoveEffectExample extends FlameGame {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Green square, moving and jumping
|
||||||
add(
|
add(
|
||||||
RectangleComponent.square(
|
RectangleComponent.square(
|
||||||
position: Vector2(20, 150),
|
position: Vector2(20, 150),
|
||||||
@ -80,6 +86,29 @@ class MoveEffectExample extends FlameGame {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
add(
|
||||||
|
RectangleComponent.square(
|
||||||
|
size: 15,
|
||||||
|
position: Vector2(40, 240),
|
||||||
|
paint: paint3,
|
||||||
|
)..add(
|
||||||
|
SequenceEffect(
|
||||||
|
[
|
||||||
|
MoveEffect.by(
|
||||||
|
Vector2(5, 0),
|
||||||
|
NoiseEffectController(duration: 1, frequency: 20),
|
||||||
|
),
|
||||||
|
MoveEffect.by(Vector2.zero(), LinearEffectController(2)),
|
||||||
|
MoveEffect.by(
|
||||||
|
Vector2(0, 10),
|
||||||
|
NoiseEffectController(duration: 1, frequency: 10),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
infinite: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
final path1 = Path()..moveTo(200, 250);
|
final path1 = Path()..moveTo(200, 250);
|
||||||
for (var i = 1; i <= 5; i++) {
|
for (var i = 1; i <= 5; i++) {
|
||||||
final x = 200 + 100 * sin(i * tau * 2 / 5);
|
final x = 200 + 100 * sin(i * tau * 2 / 5);
|
||||||
|
|||||||
@ -6,6 +6,7 @@ export 'src/effects/controllers/duration_effect_controller.dart';
|
|||||||
export 'src/effects/controllers/effect_controller.dart';
|
export 'src/effects/controllers/effect_controller.dart';
|
||||||
export 'src/effects/controllers/infinite_effect_controller.dart';
|
export 'src/effects/controllers/infinite_effect_controller.dart';
|
||||||
export 'src/effects/controllers/linear_effect_controller.dart';
|
export 'src/effects/controllers/linear_effect_controller.dart';
|
||||||
|
export 'src/effects/controllers/noise_effect_controller.dart';
|
||||||
export 'src/effects/controllers/pause_effect_controller.dart';
|
export 'src/effects/controllers/pause_effect_controller.dart';
|
||||||
export 'src/effects/controllers/random_effect_controller.dart';
|
export 'src/effects/controllers/random_effect_controller.dart';
|
||||||
export 'src/effects/controllers/repeated_effect_controller.dart';
|
export 'src/effects/controllers/repeated_effect_controller.dart';
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/animation.dart' show Curve, Curves;
|
||||||
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
|
import 'duration_effect_controller.dart';
|
||||||
|
|
||||||
|
/// Effect controller that oscillates around 0 following a noise curve.
|
||||||
|
///
|
||||||
|
/// The [frequency] parameter controls smoothness/jerkiness of the oscillations.
|
||||||
|
/// It is roughly proportional to the total number of swings for the duration
|
||||||
|
/// of the effect.
|
||||||
|
///
|
||||||
|
/// The [taperingCurve] describes how the effect fades out over time. The
|
||||||
|
/// curve that you supply will be flipped along the X axis, so that the effect
|
||||||
|
/// starts at full force, and gradually reduces to zero towards the end.
|
||||||
|
///
|
||||||
|
/// This effect controller can be used to implement various shake effects. For
|
||||||
|
/// example, putting into a `MoveEffect.by` will create a shake motion, where
|
||||||
|
/// the magnitude and the direction of shaking is controlled by the effect's
|
||||||
|
/// `offset`.
|
||||||
|
class NoiseEffectController extends DurationEffectController {
|
||||||
|
NoiseEffectController({
|
||||||
|
required double duration,
|
||||||
|
required this.frequency,
|
||||||
|
this.taperingCurve = Curves.easeInOutCubic,
|
||||||
|
Random? random,
|
||||||
|
}) : assert(duration > 0, 'duration must be positive'),
|
||||||
|
assert(frequency > 0, 'frequency parameter must be positive'),
|
||||||
|
noise = SimplexNoise(random),
|
||||||
|
super(duration);
|
||||||
|
|
||||||
|
final double frequency;
|
||||||
|
final Curve taperingCurve;
|
||||||
|
final SimplexNoise noise;
|
||||||
|
|
||||||
|
@override
|
||||||
|
double get progress {
|
||||||
|
final x = timer / duration;
|
||||||
|
final amplitude = taperingCurve.transform(1 - x);
|
||||||
|
return noise.noise2D(x * frequency, 0) * amplitude;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flame/effects.dart';
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:flutter/animation.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('NoiseEffectController', () {
|
||||||
|
test('general properties', () {
|
||||||
|
final ec = NoiseEffectController(duration: 1, frequency: 12);
|
||||||
|
expect(ec.duration, 1.0);
|
||||||
|
expect(ec.frequency, 12.0);
|
||||||
|
expect(ec.taperingCurve, Curves.easeInOutCubic);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, false);
|
||||||
|
expect(ec.progress, 0);
|
||||||
|
expect(ec.isRandom, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('progression', () {
|
||||||
|
final random = Random(567890);
|
||||||
|
final ec = NoiseEffectController(
|
||||||
|
duration: 1,
|
||||||
|
frequency: 3,
|
||||||
|
random: random,
|
||||||
|
);
|
||||||
|
final observed = <double>[];
|
||||||
|
for (var t = 0.0; t < 1.0; t += 0.1) {
|
||||||
|
observed.add(ec.progress);
|
||||||
|
ec.advance(0.1);
|
||||||
|
}
|
||||||
|
expect(observed, [
|
||||||
|
0.0,
|
||||||
|
-0.4852269950897251,
|
||||||
|
0.7905631204866628,
|
||||||
|
0.25384428741054194,
|
||||||
|
0.06718741964100555,
|
||||||
|
0.08011164287850409,
|
||||||
|
-0.008746065536907871,
|
||||||
|
-0.07181264736289301,
|
||||||
|
-0.014005001721806985,
|
||||||
|
0.00985567863632108,
|
||||||
|
-0.000015661267181374608,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('errors', () {
|
||||||
|
expect(
|
||||||
|
() => NoiseEffectController(duration: 0, frequency: 1),
|
||||||
|
failsAssert('duration must be positive'),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
() => NoiseEffectController(duration: 1, frequency: 0),
|
||||||
|
failsAssert('frequency parameter must be positive'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user