Jump to content
Search Community

Add Tweens to Timeline in runtime

LilaQ test
Moderator Tag

Recommended Posts

Hey there,

 

I'm trying to add Tweens to the timeline when it's already running. I tried to .insert or .append a new Tween via a onComplete call of a prior tween (I'm trying to build some fancy snow animation)

 

But even if I set the delay to timeline.duration, the next Tweens won't kick in :(

 

Any ideas what I might be doing wrong?

 

Thx in advance.

 

Greetz

LilaQ

Link to comment
Share on other sites

I'd need to see your code, but it sounds like you may be adding tweens after a timeline has already completed. You can do that, but don't forget to restart() or resume() it. And be aware that a timeline's currentTime will never exceed its duration, nor can it be less than zero. So if, for example, your timeline finishes and then 5 seconds later you append() a tween, there won't be a 5-second gap between the previous end of the timeline and the start of your new tween. It kinda makes sense when you think about it, but sometimes that concepts trips people up a bit.

 

If you're still having trouble, please post a simple FLA that demonstrates the problem and we'll take a peek. (don't forget to zip it first)

Link to comment
Share on other sites

Hey,

 

thx for the quick reply :)

 

Here is my complete code (except the Circle class, but that just draws a simple Circle with random size)

 

package 
{
import com.greensock.plugins.BezierThroughPlugin;
import com.greensock.plugins.GlowFilterPlugin;
import com.greensock.plugins.TweenPlugin;
import flash.events.Event;
import com.greensock.TimelineMax;
import flash.display.Sprite;
import com.greensock.TweenMax;

/**
 * @author LilaQ
 */
public class Main extends Sprite
{
	private var CircleTimeline:TimelineMax = new TimelineMax();
	private var i:int = 0;
	private var curCircle:Circle;

	public function Main()
	{
		stage.addEventListener(Event.ACTIVATE, Init);
	}

	private function Init(e:Event)	:void 
	{
		var bg:Sprite = new Sprite();
		bg.graphics.beginFill(0x000000);
		bg.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
		bg.graphics.endFill();
		addChild(bg);

		TweenPlugin.activate([bezierThroughPlugin, GlowFilterPlugin]);

		//CircleTimeline.addCallback(, 5);
		for (i = 0; i < 30; i++) 
		{
			curCircle = new Circle();
			curCircle.x = Math.random() * stage.stageWidth;
			curCircle.y = -120;
			CircleTimeline.insert(new TweenMax(curCircle, curCircle.rand*0.5+3, 
											   {bezierThrough:[{x:Math.random()*stage.stageWidth, y:Math.random()*stage.stageHeight},{x:Math.random()*stage.stageWidth-50, y:stage.stageHeight}],
											   	orientToBezier:true, 

											   	glowFilter:{color:0xffffff, alpha:0.8, 
											   	blurX:5, blurY:5, strength: 3, quality: 2},
											   	onComplete: reAddCircle

											   	}
											   ),(-curCircle.rand-3));	// Delay					
			addChild(curCircle);
		}
		//CircleTimeline.addCallback(slowDown, 5);
	}

	private function reAddCircle()	:void
	{
		trace("Main.reAddCircle()");
			curCircle = new Circle();
			curCircle.x = Math.random() * stage.stageWidth;
			curCircle.y = -120;
			CircleTimeline.insert(new TweenMax(curCircle, curCircle.rand*0.5+3, 
											   {bezierThrough:[{x:Math.random()*stage.stageWidth, y:Math.random()*stage.stageHeight},{x:Math.random()*stage.stageWidth-50, y:stage.stageHeight}],
											   	orientToBezier:true, 

											   	glowFilter:{color:0xffffff, alpha:0.8, 
											   	blurX:5, blurY:5, strength: 3, quality: 2}


											   	}
											   ), CircleTimeline.duration);	// Delay					
			addChild(curCircle);
	}

	private function slowDown()	:void 
	{
		if(CircleTimeline.timeScale == 1)	CircleTimeline.timeScale = 0.3;
		else CircleTimeline.timeScale == 1;
	}

	private function reverse()	:void
	{
		trace("Main.reverse(tE)");
		CircleTimeline.reverse();
	}

	private function replay()	:void 
	{
		trace("Main.replay(tF)");
		CircleTimeline.play();
	}
}
}

 

Your explanation makes sense of course. My idea was to add a new snowflake every time a current one hits its finishing point, so there are alway (at the moment) 30 flakes in a tweening motion. So I thought with every new snowflake the timeline should extend itself to the current length + the length of the new flake.

 

I'll try around a little bit while you can a look at my code ;) I hope I don't have any embarissing flaws in the code ;)

 

Thanks in advance.

 

Greetz,

LilaQ

Link to comment
Share on other sites

Ok I made progress. I just use "insert" in the reAddCircle function, and delayed it to "currentTime". Works ok so far. Just need to make the snow fall less periodically, at the moment, the snow comes in "waves" :D

Link to comment
Share on other sites

One problem I did see in the logic was that you're using onCompletes to trigger a function that adds more to the timeline, so it'll work great while it's running fowards but what if it goes backwards and then fowards again and triggers the same onCompletes that already fired previously? See what I mean? Your TimelineMax will get longer and longer each time you run through the same part of the timeline. You could consider adding conditional logic to your handler so that it will only run once for each tween (skipping it on subsequent onComplete calls). Just an idea.

 

Oh, and please make sure you have the latest version of the GreenSock classes: http://www.TweenLite.com

Link to comment
Share on other sites

Hey,

 

thx, I should have the most up-to-date version of the classes :)

 

I got it running so far.

I was inspired by the page you have linked on your mainpage (http://www.timsoret.com/flash/snow/)

 

Actually I'm trying to find out how Tim's Animation can run so smoothly.

Mine is struggling with about 300 flakes, while his can handle roundabout 2500.

 

I'm not quite sure what processes are stressing out the CPU most, so maybe you could have a look at my code and tell me what could be made better.

I don't want to copy Tim's Animation, just want to learn how to optimize ActionScript 3 code properly, since I started coding AS3 only 3 weeks ago.

 

package 
{
import com.greensock.plugins.GlowFilterPlugin;
import com.greensock.plugins.BezierThroughPlugin;
import com.greensock.plugins.TweenPlugin;
import flash.net.URLRequest;
import flash.display.Loader;
import flash.events.Event;

import com.greensock.easing.*;
import com.greensock.TimelineMax;
import flash.display.Sprite;
import com.greensock.TweenMax;

/**
 * @author LilaQ
 */
public class Main extends Sprite
{
	private var CircleTimeline:TimelineMax = new TimelineMax();
	private var i:int = 0;
	private var curCircle:Circle;
	private var picture:Loader = new Loader();

	public function Main()
	{
		picture.load(new URLRequest("flake.png"));
		picture.contentLoaderInfo.addEventListener(Event.COMPLETE, Init);
	}

	private function Init(e:Event)	:void 
	{
		trace("Main.Init(e)");
		var bg:Sprite = new Sprite();
		bg.graphics.beginFill(0x000000);
		bg.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
		bg.graphics.endFill();
		addChild(bg);

		TweenPlugin.activate([bezierThroughPlugin, GlowFilterPlugin]);

		//CircleTimeline.addCallback(, 5);
		for (i = 0; i < 300; i++) 
		{
			trace("Main.Init(e)");
			curCircle = new Circle();
			addChild(curCircle);
			curCircle.x = Math.random() * stage.stageWidth;
			curCircle.y = -120;
			curCircle.cacheAsBitmap = true;

			CircleTimeline.insert(new TweenMax(curCircle, Math.random()*5, //curCircle.rand*0.5+3
											   {bezierThrough:[{x:Math.random()*stage.stageWidth, y:Math.random()*stage.stageHeight},{x:Math.random()*stage.stageWidth, y:stage.stageHeight+20}],
											   	orientToBezier:true, 
											   	ease: Linear.easeNone,
											   	rotation: 900,

											   	onComplete: reAddCircle,
											   	onCompleteParams:[curCircle]
											   	}
											   ));	// Delay	,(-curCircle.rand-3)	
			addChild(curCircle);
		}
		CircleTimeline.play();
		//CircleTimeline.addCallback(slowDown, 5);
	}

	private function reAddCircle(target:Circle)	:void
	{
		curCircle = target;
		curCircle.x = Math.random() * stage.stageWidth;
		curCircle.y = -120;
		CircleTimeline.insert(new TweenMax(curCircle, 5, 
										   {bezierThrough:[{x:Math.random()*stage.stageWidth, y:Math.random()*stage.stageHeight},{x:Math.random()*stage.stageWidth, y:stage.stageHeight+20}],
										   	orientToBezier:true, 
										   	ease: Linear.easeNone,
										   	rotation: Math.random() * 900,
										   	glowFilter:{color:0xffffff, alpha:0.8, 
										   	blurX:5, blurY:5, strength: 3, quality: 2},

										   	onComplete:reAddCircle,
										   	onCompleteParams:[curCircle]
										   	}), CircleTimeline.currentTime);	// Delay					
		addChild(curCircle);
	}
}
}

 

The Circle class (now with an image):

package {
import flash.events.Event;
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;

/**
 * @author LilaQ
 */
public class Circle extends Sprite 
{
	public var rand:Number;
	private var target:Loader = new Loader();

	public function Circle() 
	{
		target.load(new URLRequest("flake.png"));
		target.contentLoaderInfo.addEventListener(Event.COMPLETE, Init);
	}

	private function Init(e:Event)	:void
	{
		rand = Math.random();

		target.alpha = Math.random()+0.5;
		target.cacheAsBitmap = true;

		target.scaleX = rand*0.4;
		target.scaleY = rand*0.4;
		target.alpha = rand*0.4;

		addChild(target);
	}
}
}

 

Your help would be greatly appreciated. :)

 

Greetz,

LilaQ

Link to comment
Share on other sites

Optimization is a pretty broad topic with lots of possibilities. One thing to keep in mind is that graphics rendering in the Flash Player is usually by FAR the biggest CPU hog, not ActionScript code execution. One thing that can really help is to utilize BitmapData instead of just putting a bunch of Loaders on the stage which it looks like you're doing. Literally use one big BitmapData and draw() your assets to that BitmapData. That way, Flash doesn't have to worry about compositing all those overlapping DisplayObjects or managing hundreds of Loaders. Avoid vectors too if you can. Even if you just loaded your .png once and drew it to a BitmapData which you reused in a bunch of Bitmap objects, I think you'd see a big performance improvement. You're wasting a lot of resources by using a Loader for each Circle and reloading the same image over and over. Also, TweenLite is slightly faster than TweenMax. Since you're not using any TweenMax-specific features, switch to TweenLite. Don't forget to activate the necessary plugins, that's all (TweenMax did that for you, but TweenLite doesn't automatically activate things like BezierPlugin).

 

Hope that helps!

Link to comment
Share on other sites

Hey,

 

thanks for your reply. I'm not quite familiar with the BitmapData class, but I'll work into that. :)

 

Another thing I just found out is that Tim used seperate Tweens for the x and y coordinates on the flakes.

 

My question is now, does that improve the speed on the animation itself? Or is it the same amount of calculations for the cpu if you use a combined Tweenlite.to for the x and y coordinates?

 

 

Thx for all your help, it's really appreciated :)

 

Greez,

LilaQ

Link to comment
Share on other sites

Actually, it's the opposite. Having one tween manage multiple properties is easier on the CPU than doing a separate tween for each property. Sometimes it makes sense to separate them to make your job easier, though - like if you want the snow to fall at a constant rate (the y tween) yet sway back and forth horizontally (the x tween).

Link to comment
Share on other sites

  • 4 months later...

Hello guys =) I saw this thread a little late but let me explain some details :

 

• Yes, I separated x & y to animate them independently : this way, for each snowflake the x-tween don't end at the same time of the y-tween, and it greatly improves the natural and random feeling of the overall snow. It's also a great way to optimize the thing : By doing that, I avoid using "Bezier Through". Creating, storing, and using vector paths for each snowflake is a CPU killer too. So I simulate the curve motion using two time-independant tweens : one for x, one for y.

 

• Avoid vector graphics for thousands of elements, because rendering vectors is very CPU intensive, as Jack said.

 

• Don't use loaders. With your way, you're loading 300x the same element but Flash doesn't understand it, and it stores 300 different elements. Juste instantiate a new Sprite or MovieClip for each Snowflake, and they'll share the same memory, so it's an instant win. You can even use 10 different bitmap particles, it won't really affect performances.

 

• Use strict-data typing (see the related page on the greensock site)

 

Hope it helps if someone walk through here =)

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...