Scale Animation from Maya to Unity

A while ago a friend of mine was working on a game in Unity that featured a creature with slinky like arms that would squash and stretch by animating scale on the arm joints. He was having an issue exporting scale animation on the arms into Unity, so I wrote a script to convert the scale animation to translation animation which Unity can read in.

Conditions:

  • The script assumes the joints scale along their X axis only. ( This can be changed if your joints use Z or Y as their twist axis )

How To Use It:

  1. Copy this python script into Maya’s script directory ( C:\Users\[ Your Name ]\Documents\maya\scripts )

  2. Increment and Save your animation scene as this will bake your animation to dense keys.

  3. Select the joints with scale animation.

  4. Run the following code in Maya’s script editor:

    1. import scale_to_translate
      joints = maya.cmds.ls( sl = True )
      scale_to_translate.main( joints )

The Code:

"""

Transforms scaleX values to translateX values on a list of keyed joints.

Author: ckurtz35@gmail.com

"""

# Imports
import maya.cmds
import maya.api.OpenMaya


def get_distance( obj_one, obj_two ):
    """

    Gets the distance between two objects. Equivalent to using
    the distance tool in Maya.

    """

    # Get the objects' translation
    trans_one = maya.cmds.xform( obj_one, t = 1, q = 1 )
    trans_two = maya.cmds.xform( obj_two, t = 1, q = 1 )

    # Convert the translation into MVector objects.
    vector_one = maya.api.OpenMaya.MVector( trans_one )
    vector_two = maya.api.OpenMaya.MVector( trans_two )

    # Subtracting the two vectors gives us a new one that
    # describes the distance between the two.
    new_vec = vector_two - vector_one

    # Taking the length of the vector gives us the
    # actual distance
    distance = new_vec.length( )

    return distance


def main( joints ):
    """

    Transforms scaleX values into translateX values on a set of joints.

    """

    # Preventing the UI from refreshing
    # speeds things up.
    maya.cmds.refresh( suspend = True )

    # Locators are used to get the distance
    # between joints later on.
    loc_one = maya.cmds.spaceLocator( )
    loc_two = maya.cmds.spaceLocator( )

    for jnt in joints:

        # The child joint position determines the length of the current joint.
        child_jnt = maya.cmds.listRelatives( jnt, children = 1, type = "joint" )
        if not child_jnt:
            continue

        child_jnt = child_jnt[ 0 ]

        # Constrain the locators to the two joints.
        constraint_one = maya.cmds.pointConstraint( jnt, loc_one, mo = False )
        constraint_two = maya.cmds.pointConstraint( child_jnt, loc_two, mo = False )

        # We need to convert the scale value for each keyframe.
        # CHANGE AXIS HERE IF NEEDED.
        key_frames = maya.cmds.keyframe( "{0}.scaleX".format( jnt ), query = 1 )
        if not key_frames:
            continue

        for k in key_frames:

            maya.cmds.currentTime( k, edit = True )

            # Getting the distance before and after we set the
            # scale to 1 will give us the distance delta ( difference )
            distance = get_distance( loc_one, loc_two )
            maya.cmds.xform( jnt, s = [ 1.0, 1.0, 1.0 ] )
            
            # CHANGE AXIS HERE IF NEEDED.
            maya.cmds.setKeyframe( jnt, at = "scaleX", time = k )
            new_distance = get_distance( loc_one, loc_two )

            delta = distance - new_distance
            
            # CHANGE AXIS HERE IF NEEDED.
            child_len = maya.cmds.getAttr( "{0}.translateX".format( child_jnt ) )

            # The distance delta is added to the child's translateX to get
            # the correct length for our current joint.
            if child_len < 0:
                new_len  = child_len - delta
            else:
                new_len = child_len + delta
            
            # CHANGE AXIS HERE IF NEEDED.
            maya.cmds.setAttr( "{0}.translateX".format( child_jnt ), new_len )
            # CHANGE AXIS HERE IF NEEDED.
            maya.cmds.setKeyframe( child_jnt, at = "translateX", time = k )

        maya.cmds.delete( constraint_one )
        maya.cmds.delete( constraint_two )

    maya.cmds.delete( loc_one )
    maya.cmds.delete( loc_two )

    maya.cmds.refresh( suspend = False )