Brandon Coffey

Programmer and Technical Artist

Maya Python QT Tools



Maya to Unity/Unreal Rig Exporter

When exporting rigs and animations done in Autodesk Maya to Unreal Engine or Unity, any custom deform nodes or constraints are unable to normally be exported. This became apparent with my experience in Death Bloom and Kitty City, when animators used custom rigs that they were used to, as they were not used to building rigs for game development. So, I created a tool, called CreateCompatibilityRig, which does the following:

  • Allows the user to specificy components of the source rig
  • Duplicates the entire joint structure and skinned mesh
  • Creates position and rotation constraints from the source joints to the new joints

Left: Source Maya Rig. Middle: Duplicated Joint Structure. Right: Output Mesh Compatible with Unity


Below is the code for the tool, which can be loaded into Maya and run with the ‘run()’ function. This opens a QT window, which is Maya’s GUI interface, and all of the user interaction is through buttons and text fields.

def run():
    rigCreator = CreateCompatibilityRig()
    rigCreator.init_gui()


class CreateCompatibilityRig:
    def __init__(self):
        self.windowName = 'unrealRigCreator'
        self.sourceJointsField = None
        self.sourceJoints = None
        self.destinationJointsField = None
        self.destinationJoints = None

    def init_gui(self):
        # Delete Existing Window if it Exists
        if cmds.window(self.windowName, exists=True):
            cmds.deleteUI(self.windowName)

        # Create Window
        cmds.window(self.windowName, title='Rig Transplant', widthHeight=(250, 300))

        # Content
        cmds.columnLayout(adjustableColumn=True, rowSpacing=5)
        cmds.separator(height=1)
        cmds.text(l='Source Joints')
        self.sourceJointsField = cmds.textField()
        cmds.button(l='Select Joints', command=self.select_source_joints)
        cmds.separator(height=10)
        cmds.text(l='Destination Joints')
        self.destinationJointsField = cmds.textField()
        cmds.button(l='Select Joints', command=self.select_destination_joints)
        cmds.separator(height=10)
        cmds.button(l='Bind Joints (Parent)', command=self.set_parent_constraints)
        cmds.button(l='Bind Joints (Scale)', command=self.set_scale_constraints)
        cmds.separator(height=10)
        cmds.button(label='Close',
                    command=('cmds.deleteUI(\"' + self.windowName + '\", window=True)'))

        # Display Window
        cmds.showWindow(self.windowName)

    def select_source_joints(self, *args):
        selected = cmds.ls(selection=True)
        self.sourceJoints = cmds.listRelatives(selected, ad=True, type='joint')
        cmds.textField(self.sourceJointsField, edit=True,
                       tx=str(self.sourceJoints))

    def select_destination_joints(self, *args):
        selected = cmds.ls(selection=True)
        self.destinationJoints = cmds.listRelatives(selected, ad=True, type='joint')
        cmds.textField(self.destinationJointsField, edit=True,
                       tx=str(self.destinationJoints))

    def set_parent_constraints(self, *args):
        sj = self.sourceJoints
        dj = self.destinationJoints
        for x in range(0, len(sj)):
            if not cmds.objExists(sj[x]):
                continue
            if cmds.objectType(sj[x], isType='joint') == 1:
                cmds.select(d=True)
                cmds.parentConstraint(sj[x], dj[x])

    def set_scale_constraints(self, *args):
        sj = self.sourceJoints
        dj = self.destinationJoints
        for x in range(0, len(sj)):
            if not cmds.objExists(sj[x]):
                continue
            if cmds.objectType(sj[x], isType='joint') == 1:
                cmds.select(d=True)
                cmds.scaleConstraint(sj[x], dj[x])