Creating a Squash and Stretch Control
This tutorial is also available at pdipierro.com: Creating a Squash and Stretch Control.
Or visit Set Driven Key for more tips and tricks.
Introduction to Squash and Stretch
Building a stretch control for your character rig
Squash and Stretch is one of the most important classical principles of animation. (Perhaps after persistence of vision).
It is used to help sell the illusion of motion. In traditional, hand-drawn animation, squash and stretch is crucial for conveying action. In CG, it can be used more subtly to bolster character animation, or in an exaggerated fashion for a more stylized look, (see DreamWorks Madagascar).
Incorporating squash and stretch controls is not always easy, especially for very extreme deformations. But we can build a simple limb-stretching control that fits over a standard IK setup.
This tutorial assumes that you know the basics of creating expressions and custom attributes. If those are new to you, check out my Lean Control tutorial first. You will also need to either know the basics of creating a simple skeleton rig, or have one that you can modify. You can find a variety of free rigs to download on the web.
You can use whatever rig you want to start out, as we'll just be modifying it. There are three main things we're concerned with:
1. The shoulder joint, which I've named "LtShoulder" in my scene.
2. The elbow joint, "LtElbow"
3. A NURBS circle, or some other control object, that the IK handle is parented to. I've named it "LtArm."
Translating LtArm back and forth has the expected result on the IK arm. When LtArm is closer to the shoulder than the length of the arm, the arm bends at the elbow.
If LtArm is hyperextended, or further from the shoulder than the length of the arm, then the arm gets left behind as the control object moves out of reach.
Ok, let's build the control. We need to start off by creating a distance node. Go to Create > Measure Tools > Distance Tool.
Switch to a front view. Click once on the shoulder and a second time on the wrist to create the distance node.
You'll see two new locators in the scene. These are connected to the two inputs of the distance node. Point constrain one of them to LtShoulder, and the other to LtArm. Make sure that "maintain offset" is off. You want the locators to snap into place.
Ok, now we need to write an expression. Open up the expression editor (Window > Animation Editors > Expression Editor) and create a new expression.
We'll start by declaring some variables.
$distance = distanceDimensionShape1.distance ;
$initialDis = 5.72655 ;
$factor = $distance/$initialDis ;
We set the variable $distance equal to the output of our distance node. It represents the distance from the shoulder to LtArm. The value of $initialDis in my scene was 5.72655. Yours will be different. It should be whatever the initial return value of the distanceDimension node is when the arm is in the locked position. You'll see why this number in important in a minute.
We're going to use the variable $factor to scale the joints in the arm for the stretch effect. Distance divided by initial distance gives us the value we need. When the arm is in the locked position, distance over initial distance = 1. That number gets larger and smaller as you move LtArm in and out, giving us the appropriate scaler value to use for the stretch effect.
Ok, we have our variables. Now let's use them. The next lines of your expression should be:
LtShoulder.scaleX = $factor ;
LtElbow.scaleX = $factor ;
Click "create" to create the expression. Now when you move LtArm around the scene, the arm should stretch to meet it:
However, you'll notice a potential problem as well. The arm never bends anymore. It just gets shorter and shorter as the control approaches the shoulder:
Although there are times when you might want this effect, most of the time you're going to want the arm to bend. So how do we fix it?
We need a clamp. The shortening occurs when $factor < 1 and the scale gets multiplied by a fraction. So we need to clamp the value.
LtShoulder.scaleX = clamp(1,10,$factor) ;
LtElbow.scaleX = clamp(1,10,$factor) ;
Now the arm should bend when the control gets close, and stretch when it's far away:
The stretch effect works, and it's pretty cool so far, but there's more we can do with it. It might be useful to have an envelope attribute that controls the degree of stretchiness.
We'll start by adding a custom attribute to LtArm. (Right click in the Channel Box and select "Add Attribute"). Call it "Stretch." Make sure it is set to "float" data type. We should also set min=0, max=1, and default=1.
(UPDATE: In newer versions of Maya, you can't just right click in the channel box to "Add Attribute." For whatever reason, that menu option is now only accessible from the "Edit" drop-down box.)
Now we need a little bit of math to figure out how to write our expression. The easiest way to do it is to think of the control as a blender between two options. When LtArm.Stretch = 1, the arm stretches to wherever the control object is. When LtArm.Stretch = 0, it doesn't stretch at all. It behaves as a normal IK arm would. Values between 0 and 1 should interpolate.
To do this, we're going to have to modify our expression to have two different terms.
If we wanted to blend between terms P and Q, where x is the blender, our expression would look like this:
y = (Q *(1-x)) + (P * x)
In that equation, if x=0, then y=Q. And if x=1, y=P. Plug the numbers in and see for yourself.
In our expression, P = clamp (1,10,$factor) and Q = 1. (Q = 1 because when the stretchiness is off, we want LtShoulder.scaleX and LtElbow.scaleX to equal 1).
x needs to equal our custom attribute, so let's declare a new variable.
$envelope = LtArm.Stretch ;
So, using our equation as a guide, and plugging in the terms, our expression comes out like this:
LtShoulder.scaleX = 1*(1-$envelope) + (clamp(1,10,$factor)*$envelope) ;
And of course we can remove the "1*" from the first term to simplify to:
LtShoulder.scaleX = (1-$envelope) + (clamp(1,10,$factor)*$envelope) ;
Phew! I hope that was clear enough for everyone to follow along. Make the same amendment to LtElbow.scaleX and you should have a working control:
LtArm.Stretch = 1
LtArm.Stretch = 0
One last task: There might be occasions when the animator wants to use that arm shortening effect that we had earlier. Let's make a switch to go back and forth between an arm that bends and an arm that shortens.
Create another custom attribute on LtArm. This time call it ShortArm, and make it a "boolean" data type.
Now in the expression, we need to declare another variable.
$switch = (1-LtArm.ShortArm) ;
So when LtArm.ShortArm is off, $switch = 1, and when LtArm.ShortArm is on, $switch = 0.
Now, modify the clamp portion of the expression to read:
Chaging LtArm.ShortArm modifies the clamp function that we needed earlier to remove the shortening. Now instead of removing it entirely, you can turn it on and off.
LtArm.ShortArm = on
LtArm.ShortArm = off
You can download the final control here: squash and stretch
I hope that you enjoyed the lesson. Feel free to email me with any questions or corrections. Or check out my weblog: Set Driven Key, for more tutorials and tips.