Friday, November 14, 2008

AS3 Tween class randomly stops during animation

I gave the new AS3 Tween class a whirl to perform some simple animation entirely via ActionScript, rather than sharing the responsibilities between ActionScript and the Timeline.

It worked great...most of the time. The rest of the time, the Tweens would randomly decide to stop -- mid-animation -- without throwing any error as to why. "Most of the time" just doesn't cut it.

The Problem

Here is a simplified example of this situation:

package
{
   import flash.display.*;
   import fl.transitions.*;
   import fl.transitions.easing.*;

   public class SomeContainer() extends Sprite
   {
      ...
      public function animate(o:DisplayObject, x2:int, duration:uint)
      {
         var tween:Tween = new Tween(
               o,
               "x",
               Regular.easeOut,
               someDisplayObject.x,
               x2),
               duration);

      }
   }
}

When invoking animate(), the tween works most of the time, but randomly it stops.

There is no warning of this problem in the ActionScript 3 reference manual, but this Adobe Devnet article contains some fine print that reads:

Note: Consider variable scope when using the Tween class. If a tween is created in a function, it is important that the variable's scope exists beyond the function itself. If a tween is stored to a variable of local scope, ActionScript garbage collection removes the tween as the function completes, which will likely be before the tween has even begun.

So the problem with this scenario is that the Flash Garbage Collector does not care whether the function-scoped variable is still working asynchronously or not - it destroys objects simply by reference counts and in this case nothing outside of the function references this Tween instance.

The Solution

To fix this, simply scope the Tween instance to a class-level variable (a private attribute) rather than a function-level variable. For example:

package
{
   import flash.display.*;
   import fl.transitions.*;
   import fl.transitions.easing.*;

   public class SomeContainer() extends Sprite
   {
      ...
      public function animate(o:DisplayObject, x2:int, duration:uint)
      {
         tween = new Tween(
               o,
               "x",
               Regular.easeOut,
               someDisplayObject.x,
               x2),
               duration);
      }

      private var tween:Tween;
   }
}

This way, the garbage collector sees that something outside of the scope of the function still references the Tween instance created within the function, so it does not destroy the instance.

29 comments:

  1. Thanks! This really helps me alot! :D

    ReplyDelete
  2. thanks, your article confirmed my suspicion about the gc cleaning the tweener up

    ReplyDelete
  3. Awesome man... I would have been looking for hours without this tip. Great help. I wish they made this more obvious to people.

    ReplyDelete
  4. This is simple great. I have been struggling so hard over this problem! Man... tried so many wierd things to get it working.

    Thanks a million!

    ReplyDelete
  5. Thanks mate, stupid bug I new it'd be a simple anwer , GC tramp

    ReplyDelete
  6. Thanks. This is way simpler than storing in arrays and then cleaning it all up on MOTION_FINISH. Whew.

    ReplyDelete
  7. Love this article. A simple one for a simple answer. (to a hell of an annoying problem!) Thank you.

    ReplyDelete
  8. Thanks for taking the time to post - very helpful!

    ReplyDelete
  9. This comment has been removed by a blog administrator.

    ReplyDelete
  10. It's been said before, but to better represent the scope of people you've helped: Thanks, dude.

    ReplyDelete
  11. Glad this is helping so many people! =) Happy coding everyone!

    ReplyDelete
  12. That has just saved me soo much time and stress! Cheers.

    ReplyDelete
  13. well, doesn't work for me, i mean i got it the way you said it, but it still randomely stops. I use Loader to load images from xml and in the loop which loads images after image i create new Tween, but var definition is outside the function which contains that loop, still the same thing :|

    ReplyDelete
  14. @pau if you are creating a new Tween for each image you load, you would need to store those in an array rather than a var.

    The array would then show the garbage collector that the Tween instances are still referenced by something.

    If you over-wrote the same var, then only the most recently-created Tween would work without being garbage-collected.

    ReplyDelete
  15. Dude...thank you thank you thank you...I was really pulling my hair out on this exact issue!

    ReplyDelete
  16. Amazing tip! I actually spent hours on this, kept thinking either i'd coded something wrong, or my installation of flash was messing up! Thanks for the article, and i agree with an above comment that things like this should be made more obvious in the documentation!

    ReplyDelete
  17. You are the man. This has been a recurring problem for me for a long time!

    ReplyDelete
  18. Ditto to all of the above - what a nightmare scenario! I have been checking my event listeners, tweens, class structures - everything!!! THANK YOU FOR THIS VERY, VERY VALUABLE ARTICLE.

    ReplyDelete
  19. What if my variables are set globally and it still freezes...

    ReplyDelete
  20. @Jerome can you share relevant code snippets and I will take a look :)

    ReplyDelete
  21. Thanks a lot!

    Cheers from Latvian games developers! :)

    ReplyDelete
  22. Hi! I have a situation like Jerome. I've sent you an email to your gmail account. Didn't post it here because the code was too long. I've set the tween variables globally but it still stops randomly.

    ReplyDelete
  23. Seriously man, this has saved me a lot of time searching.

    It was one between only two answers i could find on google.
    Well, it seems i have to get knowing the mr. Garbage collector...

    ReplyDelete
  24. Thank you so much!!
    I had multipe tweens and sometimes half of them would all stop simultaneously. I would have never guessed it was the garbage collector.
    So I created a global array for all my tweens.

    ReplyDelete
  25. Thanks for this, it was driving me crazy. Appreciated.

    ReplyDelete
  26. Thanks...Helpfullll!

    ReplyDelete

Was this post helpful? Do you have questions about it? Do you want to share your own programming blog? I'd love to read your feedback.

Note: Only a member of this blog may post a comment.