Note: The code and Maya file are available on GitHub.
The Robin Animator V1.0 is a Maya plugin written in Python for animation prototyping. It can be used to generate basic procedural animations of little bird characters. These animations can then be exported for your games, rendered in your films or can serve as reference for more complex animations.
The question behind this project was to see whether we can create complex bird animations using simple movement components. This can be linked to emergence theory and subsumption architecture. The former talks about how a complex system is greater than the sum of its parts, while the latter shows how apparently intelligent looking behaviour can arise from a set of simple, separate component behaviours. In other words, complex character animation CAN be the result of simple movements working together! In our case, the component behaviours link to the way each body part moves and tend to act independently from each other.
I chose to focus on little bird characters, robins, to be more precise. The reason behind this is that I’m fascinated by how these little creatures move. Their speed seems to be in a different time frame from ours, due to their minute proportions. After looking at robins in the real world for a while, I decided to approximate their movement with a geometric prototype model.
Geometry and Movement
The geometric body parts link to the movement components that our robin displays. The following list shows the link between the two.
- The Head
- Geometry: Sphere and cone
- Movement: Shake (Rotate Y), Nod (Rotate Z)
- The Torso
- Geometry: Sphere scaled along Y axis
- Movement: Bend (Rotate Z) – Moves with Feet
- Geometry: Flattened spheres
- Movement: Lift (Rotate X)
- Geometry: Extruded cube
- Movement: Wag (Rotate Y), Lift (Rotate Z)
- Geometry: Modified cubes
- Movement: Bend (Rotate Z) – Moves with Torso
The robin’s movement is controlled by the RobinCTRL, a circle at the base of the character. The added attributes inside of it (eg. Lift Tail, Wag Tail etc.) are connected to the corresponding rotation fields for each geometric component of the character. These rotation fields usually have a minimum and maximum rotation limit to avoid self-intersections.
The main rule behind the rotation of any character component is a sine wave:
Where R is the rotation angle, A is the amplitude, S is the speed and is the angle linked to the current frame. The amplitude and speed can be set from the graphical user interface for each character component. The current frame is usually the one being considered for the addition of a key. To better understand the process, let us have a look at the GUI and the Python code behind it.
The GUI and the Code Behind It
The GUI has the following components:
- Reset Robin button
- Clears all the key frames of the animation
- Animation Start Frame
- Sets the start frame for any animation component
- Animation End Frame
- Sets the end frame for any animation component
- Component tabs
- Feet control the hopping movement
- Torso controls the bending of the torso
- Wings controls the flapping of the wings
- Head controls the shaking and nodding of the head
- Tail controls the wagging and lifting of the tail
Each tab usually has fields for setting up the frames per movement, the amplitude and speed. The frames per movement refers to the number of frames necessary to perform that action once. A hop taking place over 10 frames is faster than a hop over 20 frames for example. Speed can be used to tweak this effect of course.
In the case of the Feet tab, once these settings are typed into the fields, the user can press the Hop button, which calls the following method.
#Head nodding animation def createNodHeadAnimation(): robinCtrl = cmds.select('RobinCTRL', r=True) getAnimationStart() getAnimationEnd() getNodHeadFrames() getNodHeadAmplitude() getNodHeadSpeed() flip = 1 for i in range(animationStart, animationEnd, nodHeadFrames): if mirrorNodHead: flip = -flip for j in range(0, nodHeadFrames, 1): if (i+j < animationEnd): teta = j*pi/nodHeadFrames headRotation = flip * nodHeadAmplitude * math.sin(nodHeadSpeed * teta) if headRotation > 90.0: headRotation = 90.0 cmds.setAttr('RobinCTRL.NodHead', headRotation) cmds.setKeyframe('RobinCTRL', attribute='NodHead', t=i+j ) else: break
The RobinCTRL circle is first selected. Then the animation start and end frame values are extracted from the GUI. Next getNodHeadFrames(), getNodHeadAmplitude(), getNodHeadSpeed() extract the frames per hop, amplitude and speed values from the GUI. The flip parameter is a boolean which decides whether the movement should be symmetric or not (ie. hopping up and down, rather than hopping up and then jumping to a down pose briskly).
The two for loops that follow travel through the frames of animation and set a keyframe at every step. The inner loop is the one that creates the individual hopping movement, while the outer loop makes sure all the frames between the start and end frames are covered. The angle, which controls the point on the sine wave we’re currently at, goes from 0 to in nodHeadFrames steps. This is the parameter set by the getNodHeadFrames() method. The last two lines from the inner for loop set the calculated headRotation in the NodHead field of the RobinCTRL circle controller and add a keyframe to this new value.
Similar steps can be seen in the remaining movement component tabs. Individual methods were written for each tab, but I believe they can be reduced considerably as the current code is repetitive. For future work, it would be nice to introduce techniques for creating animation sequences (eg. hop for 30 frames, stop, look around etc.). Also, saving parameter settings would be useful for recreating popular animations like flying or whatever the user enjoyed doing.
Please have a play with the code (link to GitHub code and Maya file) and tell me what you think! Thank you!