Maya | Python | Rigging | Hand Part 1
- Max Liu
- Dec 19, 2024
- 5 min read
Guide Bones Creation:
When the user clicks "Create Left hand guide figures bone," the script creates a guide joint hierarchy starting with L_Hand_gde_JNT.
For each finger (Thumb, Index, Middle, Ring, and Pinky), it creates four guide joints (e.g., L_Thumb1_gde_JNT to L_Thumb4_gde_JNT).
These guide joints are positioned in a rough formation, allowing the user to adjust them as needed before finalizing the rig.
Final Joints and Controls Creation:
When the user clicks "Create figures controls and a fist control," the script duplicates the guide joints, removes the gde part from their names, and creates a final joint hierarchy (e.g., L_Thumb1_JNT).
It then creates a wireframe cube control for each joint (except the hand joint itself) for easier selection and manipulation.
A special L_fist_CTL control is created at the position of L_Hand_JNT and given a fist attribute (0 to 1).
Fist Function Setup (Set Driven Keys):
The script sets driven keys so that adjusting the fist attribute on L_fist_CTL drives the rotation of the finger joints.
When fist = 0, the fingers are straight (0° rotation).
When fist = 1, the fingers curl (e.g., 50° rotation).
This allows an animator to open and close the hand by simply adjusting one attribute.
Reset fingers control transform:
Store the fingers controls while it is been created.
Reset the transfrom when click the button.
Code:
import maya.cmds as cmds
# Finger and joint naming details
fingers = ["Thumb", "Index", "Middle", "Ring", "Pinky"]
num_joints_per_finger = 4 # e.g. Thumb1, Thumb2, Thumb3, Thumb4
# Dictionary to store initial transforms of controls
initial_transforms = {}
def create_left_hand_guide_bones(*args):
"""
Creates a left hand guide joint hierarchy with naming conventions:
L_Hand_gde_JNT as root.
For each finger in [Thumb, Index, Middle, Ring, Pinky], creates:
L_{FingerName}1_gde_JNT ... L_{FingerName}4_gde_JNT
These are positioned in a simple formation for the user to adjust.
"""
# Check if guide joints already exist
if cmds.objExists("L_Hand_gde_JNT"):
cmds.warning("Guide joints seem to exist already.")
return
# Create the hand guide joint at origin
hand_jnt = cmds.joint(name="L_Hand_gde_JNT", position=(0, 10, 0))
cmds.select(clear=True)
# Positioning offsets for each finger so user can see and adjust them
base_x_positions = {
"Thumb": -2,
"Index": -1,
"Middle": 0,
"Ring": 1,
"Pinky": 2
}
for f in fingers:
# Start finger chain
base_x = base_x_positions[f]
# Create the first finger joint
first_name = "L_{0}1_gde_JNT".format(f)
first_jnt = cmds.joint(name=first_name, position=(base_x, 10, 1))
cmds.parent(first_jnt, hand_jnt)
# Create subsequent joints for the finger
parent = first_jnt
for i in range(2, num_joints_per_finger+1):
cmds.select(clear=True)
jnt_name = "L_{0}{1}_gde_JNT".format(f, i)
jnt = cmds.joint(name=jnt_name, position=(base_x, 10, i+1))
cmds.parent(jnt, parent)
parent = jnt
cmds.select(clear=True)
cmds.inViewMessage(amg="Guide bones created.", pos="midCenter", fade=True)
def create_figures_controls_and_fist_control(*args):
"""
1. Create final joints from guide joints (remove the '_gde_' part in name).
2. Create a cube control for each final joint for easy selection.
3. Create a L_fist_CTL aligned to L_Hand_JNT with a 'fist' attribute.
4. Set Driven Keys so that 'fist' attribute controls the rotation of finger joints.
"""
# Check if guide exists
if not cmds.objExists("L_Hand_gde_JNT"):
cmds.warning("No guide joints found. Please create guide bones first.")
return
hand_guide = "L_Hand_gde_JNT"
all_guide_joints = cmds.listRelatives(hand_guide, allDescendents=True, type="joint") or []
all_guide_joints.append(hand_guide)
all_guide_joints = sorted(all_guide_joints, key=lambda x: len(x))
old_to_new = {}
cmds.select(clear=True)
for jnt in all_guide_joints:
new_name = jnt.replace("_gde_", "_")
pos = cmds.xform(jnt, q=True, ws=True, t=True)
new_jnt = cmds.joint(name=new_name, position=pos)
rot = cmds.xform(jnt, q=True, ws=True, ro=True)
cmds.xform(new_jnt, ws=True, ro=rot)
old_to_new[jnt] = new_jnt
cmds.select(clear=True)
# Re-parent final joints
for old_jnt, new_jnt in old_to_new.items():
parent = cmds.listRelatives(old_jnt, parent=True, type="joint")
if parent:
new_parent = old_to_new.get(parent[0], None)
if new_parent:
cmds.parent(new_jnt, new_parent)
# Create cube controls for each final joint (except the hand joint)
control_hierarchy = {}
for old_jnt, new_jnt in old_to_new.items():
if new_jnt == "L_Hand_JNT":
continue
ctl_name = new_jnt.replace("_JNT", "_CTL")
# Create a wireframe cube control
points = [
[-0.25, -0.25, -0.25], [0.25, -0.25, -0.25], [0.25, 0.25, -0.25], [-0.25, 0.25, -0.25], [-0.25, -0.25, -0.25],
[-0.25, -0.25, 0.25], [0.25, -0.25, 0.25], [0.25, 0.25, 0.25], [-0.25, 0.25, 0.25], [-0.25, -0.25, 0.25],
[0.25, -0.25, 0.25], [0.25, -0.25, -0.25], [0.25, 0.25, -0.25], [0.25, 0.25, 0.25], [-0.25, 0.25, 0.25], [-0.25, 0.25, -0.25]
]
ctl = cmds.curve(name=ctl_name, degree=1, point=points)
pos = cmds.xform(new_jnt, q=True, ws=True, t=True)
rot = cmds.xform(new_jnt, q=True, ws=True, ro=True)
cmds.xform(ctl, ws=True, t=pos, ro=rot)
# Store the initial transform
initial_transforms[ctl_name] = {'translate': pos, 'rotate': rot}
# Parent constraint the joint to the control
cmds.parentConstraint(ctl, new_jnt, mo=True)
# Store the control in the hierarchy dictionary
control_hierarchy[new_jnt] = ctl
# Parent the controls to follow the joint hierarchy
for old_jnt, new_jnt in old_to_new.items():
if new_jnt == "L_Hand_JNT":
continue
parent = cmds.listRelatives(new_jnt, parent=True, type="joint")
if parent:
parent_ctl = control_hierarchy.get(parent[0], None)
if parent_ctl:
cmds.parent(control_hierarchy[new_jnt], parent_ctl)
# Create the L_fist_CTL at the hand joint
fist_ctl = cmds.circle(name="L_fist_CTL", normal=(0,1,0), radius=1)[0]
hand_pos = cmds.xform("L_Hand_JNT", q=True, ws=True, t=True)
cmds.xform(fist_ctl, ws=True, t=hand_pos)
# Add the fist attribute
if not cmds.attributeQuery("fist", node=fist_ctl, exists=True):
cmds.addAttr(fist_ctl, longName="fist", attributeType="float", min=0, max=1, defaultValue=0, keyable=True)
# Set Driven Keys for finger curl
# At fist=0, all finger joints are straight (rotateX=0)
# At fist=1, fingers curl (rotateX = e.g., -50 degrees)
# Adjust values as desired for a nicer fist pose.
# We'll identify the finger joints by their naming pattern:
# L_Thumb1_JNT, L_Thumb2_JNT, ... L_Pinky4_JNT
# They should be children of L_Hand_JNT
all_final_joints = cmds.listRelatives("L_Hand_JNT", allDescendents=True, type="joint") or []
# Filter out thumb and finger joints only (ignore the hand joint)
finger_joints = [j for j in all_final_joints if any(f in j for f in fingers)]
# Set the initial "fist" to 0 and rotate all finger joints to 0:
cmds.setAttr(fist_ctl + ".fist", 0)
for jnt in finger_joints:
cmds.setAttr(jnt + ".rotateX", 0)
cmds.setDrivenKeyframe([j + ".rotateX" for j in finger_joints], cd=fist_ctl + ".fist")
# Now set "fist" to 1 and rotate finger joints to a curled pose:
cmds.setAttr(fist_ctl + ".fist", 1)
# We'll curl them more at the end joints. For simplicity, let's do a uniform rotation:
# You can adjust these values per joint for a more natural curl.
for jnt in finger_joints:
# Example: rotateX = 50 for a curled look
cmds.setAttr(jnt + ".rotateX", 50)
cmds.setDrivenKeyframe([j + ".rotateX" for j in finger_joints], cd=fist_ctl + ".fist")
# Return fist to 0
cmds.setAttr(fist_ctl + ".fist", 0)
# Set Driven Keys for controls to follow the joints
for jnt in finger_joints:
ctl = jnt.replace("_JNT", "_CTL")
if cmds.objExists(ctl):
cmds.setDrivenKeyframe([ctl + ".rotateX"], cd=fist_ctl + ".fist", dv=0, v=0)
cmds.setDrivenKeyframe([ctl + ".rotateX"], cd=fist_ctl + ".fist", dv=1, v=50)
cmds.inViewMessage(amg="Final joints and controls created with fist function.", pos="midCenter", fade=True)
def reset_control(finger):
for i in range(1, num_joints_per_finger + 1):
ctl_name = 'L_' + finger + str(i) + '_CTL'
print('ctl_name:', ctl_name)
if cmds.objExists(ctl_name) and ctl_name in initial_transforms:
print('found ctl_name:', ctl_name)
cmds.xform(ctl_name, ws=True, t=initial_transforms[ctl_name]['translate'])
cmds.xform(ctl_name, ws=True, ro=initial_transforms[ctl_name]['rotate'])
def create_figure_ui():
# If the window exists, delete it
if cmds.window("figureUI", exists=True):
cmds.deleteUI("figureUI")
# Create a new window
window = cmds.window("figureUI", title="Figure Creator", widthHeight=(300,200))
# Create a layout
cmds.columnLayout(adjustableColumn=True)
# Add buttons
cmds.button(label="Create Left hand guide figures bone", command=create_left_hand_guide_bones)
cmds.separator(height=10)
cmds.button(label="Create figures controls and a fist control", command=create_figures_controls_and_fist_control)
cmds.separator(height=10)
# Add reset buttons for each finger
cmds.button(label="Reset {0}".format(fingers[0]), command=lambda f=fingers[0]: reset_control(fingers[0]))
cmds.button(label="Reset {0}".format(fingers[1]), command=lambda f=fingers[1]: reset_control(fingers[1]))
cmds.button(label="Reset {0}".format(fingers[2]), command=lambda f=fingers[2]: reset_control(fingers[2]))
cmds.button(label="Reset {0}".format(fingers[3]), command=lambda f=fingers[3]: reset_control(fingers[3]))
cmds.button(label="Reset {0}".format(fingers[4]), command=lambda f=fingers[4]: reset_control(fingers[4]))
cmds.showWindow(window)
# To display the UI, run:
create_figure_ui()
Yorumlar