3D Animation (Character): Difference between revisions

From Animation
Jump to navigationJump to search
 
(11 intermediate revisions by the same user not shown)
Line 1: Line 1:
= Helpful Mel Scripts =
= [[Maya Character Rigs]] =
Ball With Tail Rig for beginnings in character animation
 
[[https://sillydragon.com/USA_Demo/rigs/2020_ball_with_tail.ma Ball with Tail]]
 
Below is a extremely dated version of a Unicycle Rig. I would do a lot of the elements differently now.
 
[[https://sillydragon.com/USA_Demo/rigs/2018_Unicycle_Rigged.ma Unicycle Rig 2018]]
 
Floursack built to be animated strictly FK. Excellent tool to teach Step Key and block animation techniques.
 
[[https://sillydragon.com/USA_Demo/rigs/Rig_FlourSack.ma FlourSack Character Rig 2024]]
 
Beginning rig for learning walkcycles and other animation fundamentals.
 
[[https://sillydragon.com/USA_Demo/rigs/2024_biPed_Just_Legs.ma 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.
 
[[https://sillydragon.com/USA_Demo/rigs/2025_stickChar.ma 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.
 
<pre>
keyTangent -e -global -itt clamped;
keyTangent -e -global -ott step;
</pre>
 
Set the key type to 'auto' on new set keys.
 
<pre>
keyTangent -e -global -itt auto;
keyTangent -e -global -ott auto;
</pre>
 
== Karl's Key Framer Helper ==
== Karl's Key Framer Helper ==
The MEL script below creates a floating window to help with character animation.
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.
Extremely helpful for those indidviduals who are "clickers" instead of those who love keyboard shortcuts.
<pre>
<pre>
Line 20: Line 59:
button - label "SET KEY BLOCK" -command "setKeyframe -itt linear -ott step";
button - label "SET KEY BLOCK" -command "setKeyframe -itt linear -ott step";
showWindow;
showWindow;
</pre>
== 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.
<pre>
// 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];
}
</pre>
== 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.
<pre>
// -------------------------------
// 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;
}
</pre>
</pre>

Latest revision as of 18:48, 4 January 2026

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;
}