diff --git a/packages/flame/lib/src/components/sprite_animation_group_component.dart b/packages/flame/lib/src/components/sprite_animation_group_component.dart index df9252d3a36..78a44fab840 100644 --- a/packages/flame/lib/src/components/sprite_animation_group_component.dart +++ b/packages/flame/lib/src/components/sprite_animation_group_component.dart @@ -3,7 +3,7 @@ import 'dart:ui'; import 'package:flame/components.dart'; import 'package:flame/src/effects/provider_interfaces.dart'; import 'package:flame/src/sprite_animation_ticker.dart'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart'; export '../sprite_animation.dart'; @@ -13,6 +13,12 @@ class SpriteAnimationGroupComponent extends PositionComponent /// Key with the current playing animation T? _current; + ValueNotifier? _currentAnimationNotifier; + + /// A [ValueNotifier] that notifies when the current animation changes. + ValueNotifier get currentAnimationNotifier => + _currentAnimationNotifier ??= ValueNotifier(_current); + /// Map with the mapping each state to the flag removeOnFinish final Map removeOnFinish; @@ -136,8 +142,11 @@ class SpriteAnimationGroupComponent extends PositionComponent _current = value; _resizeToSprite(); - if (changed && autoResetTicker) { - animationTicker?.reset(); + if (changed) { + if (autoResetTicker) { + animationTicker?.reset(); + } + _currentAnimationNotifier?.value = value; } } diff --git a/packages/flame/lib/src/components/sprite_group_component.dart b/packages/flame/lib/src/components/sprite_group_component.dart index cc025ad79b9..7d27da784c9 100644 --- a/packages/flame/lib/src/components/sprite_group_component.dart +++ b/packages/flame/lib/src/components/sprite_group_component.dart @@ -2,7 +2,7 @@ import 'dart:ui'; import 'package:flame/components.dart'; import 'package:flame/src/effects/provider_interfaces.dart'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart'; export '../sprite_animation.dart'; @@ -11,17 +11,23 @@ export '../sprite_animation.dart'; class SpriteGroupComponent extends PositionComponent with HasPaint implements SizeProvider { - /// Key with the current playing animation + /// Key for the current sprite. T? _current; - /// Map with the available states for this sprite group + ValueNotifier? _currentSpriteNotifier; + + /// A [ValueNotifier] that notifies when the current sprite changes. + ValueNotifier get currentSpriteNotifier => + _currentSpriteNotifier ??= ValueNotifier(_current); + + /// Map with the available states for this sprite group. Map? _sprites; /// When set to true, the component is auto-resized to match the /// size of current sprite. bool _autoResize; - /// Creates a component with an empty animation which can be set later + /// Creates a component with an empty animation which can be set later. SpriteGroupComponent({ Map? sprites, T? current, @@ -62,8 +68,12 @@ class SpriteGroupComponent extends PositionComponent /// /// Will update [size] if [autoResize] is true. set current(T? value) { + final changed = _current != value; _current = value; _resizeToSprite(); + if (changed) { + _currentSpriteNotifier?.value = value; + } } /// Returns current value of auto resize flag. diff --git a/packages/flame/test/components/sprite_animation_group_component_test.dart b/packages/flame/test/components/sprite_animation_group_component_test.dart index 53926b04800..187a1df3c70 100644 --- a/packages/flame/test/components/sprite_animation_group_component_test.dart +++ b/packages/flame/test/components/sprite_animation_group_component_test.dart @@ -199,122 +199,8 @@ Future main() async { }); }); - group('SpriteAnimationGroupComponent.autoResize', () { - test('mutual exclusive with size while construction', () { - expect( - () => SpriteAnimationGroupComponent<_AnimationState>( - autoResize: true, - size: Vector2.all(2), - ), - throwsAssertionError, - ); - - expect( - () => SpriteAnimationGroupComponent<_AnimationState>(autoResize: false), - throwsAssertionError, - ); - }); - - test('default value set correctly when not provided explicitly', () { - final component1 = SpriteAnimationGroupComponent<_AnimationState>(); - final component2 = SpriteAnimationGroupComponent<_AnimationState>( - size: Vector2.all(2), - ); - - expect(component1.autoResize, true); - expect(component2.autoResize, false); - }); - - test('resizes on current state change', () { - final sprite1 = Sprite(image, srcSize: Vector2.all(76)); - final sprite2 = Sprite(image, srcSize: Vector2.all(15)); - final animation1 = SpriteAnimation.spriteList( - List.filled(5, sprite1), - stepTime: 0.1, - loop: false, - ); - final animation2 = SpriteAnimation.spriteList( - List.filled(5, sprite2), - stepTime: 0.1, - loop: false, - ); - - final component = SpriteAnimationGroupComponent<_AnimationState>( - animations: { - _AnimationState.idle: animation1, - _AnimationState.running: animation2, - }, - current: _AnimationState.idle, - ); - expect(component.size, sprite1.srcSize); - - component.current = _AnimationState.running; - expect(component.size, sprite2.srcSize); - }); - - test('resizes only when true', () { - final sprite1 = Sprite(image, srcSize: Vector2.all(76)); - final sprite2 = Sprite(image, srcSize: Vector2.all(15)); - final animation1 = SpriteAnimation.spriteList( - List.filled(5, sprite1), - stepTime: 0.1, - loop: false, - ); - final animation2 = SpriteAnimation.spriteList( - List.filled(5, sprite2), - stepTime: 0.1, - loop: false, - ); - - final component = SpriteAnimationGroupComponent<_AnimationState>( - animations: { - _AnimationState.idle: animation1, - _AnimationState.running: animation2, - }, - current: _AnimationState.idle, - )..autoResize = false; - - component.current = _AnimationState.running; - expect(component.size, sprite1.srcSize); - - component.autoResize = true; - expect(component.size, sprite2.srcSize); - }); - - test('stop autoResizing on external size modifications', () { - final testSize = Vector2(83, 100); - final sprite1 = Sprite(image, srcSize: Vector2.all(76)); - final sprite2 = Sprite(image, srcSize: Vector2.all(15)); - final animation1 = SpriteAnimation.spriteList( - List.filled(5, sprite1), - stepTime: 0.1, - loop: false, - ); - final animation2 = SpriteAnimation.spriteList( - List.filled(5, sprite2), - stepTime: 0.1, - loop: false, - ); - final animationsMap = { - _AnimationState.idle: animation1, - _AnimationState.running: animation2, - }; - final component = SpriteAnimationGroupComponent<_AnimationState>(); - - // NOTE: Sequence of modifications is important here. Changing the size - // after changing the animations map will disable auto-resizing. So even - // if the current state is changed later, the component should still - // maintain testSize. - component - ..animations = animationsMap - ..size = testSize - ..current = _AnimationState.running; - - expectDouble(component.size.x, testSize.x); - expectDouble(component.size.y, testSize.y); - }); - - test('modify size only if changed while auto-resizing', () { + group('SpriteAnimationGroupComponent.currentAnimationNotifier', () { + test('notifies when the current animation changes', () { final sprite1 = Sprite(image, srcSize: Vector2.all(76)); final sprite2 = Sprite(image, srcSize: Vector2.all(15)); final animation1 = SpriteAnimation.spriteList( @@ -333,84 +219,29 @@ Future main() async { final component = SpriteAnimationGroupComponent<_AnimationState>( animations: animationsMap, ); - - var sizeChangeCounter = 0; - component.size.addListener(() => ++sizeChangeCounter); + var animationChangeCounter = 0; + component.currentAnimationNotifier.addListener( + () => animationChangeCounter++, + ); component.current = _AnimationState.running; - expect(sizeChangeCounter, equals(1)); + expect(animationChangeCounter, equals(1)); component.current = _AnimationState.idle; - expect(sizeChangeCounter, equals(2)); + expect(animationChangeCounter, equals(2)); component.update(1); - expect(sizeChangeCounter, equals(2)); + expect(animationChangeCounter, equals(2)); component.current = _AnimationState.running; - expect(sizeChangeCounter, equals(3)); + expect(animationChangeCounter, equals(3)); component.update(1); - expect(sizeChangeCounter, equals(4)); - }); - }); - - group('SpriteAnimationGroupComponent.autoResetTicker', () { - final sprite1 = Sprite(image, srcSize: Vector2.all(76)); - final sprite2 = Sprite(image, srcSize: Vector2.all(15)); - final animation1 = SpriteAnimation.spriteList( - List.filled(5, sprite1), - stepTime: 0.1, - loop: false, - ); - final animation2 = SpriteAnimation.spriteList( - List.filled(5, sprite2), - stepTime: 0.1, - loop: false, - ); - - test('does reset ticker by default', () { - final component = SpriteAnimationGroupComponent<_AnimationState>( - animations: { - _AnimationState.idle: animation1, - _AnimationState.running: animation2, - }, - current: _AnimationState.idle, - ); - component.update(0.9); - expect(component.animationTicker!.currentIndex, 4); + expect(animationChangeCounter, equals(3)); component.current = _AnimationState.running; - component.update(0.1); - expect(component.animationTicker!.currentIndex, 1); - - component.current = _AnimationState.idle; - expect(component.animationTicker!.currentIndex, 0); - - component.current = _AnimationState.running; - expect(component.animationTicker!.currentIndex, 0); - }); - - test('resets the ticker when enabled', () { - final component = SpriteAnimationGroupComponent<_AnimationState>( - animations: { - _AnimationState.idle: animation1, - _AnimationState.running: animation2, - }, - autoResetTicker: false, - current: _AnimationState.idle, - ); - component.update(0.9); - expect(component.animationTicker!.currentIndex, 4); - - component.current = _AnimationState.running; - component.update(0.1); - expect(component.animationTicker!.currentIndex, 1); - - component.current = _AnimationState.idle; - expect(component.animationTicker!.currentIndex, 4); - - component.current = _AnimationState.running; - expect(component.animationTicker!.currentIndex, 1); + component.update(1); + expect(animationChangeCounter, equals(3)); }); }); } diff --git a/packages/flame/test/components/sprite_group_component_test.dart b/packages/flame/test/components/sprite_group_component_test.dart index 8a24328386d..73f5117994f 100644 --- a/packages/flame/test/components/sprite_group_component_test.dart +++ b/packages/flame/test/components/sprite_group_component_test.dart @@ -132,4 +132,40 @@ Future main() async { expect(sizeChangeCounter, equals(2)); }); }); + + group('SpriteGroupComponent.currentSpriteNotifier', () { + test('notifies when the current sprite changes', () { + final spritesMap = { + _SpriteState.idle: Sprite(image, srcSize: Vector2.all(15)), + _SpriteState.running: Sprite(image, srcSize: Vector2.all(15)), + _SpriteState.flying: Sprite(image, srcSize: Vector2(15, 12)), + }; + final component = SpriteGroupComponent<_SpriteState>( + sprites: spritesMap, + ); + var spriteChangeCounter = 0; + component.currentSpriteNotifier.addListener( + () => spriteChangeCounter++, + ); + + component.current = _SpriteState.running; + expect(spriteChangeCounter, equals(1)); + + component.current = _SpriteState.idle; + expect(spriteChangeCounter, equals(2)); + + component.update(1); + expect(spriteChangeCounter, equals(2)); + + component.current = _SpriteState.running; + expect(spriteChangeCounter, equals(3)); + + component.update(1); + expect(spriteChangeCounter, equals(3)); + + component.current = _SpriteState.running; + component.update(1); + expect(spriteChangeCounter, equals(3)); + }); + }); }