Tornado in Flash MX

Someone asked me how to make a tornado with Flash, a tornado with letters. So here it is: The Tornado Tutorial

1. What you need on the scene
A movie clip of a dot, any colour you like, with the instance name “dot”.
A layer called code that will hold all the code (everything is contained in 1 frame).

2. The rotating motion
First we are going to focus on the rotating effect. You can find the piece of code on this site, in the open source section. Personally, I took it from there: actionscript-toolbox. After cleaning, you get this:

function rotate(){
	this.angle+=anglespeed;
	var a=Math.sin(this.angle)*this.radius;
	var b=Math.cos(this.angle)*this.radius;
	var scalar=perspective/(b+perspective);
	this._x=a*scalar + centerx;
	this._y=b*scalar/tilt+this.starty;
	this._xscale=this._yscale=scalar*100;	
}

3. Analyze that function
I won’t explain how the function works just now. But I want you to notice that it refers to a certain number of variables that we have to define somewhere:[list][]angle, inside the dot (because refered as this.angle)
[
]anglespeed, on the timeline
[]radius, inside the dot
[
]perspective, in the timeline
[]centerx, in the timeline
[
]tilt, in the timeline
[*]starty, in the dot[/list]That’s why you’ll need to put this just before the function declaration:

dot.angle=0;
anglespeed = Math.PI/24;
dot.radius=50;
perspective = 300;
centerx=200;
tilt = 5;
dot.starty=200;

4. Let’s make the dot move
Now all we have to do is make the dot move…

dot.onEnterFrame=rotate;

End of Part 1. To be continued…

pom :asian:

The complete code and the source:

// General constants
anglespeed = Math.PI/24;
perspective = 300;
tilt = 5;
centerx=200;

// Dot variables
dot.angle=0;
dot.radius=50;
dot.starty=200;

// Function declaration
function rotate(){
	this.angle+=anglespeed;
	var a=Math.sin(this.angle)*this.radius;
	var b=Math.cos(this.angle)*this.radius;
	var scalar=perspective/(b+perspective);
	this._x=a*scalar + centerx;
	this._y=b*scalar/tilt+this.starty;
	this._xscale=this._yscale=scalar*100;	
}

// Make it move!
dot.onEnterFrame=rotate;

pom :asian:

OK, time for some duplication. Ready?

1. What we need to change
First of all, we will have to duplicate that dot of ours, and make them all move, so the last line of the previous code

dot.onEnterFrame=rotate;

should be included in a loop to make all the dots move.
Then the variables that depend on the dot that we declared first should be declared inside the loop because each duplicated dot has its own variables.

2. The code now
The variables declaration

anglespeed = Math.PI/24;
perspective = 300;
tilt = 5;
centerx=200;
maxClip=50;

I’ve just added a maxClip variable that will tell the total amount of dots that you want.

The function remains the same.

The loop that duplicates our dots

for (var i=0;i < maxClip;i++){
	mc=dot.duplicateMovieClip("dot"+i,i);
	mc.angle=0;
	mc.radius=50;
	mc.starty=5*i;
	mc.onEnterFrame=rotate;
}

This is where we have to be good to have a good effect. If you don’t understand that code, check the AS tricks section, most of it is explained there.

You can see here that all the duplicated clips have the same starting angle angle, the same radius radius, but a different starting point on the _y axis starty. Then we assign the rotate function to the duplicate.

3. Small improvement
If you test the code now, you’ll see that the first dot is still visible. To solve that little problem, simply add this line at the bottom of the code:

dot._visible=0;

End of Part Deux. Everything OK?

pom :asian:

Complete code and source:

// General constants
anglespeed = Math.PI/24;
perspective = 300;
tilt = 5;
centerx=200;
maxClip=50;

// Function declaration
function rotate(){
	this.angle+=anglespeed;
	var a=Math.sin(this.angle)*this.radius;
	var b=Math.cos(this.angle)*this.radius;
	var scalar=perspective/(b+perspective);
	this._x=a*scalar + centerx;
	this._y=b*scalar/tilt+this.starty;
	this._xscale=this._yscale=scalar*100;	
}

// Duplicate and make it move!
for (var i=0;i < maxClip;i++){
	mc=dot.duplicateMovieClip("dot"+i,i);
	mc.angle=0;
	mc.radius=50;
	mc.starty=5*i;
	mc.onEnterFrame=rotate;
}

dot._visible=0;

pom :asian:

Time for us to make it look like a tornado…

The good thing now is that all we have to mess with is the last loop, by changing a little bit the variables we are using.

1. First thing
Increase the maxClip a bit. Something like 150-200. And define 2 new variables:

maxHeight=400;

It refers to the height of the tornado.

2. Messing with the variables
So first, we want to give our tornado the right shape. This means that the radius of the particles at the bottom is smaller than the one of the top of the tornado.
Change the for loop into this

for (var i=0;i < maxClip;i++){
	mc=dot.duplicateMovieClip("dot"+i,i);
	mc.angle=0;
	mc.starty=2*i;
	mc.radius=mc.starty/2;
	mc.onEnterFrame=rotate;
}

Notice that the radius depends on the _y position, so you should define the radius AFTER the _y position… And I divided by 2 to make it look better. You can try with any number, or nothing at all.

You should now have something like a line the describing a circle, or something like that…

3. The tornado…
The problem is that all the dots are aligned. To solve that, all we have to do is to give the dots a random angle to start with

mc.angle=random(360);

I know, the tornado is upside down, but we’ll solve that problem later :slight_smile:

4. A bit of randomness
We are also going to position our dots randomly on the _y axis (the mc.starty variable).

mc.starty=random(maxHeight);

There’s not really a big difference with the previous one, but randomness is always fun…

5. Upside Down
To flip it, just change one tiny thing

mc.radius=(maxHeight-mc.starty)/2;

You can also give a minimal radius

mc.radius=(maxHeight-mc.starty)/2+10;

That’s it for Part Trois.

pom :asian:

The complete code and source:

// General constants
anglespeed = Math.PI/24;
perspective = 300;
tilt = 5;
maxClip=150;
xcenter=200;
maxHeight=300;

// Function declaration
function rotate(){
	this.angle+=anglespeed;
	var a=Math.sin(this.angle)*this.radius;
	var b=Math.cos(this.angle)*this.radius;
	var scalar=perspective/(b+perspective);
	this._x=a*scalar + xcenter;
	this._y=b*scalar/tilt+this.starty;
	this._xscale=this._yscale=scalar*100;	
}

// Duplicate and make it move!
for (var i=0;i < maxClip;i++){
	mc=dot.duplicateMovieClip("dot"+i,i);
	mc.angle=random(360);
	mc.starty=random(maxHeight);
	mc.radius=(maxHeight-mc.starty)/2+10;
	mc.onEnterFrame=rotate;
}

dot._visible=0;

pom :asian:

But where are the letters, will you ask me. Well, they’re coming now…

1. The idea
We are going to change our dot movie clip into a movie clip containing an empty dynamic textbox, and when we duplicate our clip, we will assign a random letter to the textbox. Pretty simple on the paper :slight_smile:

2. Setting up the clip
Edit the dot movie clip. Delete the dot graphic, and replace it with a dynamic textbox, large enough to contain a letter. Set the size to something like 16, choose a nice font, and a color that you will see (it depends on the color of your background…).

Give the textbox the instance name letter. Now go back to the main scene.

Finally, embed your font.

3. Assigning the letter to the clip
First we’ll see how to make a tornado with nothing but “i” 's. The for loop becomes

for (var i=0;i < maxClip;i++){
	mc=dot.duplicateMovieClip("dot"+i,i);
	**mc.letter.text="i";**
	mc.angle=random(360);
	mc.starty=random(maxHeight);
	mc.radius=(maxHeight-mc.starty)/2+10;
	mc.onEnterFrame=rotate;
}

Pretty neat, hue?

4. Add a bit of randomness…
Now this is a trick I took from Thoriphes and his Cycling Letters tutorial. First you need to define another constant, as string that contains all the letters we can choose from

myLetters=new String ("abcdefghijklmnopqrstuvwxyz");

Then we need to change the line we’ve added in the loop

mc.letter.text=myLetters.charAt(random(myLetters.length-1));

It looks complicated but it’s not. [FONT=courier new]charAt[/FONT] returns the caracter of the considered string at the specified index. Our index is [FONT=courier new]random(myLetter.length-1)[/FONT], that is to say a random number from the first to the last caracter of our String.

And that’s it for the 4th part of this tutorial.

pom :asian:

Complete code and source:

// General constants
anglespeed = Math.PI/24;
perspective = 300;
tilt = 5;
maxClip=150;
xcenter=200;
maxHeight=300;
myLetters=new String ("abcdefghijklmnopqrstuvwxyz");

// Function declaration
function rotate(){
	this.angle+=anglespeed;
	var a=Math.sin(this.angle)*this.radius;
	var b=Math.cos(this.angle)*this.radius;
	var scalar=perspective/(b+perspective);
	this._x=a*scalar + xcenter;
	this._y=b*scalar/tilt+this.starty;
	this._xscale=this._yscale=scalar*100;	
}

// Duplicate and make it move!
for (var i=0;i < maxClip;i++){
	mc=dot.duplicateMovieClip("dot"+i,i);
	mc.letter.text=myLetters.charAt(random(myLetters.length-1));
	mc.angle=random(360);
	mc.starty=random(maxHeight);
	mc.radius=(maxHeight-mc.starty)/2+10;
	mc.onEnterFrame=rotate;
}

dot._visible=0;

pom :asian:

Well, I don’t know what to add now. I just a few improvements, nothing very important:[list][]The shape is not linear, but curved
[
]There are more particles at the top of the twister than at its bottom[/list]Here’s the code

// General constants
anglespeed = Math.PI/48;
perspective = 300;
tilt=5;
maxClip=120;
xcenter=200;
maxHeight=300;
myLetters=new String ("abcdefghijklmnopqrstuvwxyz");

// Function declaration
function rotate(){
	this.angle+=anglespeed;
	var a=Math.sin(this.angle)*this.radius;
	var b=Math.cos(this.angle)*this.radius;
	var scalar=perspective/(b+perspective);
	this._x=a*scalar + xcenter;
	this._y=b*scalar/tilt+this.starty;
	this._xscale=this._yscale=scalar*100;
	this._alpha=scalar*100;
}

// Duplicate and make it move!
for (var i=0;i < maxClip;i++){
	mc=dot.duplicateMovieClip("dot"+i,i);
	mc.letter.text=myLetters.charAt(random(myLetters.length-1));
	mc.angle=random(360);
	**mc.starty=random(maxHeight)*Math.random()+50;
	mc.radius=(maxHeight-mc.starty)*(maxHeight-mc.starty)/400+10;**
	mc.onEnterFrame=rotate;
}

dot._visible=0;

I hope you like the effect, and that you didn’t have too many problems along the way. :slight_smile:

pom :asian:

That tornado thingy is really cool… you should make it an official tutorial…

nice pom :slight_smile:

Thanks! But the guy I did it for doesn’t have MX :stuck_out_tongue:
And yeah, I think the next step would be to make a real tutorial, but I don’t have the courage to open my HTML editor…

pom :asian:

hehe yeah html editors arse scary…hehe. which one u use? dreamweavers not that bad :slight_smile:

That is some excellent code Ilyas.

Dreamweaver is not bad at all. I almost gave up on learning Javascript until I found Dreamweaver. It is a pain doing that in notepad, which was my previous source for an HTML editor.

I love how Dreamweaver color codes everything to make things easier to find.

One program I did not like was Go Live (I believe that is Adobes version of Dreamweaver), I found it to be horrible, cluttered and a pain to use. Well it was easy for me since I code everything instead of using WYSIWYG, but it was a pain trying to find the things for everyone else.

Well, I use phpEdit… Which is probably why it’s so painful :slight_smile: I don’t know, I never liked HTML, I never trusted it. It is too easily obscurated and totally unreadable.

And no money for DreamWeaver I’m afraid :frowning:

pom :asian:

I love HTML. Fun and functional. Although it is being replaced by XML so I am starting to learn that now. Since I already know HTML XML is breezing by pretty quickly.

Yeah, Dreamweaver is quite a pricey program. You can probably go to www.downloads.com and run a search for HTML Editors in their downloads section. It might turn up some nifty editors that are better that phpEdit (but I don’t know how phpEdit works so I can’t say if it is good or not).

I still have to get back to learning DHTML. I started learning it, but never “finished” because it is extremely confusing to me sometimes. What I do when that happens is take a break for a while and when I come back I am refreshed and ready to learn.

HTML is very simple to learn Ilyas. You should try learning it. It is a handy tool indeed. If you need any help with it you can ask me. I am pretty sure I will have an answer for you.

i dont really mind it…well not with dreamweaver anyway :D. but yeah i only know a bit of html… Well one thing…at least ur not using Front Page. thats just too scary for me :frowning:

A friend of mine uses Front Page. He said it is the best program he has ever used. I still think Dreamweaver is though. I don’t know what I would do without Dreamweaver.

Granted, Frontpage can do different things like VML and stuff, but who ever actually uses that stuff?

Thanks for the offer Lost, and I do know the minimal required about HTML, but I don’t think I want to go any further. Actually, seeing what you did on your site with Javascript made me hesitate a bit, but I think I’ll stick to Flash :stuck_out_tongue:

OK people, now the My God, How am I going to do that with Flash 5 tutorial.

1. What we need to change
The great thing about Flash MX is that you can duplicate a movie clip and then tell it to do whatever you want it to do. You can’t do that with Flash 5. So the behaviour of the clip must be defined when you duplicate the clip.

2. What you need
We’re going to do the effect directly with letters, it will be quicker. For a bit of explanations, you can refer to the MX part.[list][]A movie clip (press Ctrl+F8 )[]Put your clip on the scene. Give it the instance name dot[]Inside that movie clip a dynamic textbox with the variable name letter.[]Create a new layer in your movie and call it code. It will hold the code :-\ [/list]

That’s it for the first step. All the rest is code.

pom :asian:

Code for the dot:

onClipEvent (load){
	angle=random(360);
	maxHeight=300;
	starty=random(maxHeight)*Math.random()+50;
	radius=(maxHeight-starty)*(maxHeight-starty)/400+10;
	myLetters=new String ("abcdefghijklmnopqrstuvwxyz");
	letter=myLetters.charAt(random(myLetters.length-1));

	function rotate(){
		angle+=_root.anglespeed;
		var a=Math.sin(angle)*radius;
		var b=Math.cos(angle)*radius;
		var scalar=_root.perspective/(b+_root.perspective);
		this._x=a*scalar + _root.centerx;
		this._y=b*scalar/_root.tilt+starty;
		this._xscale=this._yscale=scalar*100;
		this._alpha=90*scalar;
	}
}
onClipEvent (enterFrame){
	rotate();
}

Contrary to Flash MX, I define the variables directly in the clip, in the load handler. But they’re exactly the same.

The rotate function is EXACTLY identical.

Then I make the clip rotate in the enterFrame handler.

pom :asian: