import maya.cmds as cmds
def update_target_list():
target = cmds.ls(selection=True, type="transform")
cmds.textScrollList("targetList", edit=True, removeAll=True)
if len(target) == 1:
cmds.textScrollList("targetList", edit=True, append=target)
elif len(target) > 1:
cmds.confirmDialog(title="エラー", message="ターゲットオブジェクトは1つだけ選択してください。", button=["OK"])
cmds.textScrollList("targetList", edit=True, append=["ターゲットオブジェクトは選択されていません"])
else:
cmds.textScrollList("targetList", edit=True, append=["ターゲットオブジェクトは選択されていません"])
def update_selection_list():
selection = cmds.ls(selection=True, type="transform")
cmds.textScrollList("selectionList", edit=True, removeAll=True)
if selection:
cmds.textScrollList("selectionList", edit=True, append=selection)
else:
cmds.textScrollList("selectionList", edit=True, append=["選択中のオブジェクトはありません"])
def get_text_scroll_list_items(list_name):
items = cmds.textScrollList(list_name, query=True, allItems=True)
return items if items else []
def delete_set_and_contents(set_name):
if cmds.objExists(set_name):
contents = cmds.sets(set_name, query=True) or []
if contents:
cmds.delete(contents)
cmds.delete(set_name)
print(f"セット {set_name} とその内容を削除しました。")
def get_all_child_joints(joint):
children = cmds.listRelatives(joint, ad=True, type='joint') or []
ordered = cmds.ls(children, long=True)
return [joint] + ordered[::-1]
def get_controlled_joints_in_order(ctrl_list):
result = []
seen = set()
for ctrl in ctrl_list:
joints = set()
for attr in ['rotateX', 'rotateY', 'rotateZ']:
conns = cmds.listConnections(f"{ctrl}.{attr}", s=False, d=True) or []
for c in conns:
if cmds.nodeType(c) == 'joint':
joints.add(c)
for pnt in cmds.listConnections(ctrl, type='pointConstraint') or []:
targets = cmds.listConnections(pnt + ".constraintTranslateX", s=False, d=True) or []
for t in targets:
if cmds.nodeType(t) == 'joint':
joints.add(t)
for pcn in cmds.listConnections(ctrl, type='parentConstraint') or []:
targets = cmds.listConnections(pcn + ".constraintRotateX", s=False, d=True) or []
for t in targets:
if cmds.nodeType(t) == 'joint':
joints.add(t)
for jnt in joints:
all_joints = get_all_child_joints(jnt)
for aj in all_joints:
if aj not in seen:
result.append(aj)
seen.add(aj)
return result
def duplicate_joint_chain(joint_list, prefix="IK_"):
duplicated = {}
for jnt in joint_list:
short = jnt.split('|')[-1]
dup = cmds.duplicate(jnt, name=prefix + short, parentOnly=True)[0]
duplicated[short] = dup
for jnt in joint_list:
short = jnt.split('|')[-1]
parent = cmds.listRelatives(jnt, parent=True, type="joint")
if parent:
p_short = parent[0].split('|')[-1]
if p_short in duplicated:
current_parent = cmds.listRelatives(duplicated[short], parent=True)
if not current_parent or current_parent[0] != duplicated[p_short]:
try:
cmds.parent(duplicated[short], duplicated[p_short])
except RuntimeError as e:
cmds.warning(f"{duplicated[short]} の親設定に失敗: {str(e)}")
return list(duplicated.values()), duplicated
def create_segmented_ik_chain_with_constraints():
target_items = get_text_scroll_list_items("targetList")
if not target_items or "選択されていません" in target_items[0]:
cmds.confirmDialog(title="エラー", message="ターゲットオブジェクトを指定してください。", button=["OK"])
return
target = target_items[0]
ctrl_list = get_text_scroll_list_items("selectionList")
if not ctrl_list or "選択中のオブジェクトはありません" in ctrl_list:
cmds.confirmDialog(title="エラー", message="コントローラーを選択してください。", button=["OK"])
return
original_joints = get_controlled_joints_in_order(ctrl_list)
if len(original_joints) < 2:
cmds.confirmDialog(title="エラー", message="対象ジョイントが2つ未満です。", button=["OK"])
return
duplicated_joints, dup_map = duplicate_joint_chain(original_joints, prefix="IK_")
sorted_dup = sorted(duplicated_joints, key=lambda j: j.count('|') if '|' in j else 0)
if not cmds.objExists("IK_Joints_GRP"):
cmds.group(em=True, name="IK_Joints_GRP")
if not cmds.objExists("IK_Handles_GRP"):
cmds.group(em=True, name="IK_Handles_GRP")
cmds.parent(sorted_dup[0], "IK_Joints_GRP")
ik_handles = []
for i in range(len(sorted_dup) - 1):
j1, j2 = sorted_dup[i], sorted_dup[i + 1]
try:
ik_name = f"ikHandle_{j1.split('|')[-1]}_to_{j2.split('|')[-1]}"
ik, _ = cmds.ikHandle(name=ik_name, startJoint=j1, endEffector=j2, solver="ikRPsolver")
cmds.parent(ik, "IK_Handles_GRP")
ik_handles.append(ik)
except RuntimeError as e:
cmds.warning(f"IK作成スキップ: {j1} → {j2} ({str(e)})")
# ターゲットに親付け
try:
cmds.parent("IK_Joints_GRP", target)
cmds.parent("IK_Handles_GRP", target)
print(f"IK構成をターゲット {target} にペアレントしました。")
except Exception as e:
cmds.warning(f"ターゲットへのペアレントに失敗: {str(e)}")
# 元コントローラーをIKジョイントで制御
for orig_jnt, ctrl in zip(original_joints, ctrl_list):
orig_short = orig_jnt.split('|')[-1]
if orig_short in dup_map:
ik_jnt = dup_map[orig_short]
constraint_attrs = []
for attr in ["translateX", "translateY", "translateZ", "rotateX", "rotateY", "rotateZ"]:
if not cmds.getAttr(f"{ctrl}.{attr}", lock=True):
constraint_attrs.append(attr)
if constraint_attrs:
try:
cmds.parentConstraint(
ik_jnt, ctrl,
maintainOffset=True,
skipTranslate=[a[-1].lower() for a in ["translateX", "translateY", "translateZ"] if a not in constraint_attrs],
skipRotate=[a[-1].lower() for a in ["rotateX", "rotateY", "rotateZ"] if a not in constraint_attrs]
)
print(f"{ik_jnt} → {ctrl} にコンストレイントを設定しました。")
except Exception as e:
cmds.warning(f"{ik_jnt} → {ctrl} のコンストレイント失敗: {str(e)}")
else:
print(f"{ctrl} のすべてのトランスフォーム属性がロックされています。")
if not cmds.objExists("joint_and_ik_set"):
cmds.sets(name="joint_and_ik_set", empty=True)
cmds.sets(sorted_dup + ik_handles, add="joint_and_ik_set")
cmds.confirmDialog(title="完了", message="分節IKチェーンを作成し、ターゲットへの親付け・追従設定を行いました。", button=["OK"])
def create_selection_and_joint_ui():
if cmds.window("selectionJointUI", exists=True):
cmds.deleteUI("selectionJointUI")
window = cmds.window("selectionJointUI", title="分節IKチェーン作成ツール", widthHeight=(300, 450))
cmds.columnLayout(adjustableColumn=True)
cmds.text(label="ターゲットオブジェクト:", align="center")
cmds.textScrollList("targetList", height=50)
cmds.button(label="ターゲットを更新", command=lambda x: update_target_list())
cmds.text(label="親から子の順にコントローラーを選択:", align="center")
cmds.textScrollList("selectionList", height=120)
cmds.button(label="現在選択を更新", command=lambda x: update_selection_list())
cmds.separator(height=10, style='in')
cmds.button(label="分節IKチェーンを作成", height=40, command=lambda x: create_segmented_ik_chain_with_constraints())
cmds.button(label="セットとその中身を削除", command=lambda x: delete_set_and_contents("joint_and_ik_set"))
cmds.showWindow(window)
update_target_list()
update_selection_list()
# 実行
create_selection_and_joint_ui()