Scripted Animation for the Butterfly Assignment (Conditionals)
Revised and corrected 9-25-03, 3:00 PM
Here are the scripts, demonstrated in class on September 25, that perform
Lingo-based animation for the butterfly in Part 3 of "How to Lingo" (Conditionals).
The basic idea here is to use scripted control of sprite position (locH and locV) in place of keyframe animation.
Once you introduce a random variable, the butterfly's movement becomes unpredictable,
taking us from classic animation, where sequence is predetermined, to simulation, where sequence is random or emergent.
The script actually comes in two parts. The smaller part is a startup script that
sets initial values for some variables. This is written as the main movie script:
on startMovie
global beat, xDir, yDir
beat = 0
xDir = "right"
yDir = "up"
end startMovie
We're using three variables at this point; we'll add a few more later. The variable called beat is used to count beats of the butterfly's wings--you'll
see how this works below. The variables xDir and yDir indicate the direction of motion in the X (horizontal) and Y (vertical) axes. We start them out
arbitrarily as "right" and "left," though you could give them the opposite values.
(It might even occur to you to choose their initial values at random.)
I use strings for these variables. You could use numbers (1,0) or the logical values
true and false, but I think it's easier to remember
what's going on when you use meaningful strings.
Finally note that all three of our variables are named in a global statement. This allows us to use these variables in scripts attached to another object, in our case one of the butterfly cast members.
This is called adjusting the scope of the variables. We need to scope our variables in this
way because cast members don't accept startMovie handlers, and because
the variables must be given values before the animation script is run.
The second part of the project is the animation script itself, which is written as a cast member script for one of the butterfly graphics.
You'll note that we've assumed the cast member is to be inserted in Channel 2 of the
score--that is, this image sits above the other butterfly image in the stacking
order of the Director screen. Here's the script:
on enterFrame
-- declare global variables
global beat, xDir, yDir
-- bump up the beat count by one
beat = beat+1
-- if the beat is 2, hide this sprite, reset beat to 0
if beat=2 then
sprite(2).visible = false
beat = 0
else
-- if the beat is 1, make the sprite visible
sprite(2).visible = true
end if
-- the following statements control horizontal movement
if xDir = "right" then
sprite(2).locH = sprite(2).locH+4
else
sprite(2).locH = sprite(2).locH-4
end if
-- the following statements control vertical movement
if yDir = "down" then
sprite(2).locV = sprite(2).locV+4
else
sprite(2).locV = sprite(2).locV-4
end if
-- if we've gone too far left or right, reverse direction
if sprite(2).locH > 390 then
xDir = "left"
end if
if sprite(2).locH < 10 then
xDir = "right"
end if
-- if we've gone too far up or down, reverse direction
if sprite(2).locV > 230 then
yDir = "up"
end if
if sprite(2).locV < 10 then
yDir = "down"
end if
end
Let's go through the script section by section.
First we use the counter variable beat to keep track of a two-beat
rhythm. The variable is set up with a value of 0. We add one to this value each time,
so on the first pass beat is 1 and on the second pass it is 2.
If beat evaluates to 2, we turn the sprite invisible and reset
beat to 0, so that when we increment its value on the next pass it will
be 1 and the cycle will start again.
If you think about this code, you'll see that it substitutes nicely for the
mod 2 statement in the keyframe version of the assignment.
We can't use that technique here because our movie only has a single frame!
The next two sections of the script control horizontal and vertical movement.
In each case we ask for the current value of the relevant control variable,
either xDir or yDir. We add to the current
location property of the sprite if moving down or right and subtract
if moving up or left.
If we left the script at this point, our butterfly would quickly fly off the
edge of the screen. So we add four conditional statements that kick in when
our sprite exceeds certain limits.
When a limit is exceeded in any direction, the relevant direction variable
is reset to its opposite value, sending the sprite back toward the middle of things.
Note that these limits are set 10 pixels
back from the absolute edges of the screen (and you may also notice that we're
using different screen dimensions than in the class assignment). This is an aesthetic detail,
so that the butterfly doesn't disappear completely from view at any point.
There are only two more details needed to make all this work.
So far we've only scripted one sprite, but there are two in the works.
We handle the other butterfly image in the simplest way possible, attaching
this script to its cast member:
on enterFrame
sprite(1).locH = sprite(2).locH
sprite(1).locV = sprite(2).locV
end enterFrame
Now the underlying sprite automatically matches the horizontal and vertical
position of the sprite above it.
We also have to do something about repetition.
As a behavior script for the first frame, we add an exitFrame handler
with a go to the frame instruction. Now our movie endlessly
repeats its one and only frame, and the butterfly flutters eternally from
one edge of the screen to another. This is either deep existential
truth, or a poor excuse for a screensaver.
Suppose we want our project to be a legitimate screensaver. Such programs
protect displays from burn-in by lighting up their pixels in a random
or unpredictable sequence. In other words, we need our butterfly to behave
less like a program and more like, well, a bug.
We can achieve this goal by taking out the hard-wired amount of change
in each direction (originally 4 pixels) and substituting some random
variables. So we rewrite the motion-control section of the main script thus:
-- the following statements control
-- horizontal movement
if xDir = "right" then
sprite(2).locH = sprite(2).locH + hChange
else
sprite(2).locH = sprite(2).locH - hChange
end if
We do the same with the vertical section, using the variable vChange.
These variables need to be given initial values, so we add them to the global statement in the main movie script. Then we add two more lines to this script:
hChange = 4 + random(8)
vChange = 4 + random(8)
The statement random(8) evaluates to a random whole number
between 1 and 8. (You can pass other maximum values to this function for a larger
range.) Since we don't want movement of the sprite to be less than 4 pixels at any point (an arbitrary decision), we add this random increment to 4. Thus the amount of motion is somewhere
between 5 and 12 pixels.
What we've written so far will cause the motion of the butterfly to vary each time the
movie is started up. But that's not our final objective. We want the butterfly to
alter its behavior while the movie is running. We could do this with a timer, but
since our code already recognizes collision with the screen boundaries, we can use
those collisions as an occasion for change.
In the if conditions where we change xDir
and yDir, we insert the two lines we added to the setup script:
hChange = 4 + random(8)
vChange = 4 + random(8)
This isn't the most elegant way to handle things; it would be better to break these
instructions out into a function. But since we haven't discussed
functions yet, we'll stick with the less elegant method.
You can see this code in action at the top of the page.
The source file, scriptedButterfly.dir, is available in
the directory mult201 on the FTP server
crow.ubalt.edu.