Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Invoke setToStart on child effect controller of wrapping effect controllers #3168

Merged
merged 4 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/flame/lib/effects.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export 'src/effects/controllers/duration_effect_controller.dart';
export 'src/effects/controllers/effect_controller.dart';
export 'src/effects/controllers/infinite_effect_controller.dart';
export 'src/effects/controllers/linear_effect_controller.dart';
export 'src/effects/controllers/mixins/has_single_child_effect_controller.dart';
export 'src/effects/controllers/pause_effect_controller.dart';
export 'src/effects/controllers/random_effect_controller.dart';
export 'src/effects/controllers/repeated_effect_controller.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'package:flame/src/effects/controllers/effect_controller.dart';
import 'package:flame/src/effects/effect.dart';
import 'package:flame/effects.dart';

/// An effect controller that waits for [delay] seconds before running the
/// child controller. While waiting, the progress will be reported at 0.
class DelayedEffectController extends EffectController {
class DelayedEffectController extends EffectController
with HasSingleChildEffectController {
DelayedEffectController(EffectController child, {required this.delay})
: assert(delay >= 0, 'Delay must be non-negative: $delay'),
_child = child,
Expand All @@ -14,6 +14,9 @@ class DelayedEffectController extends EffectController {
final double delay;
double _timer;

@override
EffectController get child => _child;

@override
bool get isRandom => _child.isRandom;

Expand Down Expand Up @@ -65,14 +68,12 @@ class DelayedEffectController extends EffectController {
@override
void setToStart() {
_timer = 0;
super.setToStart();
}

@override
void setToEnd() {
_timer = delay;
_child.setToEnd();
super.setToEnd();
}

@override
void onMount(Effect parent) => _child.onMount(parent);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import 'package:flame/src/effects/controllers/effect_controller.dart';
import 'package:flame/src/effects/effect.dart';
import 'package:flame/effects.dart';

/// Effect controller that wraps a [child] effect controller and repeats it
/// infinitely.
class InfiniteEffectController extends EffectController {
InfiniteEffectController(this.child) : super.empty();
class InfiniteEffectController extends EffectController
with HasSingleChildEffectController {
InfiniteEffectController(EffectController child)
: _child = child,
super.empty();

final EffectController child;
final EffectController _child;

@override
EffectController get child => _child;

@override
bool get completed => false;
Expand Down Expand Up @@ -45,17 +50,4 @@ class InfiniteEffectController extends EffectController {
}
return 0;
}

@override
void setToStart() {
child.setToStart();
}

@override
void setToEnd() {
child.setToEnd();
}

@override
void onMount(Effect parent) => child.onMount(parent);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:flame/effects.dart';
import 'package:meta/meta.dart';

/// This mixin must be used with [EffectController]s that wrap a single child
/// effect controller of type [T].
mixin HasSingleChildEffectController<T extends EffectController>
ufrshubham marked this conversation as resolved.
Show resolved Hide resolved
on EffectController {
/// Returns the wrapped child effect controller.
T get child;

@mustCallSuper
@override
void setToStart() {
child.setToStart();
}

@mustCallSuper
@override
void setToEnd() {
child.setToEnd();
}

@mustCallSuper
@override
void onMount(Effect parent) {
child.onMount(parent);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import 'dart:math';

import 'package:flame/src/effects/controllers/duration_effect_controller.dart';
import 'package:flame/src/effects/controllers/effect_controller.dart';
import 'package:flame/src/effects/effect.dart';
import 'package:flame/effects.dart';

/// An [EffectController] that wraps another effect controller [child] and
/// randomizes its duration after each reset.
Expand All @@ -14,9 +12,11 @@ import 'package:flame/src/effects/effect.dart';
/// The child's duration is randomized first at construction, and then at each
/// reset (`setToStart`). Thus, the child has a concrete well-defined duration
/// at any point in time.
class RandomEffectController extends EffectController {
RandomEffectController(this.child, this.randomGenerator)
class RandomEffectController extends EffectController
with HasSingleChildEffectController<DurationEffectController> {
RandomEffectController(DurationEffectController child, this.randomGenerator)
: assert(!child.isInfinite, 'Child cannot be infinite'),
_child = child,
super.empty() {
_initializeDuration();
}
Expand Down Expand Up @@ -52,9 +52,12 @@ class RandomEffectController extends EffectController {
);
}

final DurationEffectController child;
final DurationEffectController _child;
final RandomVariable randomGenerator;

@override
DurationEffectController get child => _child;

@override
bool get isRandom => true;

Expand All @@ -73,18 +76,12 @@ class RandomEffectController extends EffectController {
@override
double recede(double dt) => child.recede(dt);

@override
void setToEnd() => child.setToEnd();

@override
void setToStart() {
child.setToStart();
super.setToStart();
_initializeDuration();
}

@override
void onMount(Effect parent) => child.onMount(parent);

void _initializeDuration() {
final duration = randomGenerator.nextValue();
assert(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import 'package:flame/src/effects/controllers/effect_controller.dart';
import 'package:flame/src/effects/effect.dart';
import 'package:flame/effects.dart';

/// Effect controller that repeats [child] controller a certain number of times.
///
/// The [repeatCount] must be positive, and [child] controller cannot be
/// infinite. The child controller will be reset after each iteration (except
/// the last).
class RepeatedEffectController extends EffectController {
RepeatedEffectController(this.child, this.repeatCount)
class RepeatedEffectController extends EffectController
with HasSingleChildEffectController {
RepeatedEffectController(EffectController child, this.repeatCount)
: assert(repeatCount > 0, 'repeatCount must be positive'),
assert(!child.isInfinite, 'child cannot be infinite'),
_child = child,
_remainingCount = repeatCount,
super.empty();

final EffectController child;
final EffectController _child;
final int repeatCount;

/// How many iterations this controller has remaining. When this reaches 0
/// the controller is considered completed.
int get remainingIterationsCount => _remainingCount;
int _remainingCount;

@override
EffectController get child => _child;

@override
double get progress => child.progress;

Expand Down Expand Up @@ -74,15 +78,12 @@ class RepeatedEffectController extends EffectController {
@override
void setToStart() {
_remainingCount = repeatCount;
child.setToStart();
super.setToStart();
}

@override
void setToEnd() {
_remainingCount = 0;
child.setToEnd();
super.setToEnd();
}

@override
void onMount(Effect parent) => child.onMount(parent);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import 'package:flame/src/effects/controllers/duration_effect_controller.dart';
import 'package:flame/src/effects/controllers/effect_controller.dart';
import 'package:flame/src/effects/effect.dart';
import 'package:flame/effects.dart';
import 'package:flame/src/effects/measurable_effect.dart';

/// This controller can force execution of an effect at a predefined speed.
Expand All @@ -15,12 +13,14 @@ import 'package:flame/src/effects/measurable_effect.dart';
/// - the [speed] cannot be zero (or negative),
/// - the [child] controller must be a [DurationEffectController],
/// - the parent effect must be a [MeasurableEffect].
class SpeedEffectController extends EffectController {
SpeedEffectController(this.child, {required this.speed})
class SpeedEffectController extends EffectController
with HasSingleChildEffectController<DurationEffectController> {
SpeedEffectController(DurationEffectController child, {required this.speed})
: assert(speed > 0, 'Speed must be positive: $speed'),
_child = child,
super.empty();

final DurationEffectController child;
final DurationEffectController _child;
final double speed;
late MeasurableEffect _parentEffect;

Expand All @@ -30,6 +30,9 @@ class SpeedEffectController extends EffectController {
/// (which will happen at the first call to `advance()`).
bool _initialized = false;

@override
DurationEffectController get child => _child;

@override
bool get isRandom => true;

Expand Down Expand Up @@ -68,13 +71,13 @@ class SpeedEffectController extends EffectController {
@override
void setToEnd() {
_initialized = false;
child.setToEnd();
super.setToEnd();
}

@override
void setToStart() {
_initialized = false;
child.setToStart();
super.setToStart();
}

@override
Expand All @@ -84,6 +87,6 @@ class SpeedEffectController extends EffectController {
'SpeedEffectController can only be applied to a MeasurableEffect',
);
_parentEffect = parent as MeasurableEffect;
child.onMount(parent);
super.onMount(parent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:flame/effects.dart';
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';

void main() {
group('HasSingleChildEffectController', () {
test('child getter should return the wrapped child effect controller', () {
final childController = _MockEffectController();
final controller = _TestEffectController(childController);

expect(controller.child, equals(childController));
});

test('setToStart should call setToStart on the child effect controller',
() {
final childController = _MockEffectController();
final controller = _TestEffectController(childController);
controller.setToStart();
verify(childController.setToStart).called(1);
});

test('setToEnd should call setToEnd on the child effect controller', () {
final childController = _MockEffectController();
final controller = _TestEffectController(childController);
controller.setToEnd();
verify(childController.setToEnd).called(1);
});

test('onMount should call onMount on the child effect controller', () {
final childController = _MockEffectController();
final controller = _TestEffectController(childController);
final parentEffect = _MockEffect();
controller.onMount(parentEffect);
verify(() => childController.onMount(parentEffect)).called(1);
});
});
}

class _TestEffectController extends _MockEffectController
with HasSingleChildEffectController<_MockEffectController> {
_TestEffectController(_MockEffectController child) : _child = child;

final _MockEffectController _child;

@override
_MockEffectController get child => _child;
}

class _MockEffectController extends Mock implements EffectController {}

class _MockEffect extends Mock implements Effect {}
Loading