Flash Tutorial - Create a dynamic star-field using attachMovie

ActionScript 2 goodness

published: October 9, 2009

One of the most useful features of Flash is the ability to attach MovieClips to the stage. This allows you to create relatively complex on-screen animations without dragging and dropping lots of objects onto the stage, and instead using a little ActionScript.

In the following tutorial we'll create a dynamic star-field background effect. You'll be able to customize the number of stars, speed, and angle. In a follow-up I'll show you how to use these to make a little game. Even better we'll be coding most of it using classes, so you'll be able to use it in a project with around only 3 lines of code.

The best way to get you interested is to show you the end result, so have a look just below. You can download the source code and all the rest from the end of the post.

The finished product

Dynamic Flash starfield

Setting up your Movie

First create a new Flash File (ActionScript 2), set the dimensions to 550x400px and the background color to black (#000000).

Create a new graphic in your library, and name it "Star Shape". Inside the Star Shape graphic draw a white circle (no outline) and set the properties in the properties inspector to width=40, height=40, x=-20, y=-20 (so the circle is centered on the origin).

Star Shape Graphic

Now create a new MovieClip in your library and name it "My Star". Drag the "Star Shape" graphic onto the first frame of the first layer and position it at x=0, y=0.

Right click (Cmd+click on the Mac) on the My Star MovieClip in your library and select Properties. Check the box next to 'Export for ActionScript', and enter the name "Star" as the ActionScript Export Identifier.

My Star properties - Export identifier

Now select File > Publish Settings from the toolbar and select the Flash tab; then press the settings button next to where it says ActionScript 2 to open the ActionScript settings dialog.

Publishing settings - ActionScript settings

Press the "+" button to add a new ClassPath and enter the location where you store your ActionScript classes, mine is "C:\ActionScript".

ActionScript ClassPath

If your not sure what all that means, it's kind of easy. You store all of the classes you download or create in a central directory, that way it's easy to access them and share them between projects. If you don't have a folder already go ahead and create one somewhere; then enter it's location into the ActionScript settings dialog.

All that done? Click OK on both dialogs to return to your Movie. Now hit File > New and create a new ActionScript file.

The Star class

First I'll give you the full code of the star class, then I'll explain it in details afterwards. Enter the following into the ActionScript class you just created

  1. class org.josephearl.anim.starfield.Star extends MovieClip {
  2.         private var starSize, xSpeed, ySpeed:Number; // Properties
  3.        
  4.         // Constructor
  5.         public function Star() {
  6.                 xSpeed = 0;
  7.                 ySpeed = 0;
  8.                 starSize = 1;
  9.         }
  10.        
  11.         // Set size of star
  12.         public function setSize(s:Number) {
  13.                 starSize = s;
  14.                
  15.                 updateSize();
  16.         }
  17.        
  18.         // Getters and Setters
  19.         public function set xVelocity(v:Number) {
  20.                 xSpeed = v;
  21.         }
  22.         public function set yVelocity(v:Number) {
  23.                 ySpeed = v;
  24.         }
  25.         public function get size():Number {
  26.                 return starSize;
  27.         }
  28.         public function get xVelocity():Number {
  29.                 return xSpeed;
  30.         }
  31.         public function get yVelocity():Number {
  32.                 return ySpeed;
  33.         }
  34.        
  35.         // Move this star
  36.         public function moveStar() {
  37.                 _x += xSpeed;
  38.                 _y += ySpeed;
  39.         }
  40.        
  41.         // Destroy this star by removing from the stage
  42.         public function destroy() {
  43.                 starSize = xSpeed = ySpeed = null;
  44.                 this.removeMovieClip();
  45.         }
  46.        
  47.         // Update the size of the star
  48.         private function updateSize() {
  49.                 this._width = starSize;
  50.                 this._height = starSize;
  51.         }
  52. }

Let's start of with the name of the class: org.josephearl.anim.starfield.Star. This is the fully qualified name of the class, the local name is "Star" and we will be saving it as "Star.as". So what's all the rest of it before that mean? The org.josephearl.anim.starfield. tells you what the folder the Class is in relative to your main ActionScript classes folder. If you chose "C:\ActipnScript\", then the Star.as file's full location will be C:\ActionScript\org\josephearl\anim\starfield\Star.as.

For your convenience the download contains both classes uses packaged in the right folders to just drag and drop into your ActionScript folder. If you don't want to do that, go ahead and make the folders, and then save the file as "Star.as" (without the quotes of course).

Phew, slightly convoluted; hopefully it makes sense though. So what did all that class mean?

Well firstly notice in the class name there's an extra bit after the name - extends MovieClip. This means that this class inherits all of the properties and method of the MovieClip object, which makes sense since we'll be applying it our Star. This will essentially give our Star MovieClip extra properties and methods that we define here.

Okay first we have all of the properties in the class, there's starSize, xSpeed and ySpeed:

  1.         private var starSize, xSpeed, ySpeed:Number; // Properties

Unsurprisingly they control the size of the star, it's horizontal and vertical speeds (well velocities really). Notice we use the private keyword when defining them - this means the user cannot directly get or set these properties: generally you should only let users interact with your classes in the way you define.

Next comes a part that every class must have - the constructor. This set's up the class, and has to have exactly the same name as your class. The constructor can take arguments, but in this case we'll be applying our class by setting the ActionScript class of a MovieClip rather than calling new Star(); so we wouldn't have the chance to pass it any arguments. Ours just sets the properties to some default values:

  1.         // Constructor
  2.         public function Star() {
  3.                 xSpeed = 0;
  4.                 ySpeed = 0;
  5.                 starSize = 1;
  6.         }

Next we have a function that let's the user set the size of the star. Well really it's two functions; the first one is public, and this lets the user the set the starSize property of the star. This function also makes use of another private method which updates the size of the star (so that the size of the star MovieClip matches the starSize property.

  1.         // Set size of star
  2.         public function setSize(s:Number) {
  3.                 starSize = s;
  4.                
  5.                 updateSize();
  6.         }
  7.        
  8.         // Update the size of the star
  9.         private function updateSize() {
  10.                 this._width = starSize;
  11.                 this._height = starSize;
  12.         }

Notice in the updateSize method that we set _x and _y properties even though we haven't defined them; this is of course because they are properties of the MovieClip class, which we're extending so we have direct access so.

Next we have getters and setters, these let the user change the properties of class (but of course only the ones you allow them to):

  1.         // Getters and Setters
  2.         public function set xVelocity(v:Number) {
  3.                 xSpeed = v;
  4.         }
  5.         public function set yVelocity(v:Number) {
  6.                 ySpeed = v;
  7.         }
  8.         public function get size():Number {
  9.                 return starSize;
  10.         }
  11.         public function get xVelocity():Number {
  12.                 return xSpeed;
  13.         }
  14.         public function get yVelocity():Number {
  15.                 return ySpeed;
  16.         }

You might be wondering how a getter or setter method is different to any other. It's simple: a getter or setter method behaves like a property. So in the above example, you'd call myStar.yVelocity = 20; to set the ySpeed property of the star, rather than myStar.yVelocity(20) as you would with a normal method. To get the ySpeed property you'd use var yVel = myStar.yVelocity;

An important point is that ySpeed is the internal name of the property - it's used internally in the class, but from outside the class you can't touch it directly. When using the class it would appear it had a yVelocity property, even though we really know it's just getting the variable we called ySpeed in the class.

Only two more functions to go in this class, first up is the moveStar method. This changes the star's _x and _y properties by it's speed:

  1.         // Move this star
  2.         public function moveStar() {
  3.                 _x += xSpeed;
  4.                 _y += ySpeed;
  5.         }

Finally we have the destroy method - this gets rid of the star by removing the star MovieClip from the stage.

  1.         // Destroy this star by removing from the stage
  2.         public function destroy() {
  3.                 starSize = xSpeed = ySpeed = null;
  4.                 this.removeMovieClip();
  5.         }

And that's all of the star class, this will let use manage all of our stars easily.

The Starfield class

This is the main class, and it's a little longer (199 lines) than the last. Create a new ActionScript file, then enter the following code:

  1. class org.josephearl.anim.Starfield {
  2.         private var speedMax, speedMin, moveAngle, numStars, sizeMin, sizeMax, animIntId, updateInt : Number;
  3.         private var clip : MovieClip;
  4.         private var idName : String;
  5.         private var isCreated, isAnimPlaying : Boolean;
  6.        
  7.         // Constructor
  8.         public function Starfield(target_mc:MovieClip,
  9.                                                           starIdName:String,
  10.                                                           totalStars:Number,
  11.                                                           minSpeed:Number,
  12.                                                           maxSpeed:Number,
  13.                                                           angle:Number,
  14.                                                           minSize:Number,
  15.                                                           maxSize:Number,
  16.                                                           uInt:Number) {
  17.                
  18.                 var args:Number = arguments.length;
  19.                 isCreated = false;
  20.                 isAnimPlaying = false;
  21.                 clip = target_mc;
  22.                 updateInt = 50;
  23.                 idName = starIdName;
  24.                
  25.                 numStars = 80;
  26.                 moveAngle = 0;
  27.                 speedMax = 40;
  28.                 speedMin = 10;
  29.                 sizeMin = 2;
  30.                 sizeMax = 4;
  31.                
  32.                 if (args > 2) numStars = totalStars;
  33.                 if (args > 3) moveAngle = (angle * (Math.PI/180)); //convert degrees to radians
  34.                 if (args > 4) speedMax = maxSpeed;
  35.                 if (args > 5) speedMin = minSpeed;
  36.                 if (args > 6) sizeMin = minSize;
  37.                 if (args > 7) sizeMax = maxSize;
  38.                 if (args > 8) updateInt = uInt
  39.         }
  40.        
  41.         public function create():Boolean {
  42.                 if (isCreated) return false;
  43.                
  44.                 var updateSecs = updateInt/1000;
  45.                
  46.                 for (var i:Number = 0; i < numStars; i++) {
  47.                         // Create stars with random positions
  48.                         var xPos:Number = Math.random() * Stage.width;
  49.                         var yPos:Number = Math.random() * Stage.height;
  50.                         var starVelocity:Object = getRandomVelocity(); // Get a random velocity
  51.                         // We use both sizeMin and sizeMax to to add extra randomness to the size
  52.                         var starSize:Number = (Math.random() * sizeMin) + (0.7 * (Math.random() * sizeMax));
  53.                        
  54.                         // Attach our stars with random x and y positions
  55.                         var starMc:MovieClip = clip.attachMovie(idName,
  56.                                                          "star" + i + "_mc",
  57.                                                          clip.getNextHighestDepth(),
  58.                                                          {_x:xPos, _y:yPos});
  59.        
  60.                         // Now set the properties of our star
  61.                         starMc.setSize(starSize);
  62.                         // Notice we multiply the star velocity be the update time in seconds,
  63.                         // this means that the visible velocity of the of the stars will be
  64.                         // independant of the update time we choose
  65.                         starMc.xVelocity = starVelocity.vx * updateSecs;
  66.                         starMc.yVelocity = starVelocity.vy * updateSecs;
  67.                 }
  68.                
  69.                 return true;
  70.         }
  71.        
  72.         public function setAngle(newAngle:Number) {
  73.                 moveAngle = (newAngle * (Math.PI/180)); // convert degrees to radians
  74.                
  75.                 updateStarVelocities();
  76.         }
  77.        
  78.         public function setSpeed(minSpeed:Number, maxSpeed:Number) {
  79.                 speedMin = minSpeed;
  80.                 speedMax = maxSpeed;
  81.                
  82.                 updateStarVelocities();
  83.         }
  84.        
  85.         private function updateStarVelocities() {
  86.                 var updateSecs = updateInt/1000;
  87.                 for (var i:Number = 0; i < numStars; i++) {
  88.                         var starMc:MovieClip = getStarClip(i);
  89.                         var starVelocity:Object = getRandomVelocity();
  90.                         starMc.xVelocity = starVelocity.vx * updateSecs;
  91.                         starMc.yVelocity = starVelocity.vy * updateSecs;
  92.                 }
  93.         }
  94.        
  95.         // Generate a random velocity for a star
  96.         private function getRandomVelocity():Object {
  97.                 var speedFactor:Number = speedMin + (Math.random() * (speedMax - speedMin));
  98.                 var xSpeed:Number = -Math.sin(moveAngle) * speedFactor;
  99.                 var ySpeed:Number = Math.cos(moveAngle) * speedFactor;
  100.                
  101.                 return {vx:xSpeed, vy:ySpeed};
  102.         }
  103.        
  104.         // Generate a randome position for star to start at
  105.         private function getRandomPosition():Object {
  106.                 // We construct a circle at the center of the stage
  107.                 // with a radius of length equal to the distance from the center of the stage to
  108.                 // one of the corners.
  109.                 var posCenter:Object = {x:Stage.width/2, y:Stage.height/2};
  110.                 var posRadius:Number = Math.sqrt(((Stage.width/2)*(Stage.width/2)) + ((Stage.height/2)*(Stage.height/2))) //ensure we get the max radius
  111.                
  112.                 // Then we generate a random angle which will be somewhere between -90
  113.                 // and +90 degrees to our field direction
  114.                 var randAngle = moveAngle + (Math.random() * Math.PI) - (Math.PI/2);
  115.                
  116.                 // At this angle we draw a line from the center to the
  117.                 // circumference of the circle and get the x and y from where they meet
  118.                 var xPos = posCenter.x + (Math.sin(randAngle) * posRadius);
  119.                 var yPos = posCenter.y - (Math.cos(randAngle) * posRadius);
  120.                
  121.                 // Now we ensure that the star won't be off stage
  122.                 if (xPos < 0) {
  123.                         xPos = 0;
  124.                 }
  125.                 else if (xPos > Stage.width) {
  126.                         xPos = Stage.width;
  127.                 }
  128.                
  129.                 if (yPos < 0) {
  130.                         yPos = 0;
  131.                 }
  132.                 else if (yPos > Stage.height) {
  133.                         yPos = Stage.height;
  134.                 }
  135.                
  136.                 return {x:xPos, y:yPos};
  137.         }
  138.        
  139.         // Check whether the star is off-stage or not
  140.         private function isOffstage(mc:MovieClip):Boolean {
  141.                 if (mc._x < 0) return true;
  142.                 if (mc._x > Stage.width) return true;
  143.                 if (mc._y < 0) return true;
  144.                 if (mc._y > Stage.height) return true;
  145.                 return false;
  146.         }
  147.        
  148.         // Return an instance of the star MovieClip specified by
  149.         // the number n
  150.         private function getStarClip(n:Number):MovieClip {
  151.                 return clip["star" + n + "_mc"];
  152.         }
  153.        
  154.         // Moves the animation one step forward
  155.         private function animationStep(instance) {
  156.                 instance.moveStars();
  157.         }
  158.        
  159.         // Moves all of our stars one step
  160.         private function moveStars() {
  161.                 for (var i:Number = 0; i < numStars; i++) {
  162.                         var starMc:MovieClip = getStarClip(i);
  163.                        
  164.                         if (isOffstage(starMc)) {
  165.                                 var starPos:Object = getRandomPosition();
  166.                                 starMc._x = starPos.x;
  167.                                 starMc._y = starPos.y;
  168.                         }
  169.                        
  170.                         starMc.moveStar();
  171.                 }
  172.         }
  173.        
  174.         // Start the starfield animation
  175.         public function playAnimation() {
  176.                 if (isAnimPlaying) return;
  177.                
  178.                 animIntId = setInterval(animationStep, updateInt, this);
  179.         }
  180.        
  181.         // Stop the starfield animtion
  182.         public function stopAnimation() {
  183.                 if (!isAnimPlaying) return;
  184.                
  185.                 clearInterval(animIntId);
  186.                 isAnimPlaying = false;
  187.         }
  188.        
  189.         // Stop the animation, destroy all the stars and clear our variables
  190.         public function destroy():Boolean {
  191.                 if (!isCreated) return false;
  192.                 stopAnimation();
  193.                
  194.                 for (var i:Number = 0; i < numStars; i++) {
  195.                         var starMc:MovieClip = getStarClip(i);
  196.                         starMc.destroy();
  197.                 }
  198.                
  199.                 speedMax = speedMin = moveAngle = numStars = sizeMin = sizeMax = animIntId = null;
  200.                 clip = null;
  201.                 isCreated, isAnimPlaying = null;
  202.                 return true;
  203.         }
  204. }

As always first we have the properties of our Starfield.

  1.         private var speedMax, speedMin, moveAngle, numStars, sizeMin, sizeMax, animIntId, updateInt : Number;
  2.         private var clip : MovieClip;
  3.         private var idName : String;
  4.         private var isCreated, isAnimPlaying : Boolean;

I'll give a quick explanation of the most important ones:

  • speedMin and speedMax control the minimum and maximum speed of the stars in the star-field
  • The moveAngle property determines the angle at which the stars in the field move at.
  • numStars is of course the number of stars in our star-field.
  • sizeMin and sizeMax control the sizes of the stars.
  • The clip property will store a reference to the MovieClip which will contain the star-field
  • updateInt this will be the interval that the screen is updated at, in milliseconds. A lower value results in a smoother animation (but it is more computationally intensive); conversely a higher value with result in a more jerky looking animation, but will require less system resources
  • The idName property will contain the ActionScript Export Identifier of the Star MovieClip we want to use in this star-field (this let's you have different fields with different stars)

Then comes the obligatory constructor; this time it takes some arguments which let the user set all of the important properties of the star field. Most of the arguments, except target_mc and starIdName, don't have to be specified and will use the default values defined in the constructor.

  1.         // Constructor
  2.         public function Starfield(target_mc:MovieClip,
  3.                                                           starIdName:String,
  4.                                                           totalStars:Number,
  5.                                                           minSpeed:Number,
  6.                                                           maxSpeed:Number,
  7.                                                           angle:Number,
  8.                                                           minSize:Number,
  9.                                                           maxSize:Number,
  10.                                                           uInt:Number) {
  11.                
  12.                 var args:Number = arguments.length;
  13.                 isCreated = false;
  14.                 isAnimPlaying = false;
  15.                 clip = target_mc;
  16.                 updateInt = 50;
  17.                 idName = starIdName;
  18.                
  19.                 numStars = 80;
  20.                 moveAngle = 0;
  21.                 speedMax = 40;
  22.                 speedMin = 10;
  23.                 sizeMin = 2;
  24.                 sizeMax = 4;
  25.                
  26.                 if (args > 2) numStars = totalStars;
  27.                 if (args > 3) moveAngle = (angle * (Math.PI/180)); //convert degrees to radians
  28.                 if (args > 4) speedMax = maxSpeed;
  29.                 if (args > 5) speedMin = minSpeed;
  30.                 if (args > 6) sizeMin = minSize;
  31.                 if (args > 7) sizeMax = maxSize;
  32.                 if (args > 8) updateInt = uInt
  33.         }

Then we have one of the most important methods of the class, the create method. Once a Starfield instance has been created, this will method will add of all the stars to the stage.

  1.         public function create():Boolean {
  2.                 if (isCreated) return false;
  3.                
  4.                 var updateSecs = updateInt/1000;
  5.                
  6.                 for (var i:Number = 0; i < numStars; i++) {
  7.                         // Create stars with random positions
  8.                         var xPos:Number = Math.random() * Stage.width;
  9.                         var yPos:Number = Math.random() * Stage.height;
  10.                         var starVelocity:Object = getRandomVelocity(); // Get a random velocity
  11.                         // We use both sizeMin and sizeMax to to add extra randomness to the size
  12.                         var starSize:Number = (Math.random() * sizeMin) + (0.7 * (Math.random() * sizeMax));
  13.                        
  14.                         // Attach our stars with random x and y positions
  15.                         var starMc:MovieClip = clip.attachMovie(idName,
  16.                                                          "star" + i + "_mc",
  17.                                                          clip.getNextHighestDepth(),
  18.                                                          {_x:xPos, _y:yPos});
  19.        
  20.                         // Now set the properties of our star
  21.                         starMc.setSize(starSize);
  22.                         // Notice we multiply the star velocity be the update time in seconds,
  23.                         // this means that the visible velocity of the of the stars will be
  24.                         // independant of the update time we choose
  25.                         starMc.xVelocity = starVelocity.vx * updateSecs;
  26.                         starMc.yVelocity = starVelocity.vy * updateSecs;
  27.                 }
  28.                
  29.                 return true;
  30.         }

Essentially this loops through the number of stars we have and each loop adds a star to the stage, giving it a random position. We add a new star to the stage using the MovieClip.attachMovie method.

To use the attachMovie method, you do something like the similar:

  1. my_mc.attachMovie(exportId:String, newName:String, attachLevel:Number, initObject:Object);
  • my_mc is the MovieClip that you want to attach your MovieClip to (ie it will be the parent of the attached MovieClip)
  • The exportId argument is the ActionScript Export Identifier of any MovieClip in your library.
  • newName will be the name of the MovieClip attached, e.g. "myNew_mc"
  • attachLevel is the depth of the MovieClip, usually use something like my_mc.getNextHighestDepth()
  • The initObj is an optional Object you can pass to initialize MovieClip variables. You might pass {_x:10, _y:50} which will set the MovieClips position to x=10, y=50 when it is attached.

Notice we use several methods of the Star class we created earlier in the create method.

  1.                         // Now set the properties of our star
  2.                         starMc.setSize(starSize);
  3.                         // Notice we multiply the star velocity be the update time in seconds,
  4.                         // this means that the visible velocity of the of the stars will be
  5.                         // independant of the update time we choose
  6.                         starMc.xVelocity = starVelocity.vx * updateSecs;
  7.                         starMc.yVelocity = starVelocity.vy * updateSecs;

We set the velocity of each individual star as a the velocity chosen multiplied by the update interval in seconds. This means that the visible speed is independent of the interval chosen.

Next we have two methods that change the angle and speed of the Starfield.

  1.         public function setAngle(newAngle:Number) {
  2.                 moveAngle = (newAngle * (Math.PI/180)); // convert degrees to radians
  3.                
  4.                 updateStarVelocities();
  5.         }
  6.        
  7.         public function setSpeed(minSpeed:Number, maxSpeed:Number) {
  8.                 speedMin = minSpeed;
  9.                 speedMax = maxSpeed;
  10.                
  11.                 updateStarVelocities();
  12.         }

These are pretty simple and just change the relevant properties of the Starfield. Both make use of a private method, updateStarVelocities, which passes the changes in speed and direction onto each individual star.

  1.         private function updateStarVelocities() {
  2.                 var updateSecs = updateInt/1000;
  3.                 for (var i:Number = 0; i < numStars; i++) {
  4.                         var starMc:MovieClip = getStarClip(i);
  5.                         var starVelocity:Object = getRandomVelocity();
  6.                         starMc.xVelocity = starVelocity.vx * updateSecs;
  7.                         starMc.yVelocity = starVelocity.vy * updateSecs;
  8.                 }
  9.         }

This just loops through all our stars and sets their velocities again, just like we did in the create method.

We then have another two private methods, getRandomVelocity and getRandomPosition. These are used by other methods to generate random velocities and positions for the individual stars. There's explanations as to how exactly this is calculated in the comments.

  1.         // Generate a random velocity for a star
  2.         private function getRandomVelocity():Object {
  3.                 var speedFactor:Number = speedMin + (Math.random() * (speedMax - speedMin));
  4.                 var xSpeed:Number = -Math.sin(moveAngle) * speedFactor;
  5.                 var ySpeed:Number = Math.cos(moveAngle) * speedFactor;
  6.                
  7.                 return {vx:xSpeed, vy:ySpeed};
  8.         }
  9.        
  10.         // Generate a randome position for star to start at
  11.         private function getRandomPosition():Object {
  12.                 // We construct a circle at the center of the stage
  13.                 // with a radius of length equal to the distance from the center of the stage to
  14.                 // one of the corners.
  15.                 var posCenter:Object = {x:Stage.width/2, y:Stage.height/2};
  16.                 var posRadius:Number = Math.sqrt(((Stage.width/2)*(Stage.width/2)) + ((Stage.height/2)*(Stage.height/2))) //ensure we get the max radius
  17.                
  18.                 // Then we generate a random angle which will be somewhere between -90
  19.                 // and +90 degrees to our field direction
  20.                 var randAngle = moveAngle + (Math.random() * Math.PI) - (Math.PI/2);
  21.                
  22.                 // At this angle we draw a line from the center to the
  23.                 // circumference of the circle and get the x and y from where they meet
  24.                 var xPos = posCenter.x + (Math.sin(randAngle) * posRadius);
  25.                 var yPos = posCenter.y - (Math.cos(randAngle) * posRadius);
  26.                
  27.                 // Now we ensure that the star won't be off stage
  28.                 if (xPos < 0) {
  29.                         xPos = 0;
  30.                 }
  31.                 else if (xPos > Stage.width) {
  32.                         xPos = Stage.width;
  33.                 }
  34.                
  35.                 if (yPos < 0) {
  36.                         yPos = 0;
  37.                 }
  38.                 else if (yPos > Stage.height) {
  39.                         yPos = Stage.height;
  40.                 }
  41.                
  42.                 return {x:xPos, y:yPos};
  43.         }

With our animation, we're going to place a load of stars on-screen in random positions, move them in the desired direction, and then once they pass off screen we want to move them to the other end of the screen and start them moving again. That way we don't need to keep creating and adding MovieClips all the time so it's more efficient. To do this we need to detect if a MovieClip is off-stage.

  1.         // Check whether the star is off-stage or not
  2.         private function isOffstage(mc:MovieClip):Boolean {
  3.                 if (mc._x < 0) return true;
  4.                 if (mc._x > Stage.width) return true;
  5.                 if (mc._y < 0) return true;
  6.                 if (mc._y > Stage.height) return true;
  7.                 return false;
  8.         }

We need to keep referencing our stars throughout this class, in order to move them and change their properties. The getStarClip function takes a number and returns the MovieClip of the star corresponding to the number.

  1.         private function getStarClip(n:Number):MovieClip {
  2.                 return clip["star" + n + "_mc"];
  3.         }

The animationStep doesn't actually do animation. It receives an instance of a Starfield, and calls the moveStars method of that instance. The reason we do it this way, is because of variable scope.

Variable scope

It's important to understand, so I'll go on a quick detour to explain why we do that. Feel free to skip this part if you like and get onto the rest of the code.

Firstly, we'll animate our star-field by calling the animation function at an interval. The animation function will move all of the stars on one step. To do this we use the setInterval global method, which allows you to call functions at regular intervals.

Calling something such as setInterval(animationFunction, 100) will call the animationFunction function every 100 milliseconds. If the animationFunction method references this, or needs to access other members of the class, it's important to understand what this is.

Normally you change a class property by doing something such as this.property = newValue. It's important to know that Flash lets you omit the this keyword, so even if you don't put it in, that's what is really going on.

In this particular case, because the animationFunction is called by the setInterval; the this keyword refers to the setInterval event that fires the function. Thus in order to access methods and properties of the class we can't just call myMethod(), because that's really doing this.myMethod() and myMethod isn't a method of the setInterval event that fires.

In order to make the animation function more readable, we write it as normal, and use a helper to function to call it so that the this keyword in the real animation function does refer to our class. We pass the helper function animationStep an instance of our Starfield, which then calls the real animation function using Starfield.moveStars(); and the this keyword in our moveStars method now does refer to our class, so we can access all of the properties and methods easily.

Back on track

Not much more code to go through! The moveStars method is up next, and all this does is loop through all of our stars. We check if the star is off-stage, if it is then we move it to a new position. Then we call each stars moveStar method (remember we made this earlier in our Star class).

  1.         private function moveStars() {
  2.                 for (var i:Number = 0; i < numStars; i++) {
  3.                         var starMc:MovieClip = getStarClip(i);
  4.                        
  5.                         if (isOffstage(starMc)) {
  6.                                 var starPos:Object = getRandomPosition();
  7.                                 starMc._x = starPos.x;
  8.                                 starMc._y = starPos.y;
  9.                         }
  10.                        
  11.                         starMc.moveStar();
  12.                 }
  13.         }

The moveStars and animationStep methods don't do anything on their own though, and both are private so they can't be accessed in our Movies. The next method solves this and stars the star-field animation playing:

  1.         public function playAnimation() {
  2.                 if (isAnimPlaying) return;
  3.                
  4.                 animIntId = setInterval(animationStep, updateInt, this);
  5.         }

We use the setInterval method to call our animationStep function at regular intervals. Notice we pass the animationStep method the current Starfield instance by passing it the this keyword.

The setInterval function returns a number, you can use this in conjunction with the clearInterval method to stop the interval. We use this make a method which stops the star-field animation.

  1.         public function stopAnimation() {
  2.                 if (!isAnimPlaying) return;
  3.                
  4.                 clearInterval(animIntId);
  5.                 isAnimPlaying = false;
  6.         }

Don't worry it's the last bit of code in this class now, the destroy method. This stops the animation and gets rid of all of the stars by calling their individual destroy destroy method that we defined in our Star class.

  1.         public function destroy():Boolean {
  2.                 if (!isCreated) return false;
  3.                 stopAnimation();
  4.                
  5.                 for (var i:Number = 0; i < numStars; i++) {
  6.                         var starMc:MovieClip = getStarClip(i);
  7.                         starMc.destroy();
  8.                 }
  9.                
  10.                 speedMax = speedMin = moveAngle = numStars = sizeMin = sizeMax = animIntId = null;
  11.                 clip = null;
  12.                 isCreated, isAnimPlaying = null;
  13.                 return true;
  14.         }

Save the file as "Starfield.as" in the "org\josephearl\anim\" folder (relative to your main ActionScript ClassPath.

And that's all of our classes done!

The Starfield animation

Now you'll see why we went to all that trouble of making those two classes, rather than just coding up a quick solution in our Movie using onEnterFrame or something similar.

Return to your Movie. Right-click on the "My Star" MovieClip in your library, select properties, and enter "org.josephearl.anim.starfield.Star" into the Class.

My Star properties - setting the MovieClip class

Hit OK, and return to your Movie. Now create a new MovieClip in your library, and give it the name "Starfield"; leave it empty. Return to the root timeline of your Movie, and drag the Starfield MovieClip from your library onto the Stage. Position it at x=0, y=0 and give it the instance name "starfield_mc". Now create a new layer in your root timeline, select the first frame, and open the Actions window, then enter the next 4 lines of code:

  1. import org.josephearl.anim.Starfield;
  2.  
  3. var myStarfield:Starfield = new Starfield(starfield_mc, "Star", 100, 40, 300, 0, 2, 4, 20);
  4. myStarfield.create();
  5. myStarfield.playAnimation();

First we import our Starfield class with the import org.josephearl.anim.Starfield; statement.

Next we create a new Starfield, 'myStarfield', which we pass the ActionScript Export Identifier of the My Star MovieClip in our library and the name of the blank MovieClip we dragged onto the stage. This will contain the Starfield. We also pass it the speed and size etc, and we set the angle to 0. We update our star field every 20 milliseconds (50FPS).

After that all we have to do is call the create method of our Starfield to make the stars, and then call the playAnimation method to start things off.

And that's it. Hit Ctrl+Enter to test your Movie.

Maybe you could add a spaceship to your Movie to make it look like it's moving through space (actually, I'll show you how to make one in the next installment).

Recap

Once you have the classes saved and made, here's how simple it is to add a Starfield to any Movie:

  1. Create a Star MovieClip in your library, give it an Export Identifier and attach the Star class to it.
  2. Create a blank MovieClip, drag it onto the stage and give it an instance name
  3. Import the Starfield class into your MovieClip
  4. Create a new instance of the Starfield class
  5. Call the create and playAnimation methods of your Starfield to get things going

Download files

Bear in mind that you'll need to change the Class Path in the ActionScript settings to get the FLA files to publish properly (and make sure you have the classes installed too).

In the next installment I'll show you how to draw the Spaceship, add some controls which will change the direction of the star field and spaceship; then finally we'll make our spaceship shoot some bullets (or lasers, take your pick).

Having problems? Liked this tutorial? Let me know what you thought in the comments section.

comments (0)     Page views 1450     left side of starright side of starleft side of starright side of starleft side of starright side of starleft side of starright side of starleft side of starright side of star 

« Previous entry | Next entry »

Services

WEB & FLASH DESIGN

  • Standards compliant CSS & XHTML web pages
  • Custom-built dynamic sites based on PHP/MySQL, e.g. blogs, CMS and more
  • Flash adverts, menu design and apps
  • PSD or PNG to XHTML/CSS
  • Flash Lite apps and games for mobile devices/cellphones including Nokia S60, Windows Mobile 6.1 and LG

 

Twitter updates

FOLLOW ME ON TWITTER

    "We can't solve problems by using the same kind of thinking we used when we created them." - Einstein on 31st Aug, 06:50:54

    Not buying another external WD drive although I do like the design. Recently had my third one fail. Glad I had other backups. Seagate? on 31st Aug, 06:41:00

    Looking for a new small yet powerful laptop/notebook. Narrowed it down to HP Envy 13 / HP Envy 14 / Asus U33Jc. Any advice? on 31st Aug, 06:21:37

    Must say the people at RGB co. / BirdGuides were all rather lovely. Shall hopefully be seeing some of them again soon. on 17th Aug, 14:05:11

    Finished watching Summer Heights High - rather funny I must say on 15th Aug, 14:20:43