From ffba0f91b8dc5bf14d3c4ea871b262fe5e397a06 Mon Sep 17 00:00:00 2001 From: Sai Rajendra Immadi Date: Fri, 24 May 2024 12:32:43 +0530 Subject: [PATCH 1/2] docs: Fix IsometricTileMap code (#3165) Fix the code in the docs page examples which has IsometricTileset and is not present in the flame to SpriteSheet Co-authored-by: Lukas Klingsbo --- doc/flame/components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/flame/components.md b/doc/flame/components.md index 2285e269938..643bfe0f9f8 100644 --- a/doc/flame/components.md +++ b/doc/flame/components.md @@ -1231,7 +1231,7 @@ A simple example on how to use it: // Creates a tileset, the block ids are automatically assigned sequentially // starting at 0, from left to right and then top to bottom. final tilesetImage = await images.load('tileset.png'); -final tileset = IsometricTileset(tilesetImage, 32); +final tileset = SpriteSheet(image: tilesetImage, srcSize: Vector2.all(32)); // Each element is a block id, -1 means nothing final matrix = [[0, 1, 0], [1, 0, 0], [1, 1, 1]]; add(IsometricTileMapComponent(tileset, matrix)); From 217c95f0a53fd5a7933bfa57833f951cc0037878 Mon Sep 17 00:00:00 2001 From: DevKage <33748002+ufrshubham@users.noreply.github.com> Date: Fri, 24 May 2024 13:10:06 +0530 Subject: [PATCH 2/2] fix: Invoke `setToStart` on child effect controller of wrapping effect controllers (#3168) `DelayedEffectController` was not calling `setToStart` on its child, causing it to not behave as expected when used with `InfiniteEffectController`. This was reported by a discord member [in this message](https://discord.com/channels/509714518008528896/516639688581316629/1242258377766080666). This PR introduces a `HasSingleChildEffectController` mixin which can be added to any effect controller that wraps a single child effect controller. This mixin makes sure that the `setToStart`, `setToEnd` and `onMount` methods always get invoked on the child controllers. --- packages/flame/lib/effects.dart | 1 + .../delayed_effect_controller.dart | 15 +++--- .../infinite_effect_controller.dart | 28 ++++------ .../has_single_child_effect_controller.dart | 28 ++++++++++ .../controllers/random_effect_controller.dart | 23 ++++----- .../repeated_effect_controller.dart | 21 ++++---- .../controllers/speed_effect_controller.dart | 21 ++++---- ...s_single_child_effect_controller_test.dart | 51 +++++++++++++++++++ 8 files changed, 131 insertions(+), 57 deletions(-) create mode 100644 packages/flame/lib/src/effects/controllers/mixins/has_single_child_effect_controller.dart create mode 100644 packages/flame/test/effects/controllers/mixins/has_single_child_effect_controller_test.dart diff --git a/packages/flame/lib/effects.dart b/packages/flame/lib/effects.dart index ac4ec21afab..d3382e11e44 100644 --- a/packages/flame/lib/effects.dart +++ b/packages/flame/lib/effects.dart @@ -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'; diff --git a/packages/flame/lib/src/effects/controllers/delayed_effect_controller.dart b/packages/flame/lib/src/effects/controllers/delayed_effect_controller.dart index d4b7c825a85..940c71da88e 100644 --- a/packages/flame/lib/src/effects/controllers/delayed_effect_controller.dart +++ b/packages/flame/lib/src/effects/controllers/delayed_effect_controller.dart @@ -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, @@ -14,6 +14,9 @@ class DelayedEffectController extends EffectController { final double delay; double _timer; + @override + EffectController get child => _child; + @override bool get isRandom => _child.isRandom; @@ -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); } diff --git a/packages/flame/lib/src/effects/controllers/infinite_effect_controller.dart b/packages/flame/lib/src/effects/controllers/infinite_effect_controller.dart index 0f900f9f97e..abfcf3b77d2 100644 --- a/packages/flame/lib/src/effects/controllers/infinite_effect_controller.dart +++ b/packages/flame/lib/src/effects/controllers/infinite_effect_controller.dart @@ -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; @@ -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); } diff --git a/packages/flame/lib/src/effects/controllers/mixins/has_single_child_effect_controller.dart b/packages/flame/lib/src/effects/controllers/mixins/has_single_child_effect_controller.dart new file mode 100644 index 00000000000..9dd0d1d063a --- /dev/null +++ b/packages/flame/lib/src/effects/controllers/mixins/has_single_child_effect_controller.dart @@ -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 + 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); + } +} diff --git a/packages/flame/lib/src/effects/controllers/random_effect_controller.dart b/packages/flame/lib/src/effects/controllers/random_effect_controller.dart index 83ba219b822..1851562e986 100644 --- a/packages/flame/lib/src/effects/controllers/random_effect_controller.dart +++ b/packages/flame/lib/src/effects/controllers/random_effect_controller.dart @@ -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. @@ -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 { + RandomEffectController(DurationEffectController child, this.randomGenerator) : assert(!child.isInfinite, 'Child cannot be infinite'), + _child = child, super.empty() { _initializeDuration(); } @@ -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; @@ -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( diff --git a/packages/flame/lib/src/effects/controllers/repeated_effect_controller.dart b/packages/flame/lib/src/effects/controllers/repeated_effect_controller.dart index 31506cb1fbe..de3ccd78849 100644 --- a/packages/flame/lib/src/effects/controllers/repeated_effect_controller.dart +++ b/packages/flame/lib/src/effects/controllers/repeated_effect_controller.dart @@ -1,19 +1,20 @@ -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 @@ -21,6 +22,9 @@ class RepeatedEffectController extends EffectController { int get remainingIterationsCount => _remainingCount; int _remainingCount; + @override + EffectController get child => _child; + @override double get progress => child.progress; @@ -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); } diff --git a/packages/flame/lib/src/effects/controllers/speed_effect_controller.dart b/packages/flame/lib/src/effects/controllers/speed_effect_controller.dart index dfed43a3c21..f3091b47e82 100644 --- a/packages/flame/lib/src/effects/controllers/speed_effect_controller.dart +++ b/packages/flame/lib/src/effects/controllers/speed_effect_controller.dart @@ -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. @@ -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 { + 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; @@ -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; @@ -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 @@ -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); } } diff --git a/packages/flame/test/effects/controllers/mixins/has_single_child_effect_controller_test.dart b/packages/flame/test/effects/controllers/mixins/has_single_child_effect_controller_test.dart new file mode 100644 index 00000000000..dd7e9c79333 --- /dev/null +++ b/packages/flame/test/effects/controllers/mixins/has_single_child_effect_controller_test.dart @@ -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 {}