From e8d267d7878b08a0b721e264812d31473bfc419c Mon Sep 17 00:00:00 2001 From: Scott Ysebert Date: Wed, 8 Jan 2020 11:40:04 -0800 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=90=9B=20-=20Fixed=20a=20bug=20in=20`?= =?UTF-8?q?flare=5Fcontrols`=20that=20was=20ignoring=20`mixSeconds`=20pass?= =?UTF-8?q?ed=20into=20the=20`play()`=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- flare_flutter/lib/flare_controls.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/flare_flutter/lib/flare_controls.dart b/flare_flutter/lib/flare_controls.dart index ed89a16..cfacf9a 100644 --- a/flare_flutter/lib/flare_controls.dart +++ b/flare_flutter/lib/flare_controls.dart @@ -15,7 +15,6 @@ class FlareControls extends FlareController { /// The current [ActorAnimation]. String _animationName; - final double _mixSeconds = 0.1; /// The [FlareAnimationLayer]s currently active. final List _animationLayers = []; @@ -68,9 +67,9 @@ class FlareControls extends FlareController { layer.mix += elapsed; layer.time += elapsed; - double mix = (_mixSeconds == null || _mixSeconds == 0.0) + double mix = (layer.mixSeconds == null || layer.mixSeconds == 0.0) ? 1.0 - : min(1.0, layer.mix / _mixSeconds); + : min(1.0, layer.mix / layer.mixSeconds); /// Loop the time if needed. if (layer.animation.isLooping) { From 4794f588504bc4b9d703a9f877ee52522bf88b28 Mon Sep 17 00:00:00 2001 From: Scott Ysebert Date: Wed, 8 Jan 2020 17:05:19 -0800 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=9A=A7=20-=20Made=20some=20good=20pro?= =?UTF-8?q?gress=20on=20fixing=20the=20animation=20controller,=20but=20I?= =?UTF-8?q?=20noticed=20that=20when=20it=20gets=20spammed=20with=20request?= =?UTF-8?q?s,=20there=20is=20a=20bit=20of=20a=20slowdown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- flare_flutter/lib/flare_controls.dart | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/flare_flutter/lib/flare_controls.dart b/flare_flutter/lib/flare_controls.dart index cfacf9a..5c8015a 100644 --- a/flare_flutter/lib/flare_controls.dart +++ b/flare_flutter/lib/flare_controls.dart @@ -19,6 +19,8 @@ class FlareControls extends FlareController { /// The [FlareAnimationLayer]s currently active. final List _animationLayers = []; + double _ticker = 0.0; + /// Called at initialization time, it stores the reference /// to the current [FlutterActorArtboard]. @override @@ -33,15 +35,22 @@ class FlareControls extends FlareController { /// to the end of the list of currently playing animation layers. void play(String name, {double mix = 1.0, double mixSeconds = 0.2}) { _animationName = name; + if (_animationName != null && _artboard != null) { + int layerIndex = _animationLayers.indexWhere((layer) => layer.name == name); ActorAnimation animation = _artboard.getAnimation(_animationName); - if (animation != null) { + + if (animation != null && layerIndex == -1) { _animationLayers.add(FlareAnimationLayer() ..name = _animationName ..animation = animation ..mix = mix ..mixSeconds = mixSeconds); isActive.value = true; + } else if (layerIndex >= 0) { + /// If we already have reference to this, update the seconds + FlareAnimationLayer layer = _animationLayers[layerIndex]; + layer.mixSeconds = mixSeconds; } } } @@ -59,17 +68,21 @@ class FlareControls extends FlareController { /// List of completed animations during this frame. List completed = []; + _ticker += elapsed; + /// This loop will mix all the currently active animation layers so that, /// if an animation is played on top of the current one, it'll smoothly mix - /// between the two instead of immediately switching to the new one. + /// between the two instead of immediately switching to the new one. for (int i = 0; i < _animationLayers.length; i++) { FlareAnimationLayer layer = _animationLayers[i]; - layer.mix += elapsed; - layer.time += elapsed; + layer.time = _ticker; + + layer.mix += layer.name == _animationName ? elapsed / layer.mixSeconds : -elapsed / layer.mixSeconds; + layer.mix = max(0.0, min(1.0, layer.mix)); double mix = (layer.mixSeconds == null || layer.mixSeconds == 0.0) ? 1.0 - : min(1.0, layer.mix / layer.mixSeconds); + : max(0.0, min(1.0, layer.mix / layer.mixSeconds)); /// Loop the time if needed. if (layer.animation.isLooping) { @@ -79,8 +92,8 @@ class FlareControls extends FlareController { /// Apply the animation with the current mix. layer.animation.apply(layer.time, _artboard, mix); - /// Add (non-looping) finished animations to the list. - if (layer.time > layer.animation.duration) { + /// axe it after it's finished mixing + if (mix == 0) { completed.add(layer); } } From 398c5747c346f482194b41eb399b95ac3360fb01 Mon Sep 17 00:00:00 2001 From: Scott Ysebert Date: Fri, 10 Jan 2020 09:54:41 -0800 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20-=20Maintain=20current=20time?= =?UTF-8?q?=20of=20each=20layer=20so=20that=20they=20can=20be=20mixed=20in?= =?UTF-8?q?=20and=20out=20no=20matter=20if=20they=20have=20finished=20mixi?= =?UTF-8?q?ng=20already?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- flare_flutter/lib/flare_controls.dart | 56 +++++++++++++++------------ 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/flare_flutter/lib/flare_controls.dart b/flare_flutter/lib/flare_controls.dart index 5c8015a..6f286ad 100644 --- a/flare_flutter/lib/flare_controls.dart +++ b/flare_flutter/lib/flare_controls.dart @@ -4,6 +4,14 @@ import 'flare.dart'; import 'flare_actor.dart'; import 'flare_controller.dart'; +/// [TimedAnimation] holds a reference to [FlareAnimationLayer]s +/// and [currentTime] maintains that layers current animation timeline +/// for mixing purposes +class TimedAnimation { + FlareAnimationLayer layer; + double currentTime; // how many seconds have elapsed +} + /// [FlareControls] is a concrete implementation of the [FlareController]. /// /// This controller will provide some basic functionality, such as @@ -16,9 +24,11 @@ class FlareControls extends FlareController { /// The current [ActorAnimation]. String _animationName; - /// The [FlareAnimationLayer]s currently active. - final List _animationLayers = []; + /// The [TimedAnimation]s currently active. + final List _animations = []; + /// Used as a reference for each animation layer + /// to stay in sync double _ticker = 0.0; /// Called at initialization time, it stores the reference @@ -37,20 +47,21 @@ class FlareControls extends FlareController { _animationName = name; if (_animationName != null && _artboard != null) { - int layerIndex = _animationLayers.indexWhere((layer) => layer.name == name); + int layerIndex = _animations.indexWhere((ani) => ani.layer.name == name); ActorAnimation animation = _artboard.getAnimation(_animationName); if (animation != null && layerIndex == -1) { - _animationLayers.add(FlareAnimationLayer() + _animations.add(TimedAnimation() + ..layer = (FlareAnimationLayer() ..name = _animationName ..animation = animation ..mix = mix - ..mixSeconds = mixSeconds); + ..mixSeconds = mixSeconds) + ..currentTime = mix * mixSeconds); isActive.value = true; } else if (layerIndex >= 0) { /// If we already have reference to this, update the seconds - FlareAnimationLayer layer = _animationLayers[layerIndex]; - layer.mixSeconds = mixSeconds; + _animations[layerIndex].layer.mixSeconds = mixSeconds; } } } @@ -66,23 +77,20 @@ class FlareControls extends FlareController { @override bool advance(FlutterActorArtboard artboard, double elapsed) { /// List of completed animations during this frame. - List completed = []; + List completed = []; _ticker += elapsed; /// This loop will mix all the currently active animation layers so that, /// if an animation is played on top of the current one, it'll smoothly mix /// between the two instead of immediately switching to the new one. - for (int i = 0; i < _animationLayers.length; i++) { - FlareAnimationLayer layer = _animationLayers[i]; + for (int i = 0; i < _animations.length; i++) { + FlareAnimationLayer layer = _animations[i].layer; layer.time = _ticker; + _animations[i].currentTime += layer.name == _animationName ? elapsed : -elapsed; + _animations[i].currentTime = max(0.0, min(layer.mixSeconds, _animations[i].currentTime)); - layer.mix += layer.name == _animationName ? elapsed / layer.mixSeconds : -elapsed / layer.mixSeconds; - layer.mix = max(0.0, min(1.0, layer.mix)); - - double mix = (layer.mixSeconds == null || layer.mixSeconds == 0.0) - ? 1.0 - : max(0.0, min(1.0, layer.mix / layer.mixSeconds)); + layer.mix = max(0.0, min(1.0, _animations[i].currentTime / layer.mixSeconds)); /// Loop the time if needed. if (layer.animation.isLooping) { @@ -90,19 +98,19 @@ class FlareControls extends FlareController { } /// Apply the animation with the current mix. - layer.animation.apply(layer.time, _artboard, mix); + layer.animation.apply(layer.time, _artboard, layer.mix); - /// axe it after it's finished mixing - if (mix == 0) { - completed.add(layer); + /// Axe it after it's finished mixing + if (layer.mix == 0) { + completed.add(_animations[i]); } } /// Notify of the completed animations. - for (final FlareAnimationLayer animation in completed) { - _animationLayers.remove(animation); - onCompleted(animation.name); + for (final TimedAnimation animation in completed) { + _animations.remove(animation); + onCompleted(animation.layer.name); } - return _animationLayers.isNotEmpty; + return _animations.isNotEmpty; } }