3D Animation (Character)

From Animation
Jump to navigationJump to search

Ball With Tail Rig for beginnings in character animation

[Ball with Tail]

Below is a extremely dated version of a Unicycle Rig. I would do a lot of the elements differently now.

[Unicycle Rig 2018]

Floursack built to be animated strictly FK. Excellent tool to teach Step Key and block animation techniques.

[FlourSack Character Rig 2024]

Beginning rig for learning walkcycles and other animation fundamentals.

[Humanoid stick character with just legs]

3D Stickman character with index finger and group fingers. Solid beginning for animating humanoid characters without the need for dialogue.

[Bipedal humanoid stick character]

Helpful MEL Scripts

Setting Keys Auto or Stepped

The key types can be changed via Preferences > Setting > Animation, but that can be cumbersome. The scripts below can be used to swap the default type of key set via a shelf executed script.

Set the key type to 'stepped' on new set keys.

keyTangent -e -global -itt clamped;
keyTangent -e -global -ott step;

Set the key type to 'auto' on new set keys.

keyTangent -e -global -itt auto;
keyTangent -e -global -ott auto;

Karl's Key Framer Helper

The MEL script below creates a floating window to help with character animation.

Extremely helpful for those indidviduals who are "clickers" instead of those who love keyboard shortcuts.

global string $select[];
$select = {"placeHolder", "otherPlaceHolder"};
window -resizeToFitChildren 1 KarlsCharacterKeyFramer;
columnLayout;
text -label "Make those keyframes";
button - label "Copy keyframes" -command "timeSliderCopyKey";
button - label "Paste keyframes" -command "timeSliderPasteKey false";
button - label "Delete keyframes" -command "timeSliderClearKey";
button - label "Gizmo Object" -command "manipRotateContext -e -mode 0 Rotate; manipMoveContext -e -mode 0 Move";
button - label "Gizmo World" -command "manipRotateContext -e -mode 1 Rotate; manipMoveContext -e -mode 2 Move";
button - label "Graph Editor" -command "GraphEditor";
button - label "Store Selection" -command "$select = `ls -sl`";
button - label "Get Selection" -command "select -r $select";
button - label "SET KEY" -command "setKeyframe";
button - label "SET KEY BLOCK" -command "setKeyframe -itt linear -ott step";
showWindow;

Mirror Transforms Locally

Mirror/swap control curves transforms. Good for swapping hands and feet to alternate mirror positions, like in a copied walkcycle pose.

Note: This script will only work when the mirrored curves are sharing a hierarchical parent which can be used as the x-axis local zero. Example, hand controllers parented to waist controllers, not floating or only parented to the 'root' curve.

Usage: Select a control curve (like a left hand), then select the alternate control curve (like a right hand), then execute the script.

// Get ordered selection
string $sel[] = `ls -sl -o`;
if (size($sel) != 2)
{
    warning "Select exactly two curvs.";
}
else
{
    string $curveA = $sel[0];
    string $curveB = $sel[1];
    // --------------------------------
    // Store LOCAL transforms (vector attrs)
    // --------------------------------
    float $posA[] = `getAttr ($curveA + ".translate")`;
    float $rotA[] = `getAttr ($curveA + ".rotate")`;
    float $sclA[] = `getAttr ($curveA + ".scale")`;

    float $posB[] = `getAttr ($curveB + ".translate")`;
    float $rotB[] = `getAttr ($curveB + ".rotate")`;
    float $sclB[] = `getAttr ($curveB + ".scale")`;
    // --------------------------------
    // Apply MIRRORED A → B (local space)
    // --------------------------------
    setAttr ($curveB + ".translate")
        (-$posA[0]) $posA[1] $posA[2];
    setAttr ($curveB + ".rotate")
        $rotA[0] (-$rotA[1]) (-$rotA[2]);
    setAttr ($curveB + ".scale")
        $sclA[0] $sclA[1] $sclA[2];
    // --------------------------------
    // Apply MIRRORED B → A (local space)
    // --------------------------------
    setAttr ($curveA + ".translate")
        (-$posB[0]) $posB[1] $posB[2];
    setAttr ($curveA + ".rotate")
        $rotB[0] (-$rotB[1]) (-$rotB[2]);
    setAttr ($curveA + ".scale")
        $sclB[0] $sclB[1] $sclB[2];
}

Mirror Around a Selected Pivot

Mirror/swap script which uses three curves. The user selects the two curves to mirror then the third curve used as a pivot.

Usage: Select the curves you intend to mirror and the third curve you want to mirror around, then execute the script.

// -------------------------------
// 3-Curve Mirror & Swap Reference-Safe Script
// -------------------------------

// First-time variable declaration
string $selection[] = `ls -sl -o`;
if (size($selection) != 3) {
    warning "Select exactly 3 curves: curveA, curveB, mirrorCurve (last)";
} else {

    // Assign selection (reuse variable, no redeclaration)
    $selection = `ls -sl -o`;
    string $curveA = $selection[0];
    string $curveB = $selection[1];
    string $mirror = $selection[2];

    // -------------------------------
    // Create utility nodes
    // -------------------------------
    string $mmA = `createNode multMatrix`;
    string $decompA = `createNode decomposeMatrix`;
    string $mmB = `createNode multMatrix`;
    string $decompB = `createNode decomposeMatrix`;

    // -------------------------------
    // Connect world matrices to get local in mirror space
    // -------------------------------
    connectAttr -f ($curveA + ".worldMatrix[0]") ($mmA + ".matrixIn[0]");
    connectAttr -f ($mirror + ".worldInverseMatrix[0]") ($mmA + ".matrixIn[1]");
    connectAttr -f ($mmA + ".matrixSum") ($decompA + ".inputMatrix");

    connectAttr -f ($curveB + ".worldMatrix[0]") ($mmB + ".matrixIn[0]");
    connectAttr -f ($mirror + ".worldInverseMatrix[0]") ($mmB + ".matrixIn[1]");
    connectAttr -f ($mmB + ".matrixSum") ($decompB + ".inputMatrix");

    // -------------------------------
    // Read local transforms
    // -------------------------------
    float $posA[] = `getAttr ($decompA + ".outputTranslate")`;
    float $rotA[] = `getAttr ($decompA + ".outputRotate")`;
    float $sclA[] = `getAttr ($decompA + ".outputScale")`;

    float $posB[] = `getAttr ($decompB + ".outputTranslate")`;
    float $rotB[] = `getAttr ($decompB + ".outputRotate")`;
    float $sclB[] = `getAttr ($decompB + ".outputScale")`;

    // -------------------------------
    // Mirror across local X
    // -------------------------------
    float $mirAPos[] = { -$posA[0], $posA[1], $posA[2] };
    float $mirARot[] = { $rotA[0], -$rotA[1], -$rotA[2] };
    float $mirBPos[] = { -$posB[0], $posB[1], $posB[2] };
    float $mirBRot[] = { $rotB[0], -$rotB[1], -$rotB[2] };

    // -------------------------------
    // Compose mirrored local matrices
    // -------------------------------
    string $cmA = `createNode composeMatrix`;
    string $cmB = `createNode composeMatrix`;

    setAttr -type "double3" ($cmA + ".inputTranslate") $mirBPos[0] $mirBPos[1] $mirBPos[2];
    setAttr -type "double3" ($cmA + ".inputRotate") $mirBRot[0] $mirBRot[1] $mirBRot[2];
    setAttr -type "double3" ($cmA + ".inputScale") $sclB[0] $sclB[1] $sclB[2];

    setAttr -type "double3" ($cmB + ".inputTranslate") $mirAPos[0] $mirAPos[1] $mirAPos[2];
    setAttr -type "double3" ($cmB + ".inputRotate") $mirARot[0] $mirARot[1] $mirARot[2];
    setAttr -type "double3" ($cmB + ".inputScale") $sclA[0] $sclA[1] $sclA[2];

    // -------------------------------
    // Multiply with mirror world matrix
    // -------------------------------
    string $mmFinalA = `createNode multMatrix`;
    string $mmFinalB = `createNode multMatrix`;

    connectAttr -f ($cmA + ".outputMatrix") ($mmFinalA + ".matrixIn[0]");
    connectAttr -f ($mirror + ".worldMatrix[0]") ($mmFinalA + ".matrixIn[1]");

    connectAttr -f ($cmB + ".outputMatrix") ($mmFinalB + ".matrixIn[0]");
    connectAttr -f ($mirror + ".worldMatrix[0]") ($mmFinalB + ".matrixIn[1]");

    // -------------------------------
    // Get final world matrices
    // -------------------------------
    float $finalA[] = `getAttr ($mmFinalA + ".matrixSum")`;
    float $finalB[] = `getAttr ($mmFinalB + ".matrixSum")`;

    // -------------------------------
    // Apply final transforms (expanded for MEL xform)
    // -------------------------------
    xform -ws -m 
        $finalA[0] $finalA[1] $finalA[2] $finalA[3]
        $finalA[4] $finalA[5] $finalA[6] $finalA[7]
        $finalA[8] $finalA[9] $finalA[10] $finalA[11]
        $finalA[12] $finalA[13] $finalA[14] $finalA[15]
        $curveA;

    xform -ws -m 
        $finalB[0] $finalB[1] $finalB[2] $finalB[3]
        $finalB[4] $finalB[5] $finalB[6] $finalB[7]
        $finalB[8] $finalB[9] $finalB[10] $finalB[11]
        $finalB[12] $finalB[13] $finalB[14] $finalB[15]
        $curveB;

    // -------------------------------
    // Cleanup
    // -------------------------------
    delete $mmA $decompA $mmB $decompB $cmA $cmB $mmFinalA $mmFinalB;
}