Procedural Animation
Most designers have their wake up moment with programming Flash when they finally
understand the power of onEnterFrame() to be interactive in a way
that is impossible with timeline based tweens. The Flash metaphor is one of a movie,
with a master timeline that always run and every individual MovieClip being capable
of having its own internal timeline running in parallel as well. In fact, you really
can't get around it. A timeline is tightly coupled to every MovieClip, even if that
timeline is only one frame long. But harnessing the constant and repetitive power
of code attached to this onEnterFrame event gives you unprecedented
and dynamic control that leaves sequential animations in the dust.
When using onEnterFrame() to control multiple animations on stage,
you will usually first decide if you want one onEnterFrame() on
the primary KeyFrame controlling all the "dumb" MovieClips, or if you want each
MovieClip to be "smart" and calculate its own motion. Often, either solution would
work so it may come down to developer preference. So let's look at both techniques
and see how to duplicate them in Silverlight.
Duplicating a Single onEnterFrame() Manager
Here is a sample Flash movie that controls its particles with a single onEnterFrame()
on the first KeyFrame.
As with our previous Flash examples, this single frame project has a dot
movieClip exported for ActionScript in the Library and the following
code on the main stage:
So to duplicate this in Silverlight, we prepare our environment the same way we
did in the Animation Lesson. Create a Silverlight Project
with Web Hosting option named Lesson03_a, and
prepare Page.xaml.
The Page.xaml modifications include:
- Convert the default Grid tag to a Canvas tag with
desired background color of #333333;
- Set the Width and Height of the User Control tag
in Page.xaml to match the SWF: 600 x 300.
- Add a Loaded event handler to the User Control tag, automatically
creating the function definition in the code behind file Page.xaml.cs
Page.xaml should now look like this:
Create a new User Control class for our Silverlight Project called
Dot.xaml. It will be identical to the Dot.xaml
we created in Lesson 02, only we don't need the Storyboard this time. After you
remove the Width and Height values of Dot.xaml's
User Control tag, don't forget to switch LayoutRoot
to be a Canvas instead of the default Grid and
remove its background color.
Adding the Ellipse shape for the particle, Dot.xaml
should look like this:
Press F7 to get to the Dot.xaml.cs code behind
file. As we learned in the Animation Lesson, we're going to add the X and
Y public properties to our User Control to make positioning them
easier. We also need some public variables for the velocity, but we can't just add
new properties on the fly like we did with vx and vy in our ActionScript 1.0 code
above (and like Silverlight's C#, ActionScript 3.0
frowns on this as well). So these variables should also be defined ahead of time
in our code behind file.
Therefore, our modified Dot.xaml.cs file now looks like this:
Now that our visuals have been defined and the User Control prepared, we can get
to the main chunk of code, duplicating the onEnterFrame() functionality
in Silverlight. Open Page.xaml.cs and go to the Loaded()
event handler we added from XAML when we first created this project.
We'll do this in two steps. First I'll show you the basic structure of a repeating
Storyboard loop, and then we'll put in the animation logic specific to this project.
To duplicate an onEnterFrame() type structure in Silverlight, you
can do it like this:
Line 17 defines the Storyboard object and we can initialize it in our Loaded()
handler as shown in Line 26. We add a Completed event which our Storyboard will
call after this empty Storyboard is done. Since an empty Storyboard has no parameters
or targets or duration defined, when we tell it to Begin() on Line
29, it will fire its Completed event one frame later. So to have
this logic fire repeatedly, simply put another Begin() request
inside the completed event. Also note, before we can use the Storyboard object,
we need to add it to our current User Control's Resources collection. You can
add the Storyboard and give it a resource name at the same time.
Now that we have an empty Storyboard acting as an onEnterFrame,
let's add our dots in the Loaded event:
And put our animation logic in the Storyboard's Completed event
like this:
And we're done. Notice that we can use a foreach command if we're
sure that the only children in our parent canvas are of the correct type. That's
why in the subsequent lessons in which we handle dynamic elements like this, we
will create an empty Canvas tag in our XAML to act as a dedicated
parent. Also, Silverlight operates at a default 60 fps versus the
Flash default of 12 fps, so we reduced the amount of drift we added
to our velocity variables in Line 47 and 48. Notice the notation for creating and
using random numbers is also different, though the range principle is the same.
In both Flash and Silverlight, if you subtract a random number up to a specific
value (n) from another random number up to that same value (n),
you create a negative n to positive n result set range. So in
Line 47 and Line 48, we assign a decimal value from -1.0 to +1.0 for the vx
and vy properties of each dot. These velocity values are added
to their current position every frame. We slow down these particles gradually with
some friction in Lines 61 and 62.
Here is the Silverlight result:
Having Each Element Control Its own Behavior
The above Flash example worked well because there was a predefined number of MovieClips
we could reference in our single animation loop. But there are other times when
we want each element to be added or removed independently over a longer period of
time, making a single looping function harder to implement. For those types of animations,
it's sometimes easier to put the onEnterFrame behavior on each
movieClip directly.
So if we changed our first Flash example to act more like a particle emitter by
replacing its current code with this:
We get a Flash movie that looks like this:
Instead of one onEnterFrame() event on the primary KeyFrame controlling
all the movieClips, each movieClip has its onEnterFrame() event
defined at the point of creation.
For our Silverlight rebuild, we no longer need the Storyboard object on Page.xaml.cs
since we will move it directly into our Dot.xaml.cs User Control.
But as we did in the Flash code above, we need to add a MouseMove
event handler to Page.xaml.cs.
There are no changes in our Dot.xaml mark up, and Dot.xaml.cs
has the same X, Y, vx, and
vy properties as in the previous lesson. But we will add a Storyboard
and count variable to the file so that each dot User Control can
define its own animation behavior like so:
This makes it easier to see the biggest conceptual difference between using an
onEnterFrame in Flash and a Storyboard Completed event
in Silverlight. In Flash, the onEnterFrame event will loop indefinitely
until you explicitly stop the behavior. On the other hand, the Storyboard Completed
event fires only once, so you need to explicitly restart the storyboard if you want
the handler to loop once per frame. By using a local count variable, though,
we an programmatically monitor a kill condition once we want the User Control to
remove itself.
Here is the Silverlight rebuild:
The choice to use a single function that loops through all animated elements on
screen and adjusts them individually or to have each visual element update itself
is a choice usually decided by weighing animation objectives and learned coding
behavior. But regardless of how you like to solve the problem in Flash, using a
Storyboard Completed event gives you a similar development framework to translate
your animation logic into Silverlight.
I want to point out, though, that being able to easily and quickly iterate through
a Canvas.Children collection and pull out strongly typed User Controls as shown
above makes the difference between the two approaches more one of personal choice.
Instead of using an eval() lookup or looping through an unpredictable list of unique
names in Flash, pulling each child out strongly typed can make things much smoother.
Now that we have covered some of the basics of mouse events, adding User Controls
dynamically, and creating animation code with Storyboard objects, lets start putting
it all together and adding some images.