Ben Traje
← Back to rigging

Automate Joint Control Curves in Maya with Python (Shape Parenting Guide)

12 Sep 26 (1mo ago)

Normally, you will have separate objects for controls and joints. Sometimes, especially, if I just want to rapidly iterate or I'm animating my own scene, I prefer having just the same.

Definitely, not a standard practice but works on my usecase. You can definitely do this though MEL with the parent -r -s command to parent shapes directly to joint.

P.S. By default you can only select the transforms. To select the shapes, in the outliner, you need to disable DAG only objects.

The Workflow

Attaching a curve shape directly to a joint transform eliminates redundant grouping nodes and keeps the Outliner clean. To automate this across multiple joints:

  1. Filter the active selection specifically for joint types.
  2. Duplicate the base template curve (conCrv).
  3. Zero out offsets by parenting the duplicate transform to the target joint.
  4. Reparent the curve's shape node to the joint using shape=True and relative=True.
  5. Rename the shape node dynamically using the joint's base name ({joint}_crv).
  6. Delete the residual empty transform node.

Maya Python Implementation

To automate the process, see the script below:

import maya.cmds as cmds

def attach_unique_con_to_joints():
    # 1. Filter selection to joints only
    selection = cmds.ls(sl=True, type='joint')
    
    if not selection:
        cmds.warning("Please select at least one joint.")
        return
        
    source_curve = "conCrv"
    
    if not cmds.objExists(source_curve):
        cmds.error(f"Source curve '{source_curve}' not found.")
        return

    for jnt in selection:
        # 2. Duplicate template curve
        temp_crv = cmds.duplicate(source_curve, name="temp_con_crv")[0]
        
        # 3. Match transform orientation and position
        cmds.parent(temp_crv, jnt, relative=True)
        
        # 4. Extract shape node
        shapes = cmds.listRelatives(temp_crv, shapes=True, fullPath=True)
        
        if shapes:
            # 5. Parent shape to joint (-r -s logic) and assign unique name
            target_name = f"{jnt}_crv"
            new_shape = cmds.parent(shapes[0], jnt, relative=True, shape=True)[0]
            cmds.rename(new_shape, target_name)
            
        # 6. Clean up empty transform
        cmds.delete(temp_crv)
        
    print(f"Controls created and renamed for {len(selection)} joints.")

attach_unique_con_to_joints()

Why Shape Parenting Matters

  • Zero Transform Clutter: The joint serves as the transform node, removing unnecessary intermediate group levels.
  • Selection Speed: Animators can pick the visual curve directly in the viewport while operating on the joint's native rotation and translation channels.
  • Deterministic Naming: Dynamically appending suffixes avoids Maya's automatic name-incrementing collisions across large skeleton hierarchies.