For
this exercise, we are going to work on a different
kind of programmatic
animation: a
self-generating or recursive animation.
Many "new masters of Flash," from Jared
Tarbell and
Robert Penner to Joshua Davis, have used this
form to create eye-popping work.
Recursive animation
is intriguing because of its minimalist simplicity.A complex form can be animated
from just
a few lines of code, and infinite variations can be created just by
changing a variable or two. Once you get the
basic concept, creating programmatic animations
can quickly become addictive! Personally, I've
spent a lot of time on programmatic animation,
and you can see the results throughout my work.
Take a look at Epigenesis to
see an entire project built with programmatic animations.
The term recursive means that the action of an object is directed
at itself,
or a copy of itself. Let's build a quick example
in Flash.
Open a new ActionScript 2.0 Flash file, draw a 50 x 50 square, and convert it a Movie Clip. In the Convert to Symbol dialog, name the symbol "squareMC" and then click Advanced—in the new options that are open to you, check Export for ActionScript to set up its Linkage.
Linkage is what allows us to pull a symbol from the Library while our Flash movie is playing, and put it on the Stage, rather than putting it onstage while we're working in the FLA. This gives us some pretty powerful advantages, since we can add instances of a symbol whenever we need to. When you check the Export for ActionScript box, Flash will automatically opt to export your symbol in the first frame (which means we have access to it as soon as the first frame is loaded up), and give you the default Linkage ID (which is the same as the symbol name, though it isn't required to be for your own projects).
|
Setting up Linkage (using "Export for ActionScript") lets you use the attachMovie() function to put instances on the Stage at runtime, so you don't have to put them all on there ahead of time. |
Click OK to finish converting your shape to a symbol, and then name the instance on the stage "sqMC" and change its opacity (alpha) to 50%.
Now double-click the squareMC to enter that Movie Clip and edit it. Create an "actions" layer, and add this code in the Actions panel:
this.attachMovie("squareMC","sqMC",1);
// The attachMovie() function needs three arguments, in order:
// the Linkage ID of the symbol you want to put on the Stage
// the instance name of the new instance you're creating
// the depth at which to place the instance
sqMC._rotation+=18;
Whatever you do, though, don't test your movie just yet, or you'll cause Flash to bog down or crash! attachMovie() is recursively referring to itself, causing an infinite loop of attaching new instances squareMC inside each instance of squareMC. Each time we add a new copy of squareMC, it runs its own copy of the code we just wrote—the code that adds a new copy of squareMC inside that one. If we let this go unchecked, Flash will try for a while to render the incredibly huge movie, with a literally infinite number of nested squareMC instances, and then finally give you this dialog to escape the infinite loop:
|
Flash will eventually
display a dialog to help you exit an
infinite loop. |
To avoid this, we need to limit the number of times that the recursive squareMC code will recurse. To do this, we can set a variable that determines the number of shapes we want to appear onstage, and then set up an conditional ("if") statement to check it if we've hit our limit. If we haven't, we let the latest squareMC call a new one; if we've topped off, then the latest squareMC won't call another one up inside itself.
Create an "actions" layer on the main timeline, and add this code to the Actions panel:
var shapeTotal = 20; // How many instances we want
var shapeNum = 0; // Our instance counter
Go back inside your squareMC symbol, and revise the code in its "actions" layer to read as follows:
if (_level0.shapeNum < _level0.shapeTotal) {
this.attachMovie("squareMC","sqMC",1);
sqMC._rotation += 18;
_level0.shapeNum++; // Increase our counter by one!
}
You may notice that this is a different way of doing the same thing we've previously done using our "for" loops (both for loops and if statements are classified as flow control code—code that determines when or if to execute other code).
Also note that this code makes use of the _level0 reference to access the variables from the main timeline. This is crucial because of the nesting of the Movie Clips, one inside each other. This way, they all refer to the same starting point. You can see how the nesting fits together by using Flash's debugger to test your movie (Debug > Debug Movie) and watch the object list, which will end up looking similar to this:
|
A truly
recursive structure, with each MC nested
inside its parent. It's
cool looking in the Debugger, but hard to
target the nested MCs with ActionScript! |
This is the basic concept behind a recursive
animation. Neat, huh?
Creating Variations
Once you've got the groundwork set, completing your recursive animation project is mostly adding, changing, and fine-tuning variables. For example, we can add a variable declaring how many degrees to rotate each recursive squareMC by. It's a common and efficient coding practice to put any useful or frequently used variables right up front in your program, so you can change them as needed without digging too deep into your code.
In our case, "right up front" means we can add this to the main timeline's "actions" layer:
var degreesRot = 5;
In our code on the "actions" layer inside the squareMC symbol, we can change it up a bit. This bit of code:
// We'll change this...
sqMC._rotation += 18;
...can change to this bit of code:
// ...to this!
sqMC._rotation += _level0.degreesRot;
Now, change the shapeTotal in
the main actions layer from 20 to 60 or so, and
test your movie.
So far, we've been manually keeping things symmetrical: with 20 shapes, we rotated each by 18 degrees; with 60 shapes, we rotate each by 5 degrees. Either way, the instances made a full 360-degree circle around the center. As coders, though, it's our job to work out how to "automate" whatever we can. There's a clear solution here: our degreesRot variable should depend on how many shapes we have!
To make sure your recursive animation is symmetrical no matter how many shapes you choose, revise the code on your main timeline by changing the assigned value of degreesRot:
var degreesRot = 360/shapeTotal;
Note: Make sure that the line assigning the value degreesRot comes after the line creating shapeTotal. Remember that, unless we control the order of code (using flow control!), it will operate in order, from beginning to end. This means that if you switch the order of these two variable assignments, Flash won't know what number you're trying to assign to degreesRot, because shapeTotal won't exist yet.
Try assigning different values to shapeTotal, and watch how the results change when you test your movie. Less is often more, but you may yield interesting results by pushing large numbers. You can create a multitude of different and surprising variations by changing the width and height of the shape, the shape's XY position inside the squareMC Movie Clip, changing the shape itself, or adding an external graphic clip, like in the first exercise. You could recursively change the transparency, using the code to set the _alpha property for each MC. In a programmatic animation with multiple overlapping shapes, the use of transparency reveals the complex visual structure that is created by overlapping partially transparent shapes. The possibilities are truly endless!
Exploring Symmetry
Symmetry is everywhere in the universe. Snowflakes, galaxies, and humans all have symmetry embedded in their form. Artists and craftsmen have used symmetry in their designs for centuries. There is something compelling about symmetrical forms, so as a designer, you can add to your toolkit by exploring how to use symmetry in your work. A good place to start is to read about the different kinds of symmetry at Wikipedia.
Programmatic animation is an ideal method for employing symmetry. Using the _rotation property allows us to explore and create compositions of rotational symmetry. There is an order in symmetry generated by the regular spacing of elements. This regular spacing is achieved in rotational symmetry by dividing the number of elements into 360, the number of degrees in a full circle. That's how this line works:
var degreesRot = 360/shapeTotal;
It evenly spaces out your recursive shapes along the full rotation of a circle.
Now, it's just a matter of cleaning up the animation, and finding which variables lead to the results you find most aesthetically-pleasing. First, if you've lowered the opacity on your squareMC, you may notice an extra box that is sticking out, a little more opaque than the others. This is the original squareMC on the stage, which is not included in the "count" since it's already there. (The code counts out instances without considering that first one, so the last one "overlaps" it.) We want to remove that, but we'll need to add code that puts our recursive animation on the Stage without it.
First, delete the instance from your Stage. Remember that you'll still have the symbol in your Library, completely unaffected.
In the actions layer of the main timeline, we could add this new code:
// This is a good start, though it won't change much...
_level0.attachMovie("squareMC", "sqMC",1);
sqMC._x=400;
sqMC._y=300;
While this method could work fine, it doesn't solve our problem: we'll still have that first, uncounted, instance on the Stage, only it's put there by code. It can also cause problems due to the nesting, which makes it difficult to target MCs and set their relative position. For example, in our original recursive animation, each MC's _rotation is 5, but the effect is cumulative: each MC is also being rotated inside its parent MC, which is also being rotated inside its parent MC, and so on up the ladder. Furthermore, it's not considered good practice to hide code inside Movie Clips.
To clear all this up, and code our program properly, we'll write a function that will handle this animation from the main timeline. The first step is to disable the old recursive code by deleting it, or commenting it out.
Go ahead and do that now: delete the code inside your squareMC symbol (and the code just above, if you bothered trying it out). Alternately, if you'd like to keep the code around for reference, you can comment it out, by placing two slashes // at the beginning of each line. A double-slash turns the rest of a line of code into a comment, which will be ignored by Flash, but remain in your code for you (or other coders) to read. (You can actually put a double-slash after some code, which puts a comment on the same line as code.)
It is more efficient to place all code on the main timeline. Nested Movie Clips can be the source of much frustration when trying to get MC instances to talk to one another. To avoid this problem, we can use a for loop to create the animation pattern all at once, and use some nifty ActionScript to encapsulate it inside a single Movie Clip. We can then move the Movie Clip around and do anything we want with it.
The following code uses some more specific (and proper) ActionScript 2.0 syntax, so it's a bit different than the previous method we've explored which uses eval() to create references to a Movie Clip. Add a new function to the variables you've already got defined, so that your main timeline actions window contains this code:
var shapeTotal = 20;
var degreesRot = 360/shapeTotal;
function makeRecursion() {
_level0.createEmptyMovieClip("recursMC",100);
recursMC._x = 400;
recursMC._y = 300;
for(i=0; i<shapeTotal; i++) {
var mcName = "sqMC"+i; // Name changes each time!
var mc = recursMC.attachMovie("squareMC",mcName,i);
// Depth changes each time!
mc._rotation += degreesRot * i;
// Rotation changes each time!
mc._alpha = 40;
}
}
makeRecursion(); // Call the function
The line that might stick out, if you're not yet familiar with it, is this one:
var mc = recursMC.attachMovie("squareMC",mcName,i);
We're using this rather than the previous technique we employed, which made use of the eval() function:
// The obsolete version!
this.attachMovie("squareMC",mcName,i);
mc = eval(mcName);
Our new technique is actually a much cleaner method, and not too complicated. Most functions return a value when they are called, even if it's just a true or false Boolean value to let you know if the function finished successfully.
The attachMovie() function always returns a specific object reference to the Movie Clip instance that it just put on the Stage; all we're doing is declaring a new variable called "mc" to which we assign that value—the one returned by attachMovie()—thereby allowing us to refer to the new Movie Clip simply by referring to the "mc" variable itself.
In short: variables can equal a lot more than just a number or a string (of letters and words) or a Boolean (true/false) value; they can equal entire code objects, functions, or anything! (Remember our arrays from the previous exercise?) Once we have that easy, shorthand reference, we can control the properties of the new Movie Clip instance by using this "mc" variable in its place.
So our new approach uses methods to dynamically name the new instances and set their properties as they are attached to the Stage. Note that all of the new instances are attached inside a new Movie Clip called "recursMC". They're all attached to that MC, and therefore exist inside it. Rather than continually nested instances, we have instances that are all "siblings," sharing the same parent.
As each one is attached, the rotation is calculated by multiplying our set number by the current value of i, which reflects the position of the instance in the sequence of rotation. (Do the math yourself and you'll see: the first one isn't rotated at all, the second is rotated a bit, the third is rotated twice as much, and so on). This dynamic naming will create as many instances as called for without having to statically program and define the actions of every Movie Clip.
Now that the function is developed independently of the original MC symbol, new power and flexibility is opening up to us. To make this function even more flexible and powerful, we can pass variables to the function at will, which will "unhook" the function from the timeline variables:
function makeRecursion(shapeTotal,xPos,yPos,mcAlpha) {
var degreesRot = 360/shapeTotal;
_level0.createEmptyMovieClip("recursMC",100);
recursMC._x = xPos;
recursMC._y = yPos;
for(i=0; i<shapeTotal; i++) {
var mcName = "sqMC"+i;
var mc = recursMC.attachMovie("squareMC",mcName,i);
mc._rotation += degreesRot * i;
mc._alpha = mcAlpha;
}
}
makeRecursion(20,400,300,40);
With this setup, you can get rid of the variables we were previously declaring on our main timeline. We won't need them anymore, since the function simply accepts all the arguments it needs independently. It's now possible to add many different forms to the stage, each with its own unique properties. The final step is to wrap the recursive instances into individually identifiable MCs. This is important, because then it's possible to add more individual MCs with recursive instances inside them. Here's how
our new version of the function will look:
function makeRecursion(shapeTotal,idNum,xPos,yPos,mcAlpha) {
var degreesRot = 360/shapeTotal;
var rMC = _level0.createEmptyMovieClip("recursMC"+idNum,idNum);
rMC._x = xPos;
rMC._y = yPos;
for(i=0; i<shapeTotal; i++) {
var mcName = "sqMC"+i;
var mc = rMC.attachMovie("squareMC",mcName,i);
mc._rotation += degreesRot * i;
mc._alpha = mcAlpha;
}
}
// Make one recursion (ID number is 1)
makeRecursion(20,1,200,300,40);
// Make another recursion (ID number is 2)
makeRecursion(20,2,600,300,30);
Every time you call makeRecursion() now, you can include a unique ID number (the second argument) for the newest recursive animation, which will be used to create the instance name for the MC that contains all of your recursive instances.
Notice that we're using the same trick with "rMC" that we used for "mc"; like attachMovie(), the createEmptyMovieClip function returns a direct reference to the new Movie Clip instance it creates. (Make sure you spot the difference in our new "attachMovie" line above!)
If you debug the movie now, you'll see that the structure looks more like this:
|
Each
Movie Clip section is properly encapsulated
inside a single MC. |
Ideas for Unlimited
Variations
Now that you have the basic code
structure of the iterated animation, you have
a structure for creating infinite variations.
Here are some ideas for ways to extend this concept
into new and interesting pieces:
|
|
|
|
Changing the Movie Clip properties in our "for" loop:
Similar
to the first exercise, you can use ActionScript
inside the for loop to change the properties
of the Movie Clip. Try using ActionScript
to change the values of various properties, including:
_width, _height, _alpha, _xscale, _yscale
Changing
the container MC properties:
In many cases,
it's less processor intensive, and therefore
more desirable, to manipulate the "container"
MC directly instead of changing each instance
inside of that MC individually. For example, if you'd
like to make your animation spin, the
best approach is to create an onEnterFrame() event
that continually increases the _rotation property
of the container MC (not the instances inside it!). You can add this code right
before the last closing bracket
of the makeRecursion() function, after the "for" loop closes, but before the function closes:
} // End of the "for" loop!
rMC.onEnterFrame = function() {
this._rotation += 10;
}
} // End of the makeRecursion function!
You can also
change the _width, _height, _alpha, _xscale, _yscale properties
of the containing MC, if you like—simply use the easy "rMC" reference we've created for it.
Changing
the graphic content:
We started with
using a generic square, but the graphic
content of your Movie Clip can be anything.
Try different primitive shapes
(circle, triangle, polygon), or experiment
with lines and irregular
shapes. Remember from the previous exercise that the position of
the graphic shape relative to the registration
mark can create very interesting variations
of the
animation, so try varying that as well.
There is truly no limit to the variations
that
can be created! |
|
|
|
|
Take a look at two
interesting versions of this animation. The shapes
you see below are the starting shapes. Click
on each one to see how it is animated.
|
|
Click
the square and circle shapes above to see
them in engaging recursive
animations. |
Within this
seemingly basic animation code lies a wealth
of graphic
and animation potential.
Remembering Exercise One, I'd like you to
spend time working, playing and testing different
ideas,
using this code as your base. Also, keep your
eyes open in the real world for similar structures.
Symmetry is a basic developmental principle
both in art and in nature, which means that there
is a lot to draw from in the world and to
emulate with animation!
Exercise Submission
Working with the codebase
above, add your own ideas to create your own
programmatic animations. Since it's easy to
create many variations in a short period of
time, be sure to use your aesthetic eye, and
view your iterations with a critical eye, in
order to select a final compelling animation.
When you're
satisfied that you've creating something really
good, please
submit your SWF animation along with a brief
statement about your inspiration and modifications,
plus any
ideas for future animations. Take your time,
have fun, and surprise me!