This one was a doozy.
There’s a way in Maya to get overlapping action on an object by using nParticles. I first came about it on a LesterBanks article when doing research for how to add dynamics to my car rig. Check out that link, it’s really cool!
So what this does is it uses a particle that is driven by some control and adds overlapping action through some direct-connection relationships and some workaround fixes… If you’re not familiar with the term, overlapping action is an animation principle for the reaction to an event. You can also think of it as Newton’s third law (action/reaction).
So check out the script that I wrote that automates this. The trickiest part was getting the same control functionality as you had before but with the dynamics layered on over it. That required me to do some fancy stuff that you’ll see described in the script comments…
import maya.cmds as cmds def createSpring(): # creates spring phsyics on the selected object ctrl = cmds.ls(sl=1, o=1) geo = cmds.ls(sl=1, o=1) ctrlLoc = cmds.xform(ctrl + ".rotatePivot", q=1, t=1, ws=1) # we have to make a duplicate of our ctrl to be able to still move our control as expected ctrlDuplicate = cmds.duplicate(ctrl) # make that duplicate a child of our original ctrl. It's technically doing the legwork for the physics. cmds.parent(ctrlDuplicate, ctrl) cmds.hide(ctrlDuplicate) # create our particle at the location of our ctrl particle = cmds.particle(p=ctrlLoc, c=1) # make our spring physics on the duplicate object to avoid double transforms spring = cmds.spring(particle, ctrlDuplicate, name=ctrl + "_spring#", damping=2.5, stiffness=15) cmds.addAttr(ctrl, attributeType="float", ln="Damping", min=0, max=100, dv=2, keyable=1) cmds.connectAttr(ctrl + ".Damping", spring + ".damping") cmds.addAttr(ctrl, attributeType="float", ln="Stiffness", min=0, max=100, dv=25, keyable=1) cmds.connectAttr(ctrl + ".Stiffness", spring + ".stiffness") # this group will track the position of our particle and will be used for world position calculations springXform = cmds.group(em=1, w=1, n=spring + "_xform") cmds.connectAttr(particle + ".worldCentroid", springXform + ".translate") # make our group that will be used to control our duplicate control ctrlXform = cmds.group(em=1, w=1, n=ctrl + "_xform") cmds.xform(ctrlXform, t=ctrlLoc) cmds.makeIdentity(ctrlXform, apply=1, t=1) cmds.parent(ctrlXform, ctrlDuplicate) cmds.pointConstraint(springXform, ctrlXform, mo=1) pointA = cmds.group(em=1, w=1, n=spring + "_ptA") cmds.pointConstraint(ctrlDuplicate, pointA) pointB = cmds.group(em=1, w=1, n=spring + "_ptB") cmds.pointConstraint(ctrlXform, pointB) dist = cmds.createNode("plusMinusAverage", n=spring + "_dist#") cmds.setAttr(dist + ".op", 2) cmds.connectAttr(pointA + ".translate", dist + ".input3D") cmds.connectAttr(pointB + ".translate", dist + ".input3D") mult = cmds.createNode("multiplyDivide", n=spring + "_mult#") # make a variable so that the user can amp up the rotation if they want cmds.addAttr(ctrl, at="float", dv=15, min=0, ln="Rot_Multiplier", k=1) # create a node that inverts this for our i1x attribute inverse = cmds.createNode("multiplyDivide", n="inverse#") cmds.setAttr(inverse + ".i2x", -1) cmds.connectAttr(ctrl + ".Rot_Multiplier", inverse + ".i1x") cmds.connectAttr(inverse + ".ox", mult + ".i2z") cmds.connectAttr(ctrl + ".Rot_Multiplier", mult + ".i2y") cmds.connectAttr(ctrl + ".Rot_Multiplier", mult + ".i2x") cmds.connectAttr(dist + ".output3Dx", mult + ".i1x") cmds.connectAttr(dist + ".output3Dy", mult + ".i1y") cmds.connectAttr(dist + ".output3Dz", mult + ".i1z") cmds.connectAttr(mult + ".ox", ctrlXform + ".rz") cmds.connectAttr(mult + ".oz", ctrlXform + ".rx") pConst = cmds.parentConstraint(ctrlXform, ctrl, geo, mo=1) particleGrp = cmds.group(particle, pointA, pointB, n="particle_Grp#") cmds.parent(particleGrp, ctrl) cmds.addAttr(ctrl, at="float", dv=1, min=0, max=1, ln="Physics", k=1) remap = cmds.createNode("remapValue", n="physicsRemap#") cmds.setAttr(remap + ".outputMax", .25) cmds.connectAttr(ctrl + ".Physics", remap + ".value.value_FloatValue") # find the weight number where our new ctrlXform node is in weightList = cmds.parentConstraint(pConst, q=1, weightAliasList=1) weightValue = "" for i in weightList: if ctrlXform in i: weightValue = i cmds.connectAttr(remap + ".outValue", pConst + "." + weightValue) cmds.group(springXform,spring,n=spring + "_GRP") """ 1. Double transformations. The object is parented to both our ctrl and the group that both are doing the same transformations. Therefore it's double transforming... Solution: All Ctrl Parent (what you animate) Ctrl You run the script on ctrl xform point constraint to particle xform particle_GRP particle spring pointA pointConst to Ctrl spring pointB pointConst to spring_xform GEO parentConst to Ctrl (1) parentConst to ctrl_xform (.25) spring spring_xform """