Actionscript 3 Basic Tutorial – Easiest way to do easy stuff (Starfield class)

actionscript 3, algorithms, BitmapData, classes, fun, Particles, perlinNoise

I don’t usually post about two lines of code snippets, or about workarounds to that damn bug or about basic tutorials for beginners. I honestly haven’t enough time and will to post on a daily basis. Usually I use this blog to mantain me focused on a single project I find interesting and take it to the end (not a really working however :-P ).

Logic comes first, code is the last step

So what about the title? What’s the the perfect beginner but somehow satisfying tutorial?
Answer snow/rain/starfield and you got it, and here we have a basic starfield class tutorial.

However the main reason I am posting this is not about the 30-40 code lines involved in rendering a random starfield but make you stop a moment and think about a possible easier way to do what you are doing.
I’ll try to be clearer: this morning I was working on a flash animation and just wanted to place a background starfield on my bucholic landscape view. So I quickly created the class but was missing something… off course a starfield is not cool if stars don’t twinkle (winkle, scintillate
shine… vary in light intensity etc.). So I started to recall the milions of experiments like this I did years ago in Actionscript 1 and ok… the first answer was something like:

  • Generate every star as a sprite/movieclip (possibily in a main container)
  • list the instance in some array, dictionary (whatever you prefer)
  • to make ‘em twinkle, at every frame, do a for loop on every instance and vary its alpha with some some increment/decrement/sine calculation (o simply randomize it if you are lazy)

or you can have a Star class togheter with the Starfield class and let the Star class think about everything, so it creates its EnterFrame event to start twinkling when added (but it’s always a bad idea to have hundres of EnterFrame events, when you can have only one in the main class.

But…

Flash teach me that there are always many options to get to the same results (that is what I really like most about flash developing), AND… that bitmap filters and blend modes are a gift to hardcore developers with tremendous possibilities.

So.. do we really want a subtle background effect to keep 50% of the CPU? I don’t.

perlinNoise is not the solutions… is how you use it

The main idea here is simply use a perlinNoise, which we know is a tremendous versatile function.
But how to use perlinNoise to let our stars twinkle?

One first option could be to create a bitmapData, apply perlinNoise with a moving offset at every frame, do a for loop on every pixel and using getPixel grab the color of that pixel and use it to change the corresponding star instance alpha.

What could we gain from this? NOTHING… simply you don’t have to do variations math in other way, but now we still have the for loop, but with even more code in it, and remember this loop must be running at every frame. We passed from 50% CPU use to 75%!

So what the solution? Easy: we keep perlinNoise, but we use masks and blendModes.

Just look at this simple code:

var sprite:Sprite=new Sprite();
addChild(sprite);
for(var i:uint=0; i<10; i++) {
	for(var j:uint=0; j<10; j++) {
		sprite.graphics.beginFill(0xffffff);
		sprite.graphics.drawCircle(25+j*50, 25+i*50, 20);
	}
}

which produces this result.

now look at this, we add a background bitmap with a perlinNoise function applied before of the previous code:

var offs:Array=[new Point(0,0), new Point(0,0)];
var bd:BitmapData=new BitmapData(500, 500, false);
bd.perlinNoise(50, 50, 2, 0, false, true, 7, true, offs);
var bmp:Bitmap=new Bitmap(bd);
addChild(bmp);

which produces this result (I made circles half transparent to better understand the effect.

Ok, but here we are speaking about writing simple code, but even about CPU performances... such a big perlinNoise changing at every frame would kill every CPU, but do we really need THAT detail?
We absolutely don't need a per-circle gradient, we simply need that every circle has its own full value.

To reduce perlinNoise detail and CPU use is very easy, we try replacing the previous code with this:

var offs:Array=[new Point(0,0), new Point(0,0)];
var bd:BitmapData=new BitmapData(10, 10, false);
bd.perlinNoise(2, 2, 2, 0, false, true, 7, true, offs);
var bmp:Bitmap=new Bitmap(bd);
addChild(bmp);
bmp.scaleX=bmp.scaleY=50;

which produces this result

See what happened? We generate a tiny 10x10 bitmapData and the simply scale the relative bitmap to a corresponding factor. Try to comment the last line and you will see actual size of the bitmap.
This time the result is almost perfect... really insignificant CPU usage and every circle has its own full value.

So here comes the trick, now we simply use the circles as mask for the bitmap:

bmp.mask=sprite;

which produces this result

and set the bitmap blendMode to BlendMode.SCREEN (or BlendMode.HARDLIGHT)

bmp.blendMode=BlendMode.SCREEN;

which produces this result

Ok, now to animate we simply change the offset of the perlinNoise function. In the previous code I use two octaves, so that we can animate the second one. If we animate the first octave we'll have an effect that look like a scrolling and not like a variation.

So we add this:

addEventListener(Event.ENTER_FRAME, onframe);
function onframe(event:Event):void {
	offs[1].x+=.02;
	offs[1].y+=.05;
	bd.perlinNoise(2, 2, 2, 0, false, true, 7, true, offs);
}

which produces this result

Now it's just need to randomize size and positions, and here is the full code:

var offs:Array=[new Point(0,0), new Point(0,0)];
var bd:BitmapData=new BitmapData(10, 10, false);
bd.perlinNoise(2, 2, 2, 0, false, true, 7, true, offs);
var bmp:Bitmap=new Bitmap(bd);
addChild(bmp);
bmp.scaleX=bmp.scaleY=50;

var sprite:Sprite=new Sprite();
addChild(sprite);
for(var i:uint=0; i<1000; i++) {
	sprite.graphics.beginFill(0xffffff);
	sprite.graphics.drawCircle(Math.random()*500, Math.random()*500, Math.random()*1.5);
}
bmp.mask=sprite;
bmp.blendMode=BlendMode.SCREEN;

addEventListener(Event.ENTER_FRAME, onframe);
function onframe(event:Event):void {
	offs[1].x+=.02;
	offs[1].y+=.05;
	bd.perlinNoise(2, 2, 2, 0, false, true, 7, true, offs);
}

You have to play with the size of the perlinNoise and the offset speed to change the intensity of the effect. You could even notice that a 10x10 bitmapData could reveal too low detail for what you need so you can try to change its size to 40x40 (remember to change bitmap scale accordingly).

Starfield class

I prepared a class with everything included and a parameter to change the bitmapdata detail and relative bitmap size automatically.

So simply download Starfield class.
The constructor is this:

public function Starfield(pw:Number, ph:Number, pcount:uint, pminSize:Number=1, pmaxSize:Number=2, pperlinNoiseSize:Number=5, pspeedX:Number=.1, pspeedY:Number=.05, pbdSize:uint=30) {

and here a sample usage:

import com.oaxoa.fx.Starfield;

var ss:Starfield=new Starfield(500, 500, 1000, .1, 1.5, 2);
addChild(ss);
ss.startTwinkle();

//ss.debug=true;

which produces this final result (2-3% CPU usage with 1000 stars).

The last commented line set the class in debug mode, which is quite usefull to fine tune the effect.

Steps summary

  1. Step #1
  2. Step #2
  3. Step #3
  4. Step #4
  5. Step #5
  6. Step #6
  7. Final result

Final comments

So this is the end of this kilometric post. As usally took me 15 minutes to write the code and 3 hours to write the post, prepare examples and explain the main idea.

What I like in this post is not the argauble result (LOL) but the main concepts:

  1. Logic comes first, code is just an addition to get thing done
  2. You can always achive the same results in many ways (for this simple stuff we could walk 10+ different paths
  3. With bitmaps tricks and blendModes life can be really easier and your CPU cooler
  4. Blogging is a heavy duty :-D

I clearly exaggerated the CPU usage difference... don't think that with the first method described you could really reach 50% of load, but it's surely heavier to manage and more code to write.

As usual if you found this useful or interesting, just drop me two lines in the comment.

p.s.: what do you think of this new theme I found? I will customize it a little when have some time.

18 Comments

18 Comments

  1. Kribba  •  Oct 8, 2008 @11:14 pm

    Looks great, thanks! :)

  2. Adam  •  Oct 9, 2008 @12:33 am

    I would never have thought to do something like this, so thank you very much for sharing your ideas. Hopefully you can continue to do tutorials like these where you evaluate a few different options and ideas before settling upon your final choice.

  3. jvc  •  Oct 9, 2008 @2:49 am

    very smart. Also love part on the blog\s philosophy on posts.

    this wordpress theme is a but crazy tho with the dual columns of categories/archives

  4. Pierluigi Pesenti  •  Oct 9, 2008 @9:56 am

    Thanks guys, I’m happy you appriciate :)
    I am thinking many other similar posts.

    About the theme, I just needed to twek it and create a dynamic sidebar for the right column. Now is less weird without duplicates.

  5. dVyper  •  Oct 9, 2008 @10:40 am

    That is an absolutely brilliant effect with such simple code! Love it!

    Don’t like the theme as much as the last one though…

  6. subblue  •  Oct 9, 2008 @8:10 pm

    A nice subtle effect well explained.

  7. jack  •  Nov 28, 2008 @12:16 pm

    beautiful.
    But I can’t find the way to resize it (for instance to a full page)

    thx again for your excellent contribution

  8. Oliver  •  Dec 18, 2008 @11:03 am

    I’ll have to study this more
    Thanks for posting!

  9. ricardo  •  Mar 20, 2009 @5:16 pm

    man, you do really great fx classes … i´m very impressed by the simplicity and the many options … all well explained … thanks very very much !

  10. Kimchi  •  Apr 7, 2009 @4:22 pm

    Thank you very much for taking your time on this. You explained it in very good detail.

  11. Kelso  •  Apr 22, 2009 @9:38 pm

    Wow, I love the tutorial and the progression of your presentation.

    I am new to AS3 and still having difficulty with the whole .as things.

    I have your Starfield class, but I am unsure how to actually make it work in Adobe Flash AS3?
    Do I make a new AS3 flash document and make the class = Starfield ?

    Any help would be appreciated…

    Thanks

  12. Pierluigi Pesenti  •  Apr 22, 2009 @11:30 pm

    Classes needs to be imported and instanciated.
    The easiest scenario is the following:

    Save your FLA and paste the .as file into the same directory, and write this code:

    import Starfield;

    var ss:Starfield=new Starfield(500, 500, 1000, .1, 1.5, 2);
    addChild(ss);
    ss.startTwinkle();

    or if you wish you can make a “as3 classes” dir that you will use to keep all the useful classes you can find and link it to every fla that uses it in your “publish settings>Flash”.
    Then writing something like:

    import com.oaxoa.fx.Starfield;

    means that you must have this path to the class file:
    as3 classes\com\oaxoa\fx\Starfield.as

    Hope I helped

  13. Pierluigi Pesenti  •  Apr 23, 2009 @3:37 pm

    Replying to Jeff Bodell who wrote me a mail to which my server doesn’t want I reply to (LOL):

    Don’t worry =)

    I assume you are working on an empty FLA (no layers or frames in the
    timeline).
    Click on the only frame you have and press F9. Et voilà, here’s the
    actionscript editor… there you have to write/paste your code.

    If you have a complex fla with may layers, I suggest to create a new layer
    and call it “actions” and keep it empty, just use it to paste as3 code.

    Feel free to write me back again if something’s not clear (better if you comment here).
    Byez

  14. Clemente G  •  May 13, 2009 @11:56 pm

    Great post, I’m really diggin your work. Keep it up.

  15. Moamen  •  Jul 15, 2009 @10:54 am

    Thanks alot for this tutorial ..
    Realy i benefited greatly for more than things , because I’m beginner actionScript 3.0 user .

    thanks again ,,,

  16. aminusia  •  Sep 8, 2009 @4:41 am

    Thanks for those nice code and philosophy, will see shat they could do in my next project :D . And over all of that, the theme is rock! Hope this will be exactly 2 lines.

  17. Mark  •  Oct 22, 2009 @5:35 pm

    Thanks for the tutorial! I’m trying to make the stars to move from right to left and then remove them with a remove.EnterFrame once they arrive to x=0 and by the other hand keep creating stars that keep moving to the left. Can you help me with this cycle?
    Thanks

  18. Pierluigi Pesenti  •  Oct 22, 2009 @6:10 pm

    With the method explained you don’t have single stars, but a single Sprite with a lot of circles inside, so you have to move the whole instance “sprite”. With this approach you should always have two different starfield sprites that are one next to the other on the x axis, when the first is completely out of the screen and the second is completely in, simply shift the x of the first from -YourWith, to +Your Width (completely out on the left side, to completely out on the right side), and so on. With two panels you can give the illusion of continuous scrolling.

    To duplicate everything it would be sure better to wrap the code inside a class, so you could simply instance it twice. Hope this can help.

Leave a Reply

Allowed tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>