I encountered an interesting issue in Moshi today, which I think may be useful to explain to anyone else using TweenLite.
A lot of Moshi Monsters is powered by commands, and often we have many commands that run at the same time. A lot of those commands are used to manipulate display objects and move them within the playarea. Today, I spotted a bug where a tween’s callback wasn’t being called on complete, and wasn’t sure why this was happening.
My first guess was to look into whether there was some unintended garbage collection occurring. After a while, I realised this wasn’t the case and started to look a bit deeper into the commands that were being run at the time. It turned out that if you were speedy enough with the mouse, you could get multiple commands to fire at once, affecting the same target.
In the below scenario (a very simplified version of the issue I encountered) we have 2 tweens, both affecting the same target clip. One tweens the clip to a new X position, and the other tweens to a new Y. Both tweens have callbacks that affect the target in different ways:
private var testClip : MovieClip;
public function ExampleOfTweenOverwrite()
{
//Draw an initial green square on the test clip
testClip = new MovieClip();
testClip.graphics.beginFill(0x00FF00);
testClip.graphics.drawRect(0, 0, 50, 50);
testClip.graphics.endFill();
this.addChild(testClip);
doTweens();
}
private function doTweens() : void
{
//Move the test clip to (300,300)
TweenLite.to(testClip, 0.5, {x : 300, onComplete : firstTweenComplete});
TweenLite.to(testClip, 0.1, {y : 300, onComplete : secondTweenComplete});
}
public function firstTweenComplete() : void
{
//Rotate our test clip a bit
testClip.rotation = 15;
}
public function secondTweenComplete() : void
{
//Re-colour our test clip red
testClip.graphics.beginFill(0xFF0000);
testClip.graphics.drawRect(0, 0, 50, 50);
testClip.graphics.endFill();
}
So, the target clip starts off as a green square. We then want to tween it to the position 300,300. When it reaches its target x or y it will rotate slightly and/or redraw red.
Initially, I would have expected the green square in the above example to end up at 300,300, be drawn red, AND be rotated. This is not what happens.
In TweenLite, you can’t by default have two tweens associated to the same target. If you try to do this, the first tween gets overwritten. This means that in the above example, you end up with a red square, not rotated, at coords 0,300.
When you have many commands that might affect the same target, it’s important to know that the tweens can get overwritten. This is why in the code I was working on today, the callback was never getting called.
To fix this issue, I had to change the “overwrite” value of the tweens, such that neither could overwrite the other. The default value of this parameter is “ALL_IMMEDIATE” (1), which means that the overwrite is carried out immediately when any tween is created. If you want all tweens to be carried out without being overwritten, you can do so by updating this value to zero. This prevents any tween conflicts and doesn’t allow it to be overwritten.
So now, we update our doTweens function to be the following:
private function doTweens() : void
{
TweenLite.to(testClip, 0.5, {x : 300, onComplete : firstTweenComplete, overwrite : 0});
TweenLite.to(testClip, 0.1, {y : 300, onComplete : secondTweenComplete, overwrite : 0});
}
Note the new overwrite params.
Now when we run the code, we get the expected output. The square is moved to (300,300), it is re-coloured red, and is slightly rotated.
If you have any issues with non-completing tweens, I recommend looking at the Greensock page for more info on the OverwriteManager and its possible settings.