Breaking It Down
Each of Mr. Eyes' expressions is a collection of linear
parametric animations. Taken separately, they are just like those
in the linear parametric demos except a sw3D property
is being animated rather than a sprite property. No difference
algorithm-wise.
The task to accomplish here is setting up numerous parametric
animations and scheduling them to run at certain times.
These are best accomplished with object-oriented techniques.
There are many ways to structure this
program. I'll show how I did it in this demo and why, with its strengths
and weaknesses. You'll need to be familiar with the basics of OO
Lingo and parametric animation. Handlers
are also called methods in this section.
One Animation Unit
Each animation, such as the tilt of an eyebrow or reddening of the eye
whites, is a single parametric animation. It is defined
by its driver, bias, and animator function. In this demo the driver, bias
and animator parts are programmed to work as separate objects:
Once these objects are set up they act as a unit, independent
from other code being executed. You can see the code that sets these objects
up in the objectChain() method of the "mr. eyes" script.
An advantage of programming each part as a separate object
is that they are interchangeable. You can write different
types of driver scripts, and as long as they call the
bias object's bias() method they can be used as a driver. Same goes for
the bias object, which needs to have a bias() method
and call the animator object's animate() method. And an animator
script just needs an animate() method.
When different scripts (classes)
are written to function in a similar way, they are said to implement a
common interface in object-oriented terminology.
In this demo there is just one driver script and one bias
script, but a variety of animator scripts for the different properties
animated.
Driver Object
All the animations are driven by time, so the driver is the familiar eTime
from the parametric demos. It is customized as an object as follows:
adds itself to the actorlist so it
receives a stepframe call every frame
waits for the start time of the animation
for the duration of the animation, generates
a value for p and passes it to the bias object's
bias() method
when the animation is finished, removes itself from
the actorlist
Bias Object
There is one type of bias object, and it provides bias types in,
out, both, and none, and a bias magnitude. Whenever
its bias() method is called, it biases p
and passes it to the animator object's animate() method.
Animator Object
There is a different animator script for each type of property
animated in the movie, and they all use a linear animator
function (except for the angry vibration which is random). The animator
scripts are almost identical, but for the different property they set.
All of them have an animate() method so they can be used
interchangeably in the object "chain" shown
above.
Each animator script accepts as an argument the object
whose property it is to animate. For rotation and position this object
is the transform object of any 3D node. Other objects
are shader.ambient for color and the resource
object for animating the start and end angles of the sphere.
This is one of those advantages of using objects—whatever
object is passed to the animator, it will animate the property of that
object. For example, whichever node's transform is passed to the animRot
animator, that node in the 3D model will be rotated by the animator.
Setting Up the Objects
Setting these three objects up for an animation requires that each be
instantiated and given an object
reference for the next object in the chain. The driver needs a reference
to the bias object so it can call its bias()
method, and the bias object needs a reference to the animator
object so it can call its animate() method.
A reference is passed as an argument like other types of variables.
In the demo, the code that sets these objects up is in
the objectChain() method of the "mr. eyes"
script.
Managing the Objects
One facial expression for Mr. Eyes requires many separate animations,
each of which has one object "chain" as shown above. After those
object chains are created, they function automatically. However, it is
useful to keep track of them for various reasons. For
example, if you wanted to cut an animation short which would require somehow
stopping each of the object chains currently active.
In this demo, the object chains are kept track of so that
the mrEyes object can tell if any animations are currently active.
If any are, no other facial expressions are allowed to start.
The mrEyes object keeps track of the object chains using
a list called driverList. Each time it creates a new
animation, it adds the driver object reference to driverList. In addition,
it passes a driverList reference to each driver object
created. When the driver object is finished with its animation, it removes
itself from the driverList with the command driverList.deleteOne(me).
When all the active animations are finished, the drivers will have removed
themselves from driverList and it will be empty.
A Few Improvements Manager Object
All the object chains for one facial expression are created when the user
clicks on one of the buttons, which takes up memory and computation time
while they wait to start.
A separate script can be written to do
the managing. To make a new facial expression, the mrEyes
object would pass a list defining the expression to the manager object.
The manager object would monitor the start times and create each object
chain only when it was ready to start. The eTime script would be modified
so that it didn't wait for a start time, but started animating immediately.
In addition, the facial expressions could be queued.
When the manager object receives the information for a new facial expression,
it could add it to a list so that when the current expression is finished
the next could be started. As the demo works now, new facial expressions
are ignored if another is still happening.
Single Bias Object The bias functions are generic for all the animations, so that only
one bias object is really needed. With just one bias object, the driver
would call animator() directly, like this:
objAnim.animate(objBias.bias(#both, p))
objBias would be global so that all drivers use the same
one. The type of bias would need to be passed to the driver so it can
use it when calling the bias function.
Things To Try
There are various things to try with this demo, depending on how far you
want to go. You could:
make up new facial expressions using the code
already in place.
define new object chains for motions not used yet in
the demo.
write new animator scripts for properties not animated
yet.
strip out all the code except for the 3D world setup
and make your own object architecture.
If you do something new with the demo
and wouldn't mind showing it, submit it for the
Idea Swap. Feel free to change the design
elements or make your own interface if you'd like (can remove the
logos).