<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>喵喵动画屋</title>
	<atom:link href="https://www.miaodonghua.com/feed" rel="self" type="application/rss+xml" />
	<link>https://www.miaodonghua.com/</link>
	<description>探索Maya世界：基础教程、动画技巧、建模艺术与渲染技术。</description>
	<lastBuildDate>Tue, 23 Dec 2025 18:07:50 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://www.miaodonghua.com/wp-content/uploads/2020/11/cropped-shuqian_logo.webp</url>
	<title>喵喵动画屋</title>
	<link>https://www.miaodonghua.com/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Maya 裙子绑定神器｜按顶点顺序创建控制器 &#038; 关节链｜Skirt Rigging Tool</title>
		<link>https://www.miaodonghua.com/3553.html</link>
		
		<dc:creator><![CDATA[喵喵动画屋]]></dc:creator>
		<pubDate>Tue, 23 Dec 2025 18:07:48 +0000</pubDate>
				<category><![CDATA[MAYA动画教学]]></category>
		<guid isPermaLink="false">https://www.miaodonghua.com/?p=3553</guid>

					<description><![CDATA[<p><a href="https://www.miaodonghua.com/3553.html">Maya 裙子绑定神器｜按顶点顺序创建控制器 &amp; 关节链｜Skirt Rigging Tool</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<pre class="wp-block-code has-vivid-green-cyan-color has-black-background-color has-text-color has-background has-link-color wp-elements-5dc825d4c9e7eefb26e2dc583d57429d"><code># -*- coding: utf-8 -*-
"""
Maya Python脚本：根据选择的多边形顶点位置创建控制器（Maya 2022 / Python 3）

功能 / Features:
- 常驻 UI：记录你 Shift 逐个点选的顶点顺序（使用 orderedSelection，避免组件 index 排序）
- 列表交互：双击列表条目 -> 选中并框显对应顶点（Select &amp; Frame）
- 可调参数：控制器颜色色卡（RGB）+ 控制器大小（半径，默认 1）
- 一键创建：从记录列表创建关节链 + 控制器 + 约束
- UI 全部中英双语
- 重要按钮配色区分（开始记录/清空/从列表创建）
- 体验优化：列表为空时“从列表创建”置灰；录制中窗口标题显示“● Recording”
- 作者信息：董帅

作者：董帅
个人博客：https://www.miaodonghua.com/
YouTube：https://www.youtube.com/channel/UCPw4S_MYsO1Tuv3VzU-e5Qw
Bilibili：https://space.bilibili.com/353020636
"""

import math
import maya.cmds as cmds


# -------------------------
# UI Button Colors (0~1 RGB)
# -------------------------
BTN_PRIMARY_GREEN = (0.22, 0.60, 0.26)   # Start recording
BTN_PRIMARY_RED   = (0.70, 0.22, 0.22)   # Stop recording
BTN_PRIMARY_BLUE  = (0.18, 0.42, 0.72)   # Create
BTN_WARN_ORANGE   = (0.78, 0.45, 0.12)   # Clear
BTN_SECONDARY     = (0.32, 0.32, 0.32)   # Other buttons


# -------------------------
# Author Links (not shown in UI; open via buttons)
# -------------------------
AUTHOR_NAME = "董帅"
URL_BLOG = "https://www.miaodonghua.com/"
URL_YOUTUBE = "https://www.youtube.com/channel/UCPw4S_MYsO1Tuv3VzU-e5Qw"
URL_BILIBILI = "https://space.bilibili.com/353020636"


# -------------------------
# Helpers
# -------------------------

def _open_url(url):
    """Open URL in default browser (Maya-friendly)."""
    try:
        cmds.launch(web=url)
        return
    except Exception:
        pass
    try:
        import webbrowser
        webbrowser.open(url, new=2)
    except Exception:
        cmds.warning("无法打开浏览器链接。\nFailed to open browser link.")


def _set_ctrl_color_rgb(ctrl_transform, rgb):
    """将控制器曲线设为指定 RGB 颜色（viewport override）。"""
    if not ctrl_transform or not cmds.objExists(ctrl_transform):
        return

    try:
        r, g, b = rgb
        r, g, b = float(r), float(g), float(b)
    except Exception:
        r, g, b = 1.0, 1.0, 0.0

    shapes = cmds.listRelatives(ctrl_transform, shapes=True, fullPath=True) or &#91;]
    for s in shapes:
        try:
            cmds.setAttr(s + ".overrideEnabled", 1)
            cmds.setAttr(s + ".overrideRGBColors", 1)
            cmds.setAttr(s + ".overrideColorRGB", r, g, b)
        except Exception:
            # 兜底：使用索引色（17 通常为黄）
            try:
                cmds.setAttr(s + ".overrideEnabled", 1)
                cmds.setAttr(s + ".overrideRGBColors", 0)
                cmds.setAttr(s + ".overrideColor", 17)
            except Exception:
                pass


def get_available_suffix(base_name):
    """获取可用的后缀（A-Z 或数字）。"""
    for i in range(26):
        suffix = chr(ord('A') + i)
        full_name = "{}_{}".format(base_name, suffix)
        main_grp = "{}_rig_grp".format(full_name)
        if not cmds.objExists(main_grp):
            return suffix

    num = 1
    while True:
        suffix = str(num)
        full_name = "{}_{}".format(base_name, suffix)
        main_grp = "{}_rig_grp".format(full_name)
        if not cmds.objExists(main_grp):
            return suffix
        num += 1
        if num > 10000:
            break

    return "1"


def aim_controller_group_to_world_center(grp, position):
    """让控制器组本地 +X 轴指向“世界中心的垂线方向”（与径向向量垂直）。"""
    radial = &#91;-position&#91;0], -position&#91;1], -position&#91;2]]
    magnitude = math.sqrt(radial&#91;0] ** 2 + radial&#91;1] ** 2 + radial&#91;2] ** 2)
    if magnitude &lt; 1e-6:
        return

    radial = &#91;radial&#91;0] / magnitude, radial&#91;1] / magnitude, radial&#91;2] / magnitude]

    world_up = &#91;0.0, 1.0, 0.0]
    tangent = &#91;
        world_up&#91;1] * radial&#91;2] - world_up&#91;2] * radial&#91;1],
        world_up&#91;2] * radial&#91;0] - world_up&#91;0] * radial&#91;2],
        world_up&#91;0] * radial&#91;1] - world_up&#91;1] * radial&#91;0],
    ]

    tangent_len = math.sqrt(tangent&#91;0] ** 2 + tangent&#91;1] ** 2 + tangent&#91;2] ** 2)

    if tangent_len &lt; 1e-6:
        world_forward = &#91;0.0, 0.0, 1.0]
        tangent = &#91;
            world_forward&#91;1] * radial&#91;2] - world_forward&#91;2] * radial&#91;1],
            world_forward&#91;2] * radial&#91;0] - world_forward&#91;0] * radial&#91;2],
            world_forward&#91;0] * radial&#91;1] - world_forward&#91;1] * radial&#91;0],
        ]
        tangent_len = math.sqrt(tangent&#91;0] ** 2 + tangent&#91;1] ** 2 + tangent&#91;2] ** 2)

    if tangent_len &lt; 1e-6:
        return

    tangent = &#91;tangent&#91;0] / tangent_len, tangent&#91;1] / tangent_len, tangent&#91;2] / tangent_len]
    rotation = cmds.angleBetween(euler=True, v1=&#91;1, 0, 0], v2=tangent)
    cmds.xform(grp, worldSpace=True, rotation=rotation)


# -------------------------
# Rig building
# -------------------------

def create_joint_chain(positions, name_prefix, start_index=1):
    """按位置顺序创建关节链。"""
    if not positions:
        return &#91;]

    joints = &#91;]

    joint_name = "{}_{:03d}_jnt".format(name_prefix, start_index)
    current_joint = cmds.joint(name=joint_name, position=positions&#91;0])
    joints.append(current_joint)

    for i in range(1, len(positions)):
        joint_index = start_index + i
        joint_name = "{}_{:03d}_jnt".format(name_prefix, joint_index)
        cmds.select(current_joint, replace=True)
        current_joint = cmds.joint(name=joint_name, position=positions&#91;i])
        joints.append(current_joint)

    if joints:
        cmds.joint(
            joints&#91;0],
            edit=True,
            orientJoint='xyz',
            secondaryAxisOrient='yup',
            zeroScaleOrient=True,
            children=True
        )

    return joints


def create_constraints(controller, joint):
    """为控制器和关节创建 parentConstraint（无 offset）。"""
    if not controller or not joint:
        return None
    if not cmds.objExists(controller) or not cmds.objExists(joint):
        return None

    parent_constraint_name = "{}_parentConstraint".format(joint)
    if cmds.objExists(parent_constraint_name):
        cmds.delete(parent_constraint_name)

    parent_constraint = cmds.parentConstraint(
        controller,
        joint,
        name=parent_constraint_name,
        maintainOffset=False
    )

    return parent_constraint


def create_controller_at_position(position, index, name_prefix, ctrl_radius, ctrl_rgb):
    """在指定位置创建控制器（circle）+ group，并设置颜色与朝向。"""
    ctrl_name = "{}_{:03d}_ctrl".format(name_prefix, index)
    ctrl = cmds.circle(name=ctrl_name, radius=float(ctrl_radius), normal=&#91;0, 1, 0])&#91;0]

    _set_ctrl_color_rgb(ctrl, ctrl_rgb)

    cmds.xform(ctrl, centerPivots=True)

    grp_name = "{}_grp".format(ctrl_name)
    grp = cmds.group(ctrl, name=grp_name)

    cmds.xform(grp, worldSpace=True, translation=position)
    aim_controller_group_to_world_center(grp, position)

    return grp, ctrl


def create_controllers_from_positions(vertex_positions, base_name, ctrl_radius=1.0, ctrl_rgb=(1.0, 1.0, 0.0)):
    """从位置列表创建：组 + 关节链 + 控制器链 + 约束。"""
    if not vertex_positions:
        cmds.warning("没有顶点位置数据！\nNo vertex positions provided!")
        return

    if not base_name or not base_name.strip():
        cmds.warning("名称为空！\nName is empty!")
        return

    try:
        ctrl_radius = float(ctrl_radius)
    except Exception:
        ctrl_radius = 1.0

    if ctrl_radius &lt;= 0.0:
        cmds.warning("控制器大小必须大于 0，已使用默认值 1。\nController size must be > 0. Using default 1.")
        ctrl_radius = 1.0

    base_name = base_name.strip()

    suffix = get_available_suffix(base_name)
    name_prefix = "{}_{}".format(base_name, suffix)

    main_grp = "{}_rig_grp".format(name_prefix)
    joint_grp = "{}_joints_grp".format(name_prefix)
    ctrl_grp = "{}_controllers_grp".format(name_prefix)

    if not cmds.objExists(main_grp):
        main_grp = cmds.group(empty=True, name=main_grp)

    if not cmds.objExists(joint_grp):
        joint_grp = cmds.group(empty=True, name=joint_grp)
        cmds.parent(joint_grp, main_grp)
    else:
        p = cmds.listRelatives(joint_grp, parent=True) or &#91;]
        if not p or p&#91;0] != main_grp:
            cmds.parent(joint_grp, main_grp)

    if not cmds.objExists(ctrl_grp):
        ctrl_grp = cmds.group(empty=True, name=ctrl_grp)
        cmds.parent(ctrl_grp, main_grp)
        start_index = 1
    else:
        p = cmds.listRelatives(ctrl_grp, parent=True) or &#91;]
        if not p or p&#91;0] != main_grp:
            cmds.parent(ctrl_grp, main_grp)
        existing_ctrls = cmds.listRelatives(ctrl_grp, children=True, type="transform") or &#91;]
        start_index = len(existing_ctrls) + 1

    created_joints = create_joint_chain(vertex_positions, name_prefix, start_index)

    if created_joints:
        cmds.parent(created_joints&#91;0], joint_grp)

    created_controllers = &#91;]
    parent_ctrl = None

    for i, (pos, joint) in enumerate(zip(vertex_positions, created_joints)):
        ctrl_index = start_index + i
        ctrl_grp_item, ctrl_item = create_controller_at_position(
            pos, ctrl_index, name_prefix,
            ctrl_radius=ctrl_radius,
            ctrl_rgb=ctrl_rgb
        )

        if not cmds.objExists(ctrl_grp_item):
            continue

        if parent_ctrl is None:
            cmds.parent(ctrl_grp_item, ctrl_grp)
        else:
            if ctrl_item and cmds.objExists(parent_ctrl):
                cmds.parent(ctrl_grp_item, parent_ctrl)
            else:
                cmds.parent(ctrl_grp_item, ctrl_grp)

        created_controllers.append(ctrl_grp_item)

        if ctrl_item and joint and cmds.objExists(ctrl_item) and cmds.objExists(joint):
            create_constraints(ctrl_item, joint)

        if ctrl_item:
            parent_ctrl = ctrl_item

    existing_controllers = &#91;c for c in created_controllers if cmds.objExists(c)]
    if existing_controllers:
        cmds.select(existing_controllers, replace=True)

    cmds.confirmDialog(
        title="完成 / Complete",
        message="成功创建 {} 个关节和 {} 个控制器！\nSuccessfully created {} joints and {} controllers!\n\n使用命名前缀 / Name prefix: {}".format(
            len(created_joints), len(created_controllers),
            len(created_joints), len(created_controllers),
            name_prefix
        ),
        button=&#91;"确定 / OK"],
        defaultButton="确定 / OK"
    )
    return created_joints, created_controllers


# =========================
# UI: Record vertex order + Create (Vertical Layout, Shift-click optimized)
# =========================

class VertexOrderUI(object):
    WIN = "createCtrlsFromVtx_orderUI"

    def __init__(self):
        self.job_id = None

        self.vertices = &#91;]
        self._last_ordered_vtx = &#91;]
        self._recording = False
        self._building = False

        self._title_base = "从顶点创建控制器 / Create Ctrls From Vtx (Ordered)"

        # UI controls
        self.name_field = None
        self.size_field = None
        self.color_field = None
        self.list_ui = None
        self.record_btn = None
        self.clear_btn = None
        self.create_btn = None
        self.status_txt = None

    # ---- Window title

    def _set_window_title(self, recording=False):
        if not cmds.window(self.WIN, exists=True):
            return
        title = self._title_base
        if recording:
            title = "● Recording  |  " + title
        cmds.window(self.WIN, edit=True, title=title)

    # ---- Create button state

    def _update_create_button_state(self):
        if not self.create_btn or not cmds.control(self.create_btn, exists=True):
            return
        enable = len(self.vertices) > 0
        cmds.button(self.create_btn, edit=True, enable=enable)

    # ---- UI

    def show(self):
        if cmds.window(self.WIN, exists=True):
            cmds.deleteUI(self.WIN)

        cmds.window(self.WIN, title=self._title_base, sizeable=False)
        cmds.columnLayout(adjustableColumn=True, rowSpacing=8)

        # Settings
        cmds.frameLayout(label="参数 / Settings", collapsable=True, collapse=False, marginWidth=8, marginHeight=6)
        cmds.columnLayout(adjustableColumn=True, rowSpacing=6)

        self.name_field = cmds.textFieldGrp(
            label="名称 Name",
            text="Skirt",
            columnAlign=(1, "right"),
            columnWidth=&#91;(1, 90), (2, 320)]
        )

        self.size_field = cmds.floatFieldGrp(
            label="大小 Size",
            numberOfFields=1,
            value1=1.0,
            columnAlign=(1, "right"),
            columnWidth=&#91;(1, 90), (2, 120)]
        )

        self.color_field = cmds.colorSliderGrp(
            label="颜色 Color",
            rgb=(1.0, 1.0, 0.0),
            columnAlign=(1, "right"),
            columnWidth=&#91;(1, 90), (2, 220)]
        )

        cmds.text(label="提示 / Tip：Shift 点选最稳定（按新增顺序记录）", align="left")

        cmds.setParent("..")
        cmds.setParent("..")

        # Recording
        cmds.frameLayout(label="记录 / Recording", collapsable=True, collapse=False, marginWidth=8, marginHeight=6)
        cmds.columnLayout(adjustableColumn=True, rowSpacing=6)

        self.record_btn = cmds.button(
            label="开始记录 / Start Recording",
            height=34,
            bgc=BTN_PRIMARY_GREEN,
            command=lambda *_: self.toggle_recording()
        )

        cmds.button(
            label="添加当前选择 / Add Selection",
            height=26,
            bgc=BTN_SECONDARY,
            command=lambda *_: self.add_current_selection()
        )

        cmds.button(
            label="撤销最后 / Undo Last",
            height=26,
            bgc=BTN_SECONDARY,
            command=lambda *_: self.pop_last()
        )

        self.clear_btn = cmds.button(
            label="清空 / Clear",
            height=30,
            bgc=BTN_WARN_ORANGE,
            command=lambda *_: self.clear()
        )

        self.status_txt = cmds.text(label="状态 / Status：未记录 / Idle（已记录 0）", align="left")

        cmds.setParent("..")
        cmds.setParent("..")

        # List
        cmds.frameLayout(label="顶点列表（按顺序）/ Vertex List (Ordered)", collapsable=False, marginWidth=8, marginHeight=6)
        cmds.columnLayout(adjustableColumn=True, rowSpacing=6)

        self.list_ui = cmds.textScrollList(
            numberOfRows=12,
            allowMultiSelection=True,
            height=220,
            doubleClickCommand=lambda *_: self.select_and_frame_from_list()
        )

        cmds.setParent("..")
        cmds.setParent("..")

        # Actions
        cmds.frameLayout(label="操作 / Actions", collapsable=False, marginWidth=8, marginHeight=6)
        cmds.columnLayout(adjustableColumn=True, rowSpacing=6)

        cmds.button(
            label="移除选中条目 / Remove Selected",
            height=26,
            bgc=BTN_SECONDARY,
            command=lambda *_: self.remove_selected_items()
        )

        cmds.button(
            label="选中并框显 / Select &amp; Frame",
            height=26,
            bgc=BTN_SECONDARY,
            command=lambda *_: self.select_and_frame_from_list()
        )

        self.create_btn = cmds.button(
            label="从列表创建 / Create From List",
            height=40,
            bgc=BTN_PRIMARY_BLUE,
            command=lambda *_: self.create_from_list()
        )

        cmds.button(
            label="关闭 / Close",
            height=26,
            bgc=BTN_SECONDARY,
            command=lambda *_: cmds.deleteUI(self.WIN)
        )

        cmds.setParent("..")
        cmds.setParent("..")

        # About (links open browser; URLs not shown in UI)
        cmds.frameLayout(label="关于 / About", collapsable=True, collapse=True, marginWidth=8, marginHeight=6)
        cmds.columnLayout(adjustableColumn=True, rowSpacing=6)

        cmds.text(label="作者 / Author：{}".format(AUTHOR_NAME), align="left")

        cmds.button(
            label="个人博客 / Blog",
            height=26,
            bgc=BTN_SECONDARY,
            command=lambda *_: _open_url(URL_BLOG)
        )
        cmds.button(
            label="YouTube / YouTube",
            height=26,
            bgc=BTN_SECONDARY,
            command=lambda *_: _open_url(URL_YOUTUBE)
        )
        cmds.button(
            label="Bilibili / Bilibili",
            height=26,
            bgc=BTN_SECONDARY,
            command=lambda *_: _open_url(URL_BILIBILI)
        )

        cmds.setParent("..")
        cmds.setParent("..")

        cmds.showWindow(self.WIN)

        # initial states
        self._set_window_title(recording=False)
        self._update_create_button_state()

    # ---- Internal

    def _set_status(self, cn, en):
        count = len(self.vertices)
        if self.status_txt and cmds.control(self.status_txt, exists=True):
            cmds.text(self.status_txt, edit=True, label="状态 / Status：{} / {}（已记录 {}）".format(cn, en, count))

    def _refresh_list(self):
        if not self.list_ui or not cmds.control(self.list_ui, exists=True):
            return
        cmds.textScrollList(self.list_ui, edit=True, removeAll=True)
        for v in self.vertices:
            cmds.textScrollList(self.list_ui, edit=True, append=v)
        self._update_create_button_state()

    def _install_job(self):
        try:
            cmds.selectPref(trackSelectionOrder=True)
        except Exception:
            pass

        if self.job_id and cmds.scriptJob(exists=self.job_id):
            cmds.scriptJob(kill=self.job_id, force=True)
            self.job_id = None

        self.job_id = cmds.scriptJob(
            event=&#91;"SelectionChanged", self._on_selection_changed],
            protected=True,
            parent=self.WIN
        )

    def _get_ordered_selected_vtx(self):
        ordered = cmds.ls(orderedSelection=True, flatten=True, long=True) or &#91;]
        return &#91;x for x in ordered if ".vtx&#91;" in x]

    # ---- Recording (Shift-click optimized)

    def toggle_recording(self):
        self._recording = not self._recording

        if self._recording:
            try:
                cmds.selectPref(trackSelectionOrder=True)
            except Exception:
                pass

            self._last_ordered_vtx = self._get_ordered_selected_vtx()
            self._install_job()

            cmds.button(self.record_btn, edit=True, label="停止记录 / Stop Recording", bgc=BTN_PRIMARY_RED)
            self._set_window_title(recording=True)
            self._set_status("记录中（Shift 点选）", "Recording (Shift-click)")
        else:
            cmds.button(self.record_btn, edit=True, label="开始记录 / Start Recording", bgc=BTN_PRIMARY_GREEN)
            self._set_window_title(recording=False)
            self._set_status("未记录", "Idle")

    def _on_selection_changed(self):
        if not self._recording or self._building:
            return

        cur_ordered = self._get_ordered_selected_vtx()
        prev_set = set(self._last_ordered_vtx)
        self._last_ordered_vtx = cur_ordered

        added = &#91;v for v in cur_ordered if v not in prev_set]
        if not added:
            return

        if len(added) > 1:
            cmds.warning(
                "一次新增多个顶点（可能框选/套索），内部顺序可能不稳定；Shift 逐个点选最稳。\n"
                "Multiple vertices added at once; internal order may be unstable."
            )

        changed = False
        for v in added:
            if v not in self.vertices:
                self.vertices.append(v)
                changed = True

        if changed:
            self._refresh_list()
            self._set_status("记录中", "Recording")

    # ---- List ops

    def add_current_selection(self):
        cur_ordered = self._get_ordered_selected_vtx()
        if not cur_ordered:
            cmds.warning("当前选择里没有顶点！\nNo vertices in current selection!")
            return
        for v in cur_ordered:
            if v not in self.vertices:
                self.vertices.append(v)
        self._refresh_list()
        self._set_status("已添加当前选择", "Selection added")

    def clear(self):
        self.vertices = &#91;]
        self._refresh_list()
        self._set_status("已清空", "Cleared")

    def pop_last(self):
        if self.vertices:
            self.vertices.pop()
            self._refresh_list()
            self._set_status("已撤销最后一个", "Undid last")

    def remove_selected_items(self):
        if not self.list_ui or not cmds.control(self.list_ui, exists=True):
            return
        selected = cmds.textScrollList(self.list_ui, query=True, selectItem=True) or &#91;]
        if not selected:
            return
        s = set(selected)
        self.vertices = &#91;v for v in self.vertices if v not in s]
        self._refresh_list()
        self._set_status("已移除选中条目", "Removed selected")

    def select_and_frame_from_list(self):
        if not self.list_ui or not cmds.control(self.list_ui, exists=True):
            return
        items = cmds.textScrollList(self.list_ui, query=True, selectItem=True) or &#91;]
        if not items:
            return

        existing = &#91;v for v in items if cmds.objExists(v)]
        if not existing:
            cmds.warning("所选条目在场景中不存在（可能 mesh 被删除/重命名）。\nSelected items not found in scene.")
            return

        try:
            cmds.select(existing, replace=True)
        except Exception:
            return

        try:
            cmds.viewFit()
        except Exception:
            pass

    # ---- Create

    def _get_ui_options(self):
        base_name = cmds.textFieldGrp(self.name_field, query=True, text=True).strip()

        try:
            size_val = float(cmds.floatFieldGrp(self.size_field, query=True, value1=True))
        except Exception:
            size_val = 1.0

        try:
            rgb = cmds.colorSliderGrp(self.color_field, query=True, rgb=True)
            rgb = (float(rgb&#91;0]), float(rgb&#91;1]), float(rgb&#91;2]))
        except Exception:
            rgb = (1.0, 1.0, 0.0)

        return base_name, size_val, rgb

    def create_from_list(self):
        if not self.vertices:
            cmds.warning("列表为空！请先记录/添加顶点。\nList is empty! Please record/add vertices first.")
            self._update_create_button_state()
            return

        base_name, size_val, rgb = self._get_ui_options()
        if not base_name:
            cmds.warning("名称为空！\nName is empty!")
            return

        positions = &#91;]
        for v in self.vertices:
            if not cmds.objExists(v):
                cmds.warning("顶点不存在，已跳过：{}\nVertex not found, skipped: {}".format(v, v))
                continue
            pos = cmds.xform(v, query=True, worldSpace=True, translation=True)
            if pos:
                positions.append(pos)

        if not positions:
            cmds.warning("没有可用的顶点位置！\nNo valid vertex positions!")
            return

        self._building = True
        try:
            create_controllers_from_positions(
                positions,
                base_name,
                ctrl_radius=size_val,
                ctrl_rgb=rgb
            )
        finally:
            self._building = False


def show_create_controllers_ui():
    ui = VertexOrderUI()
    ui.show()
    return ui


if __name__ == "__main__":
    show_create_controllers_ui()</code></pre>
<p><a href="https://www.miaodonghua.com/3553.html">Maya 裙子绑定神器｜按顶点顺序创建控制器 &amp; 关节链｜Skirt Rigging Tool</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Maya Curve Rigger｜线条类道具一键绑定（Rope / Tail / Tentacle Rig）</title>
		<link>https://www.miaodonghua.com/3550.html</link>
		
		<dc:creator><![CDATA[喵喵动画屋]]></dc:creator>
		<pubDate>Sun, 14 Dec 2025 15:39:19 +0000</pubDate>
				<category><![CDATA[MAYA绑定]]></category>
		<guid isPermaLink="false">https://www.miaodonghua.com/?p=3550</guid>

					<description><![CDATA[<p>This video demonstrates how to rig flexible props such  &#8230; </p>
<p class="link-more"><a href="https://www.miaodonghua.com/3550.html" class="more-link">继续阅读<span class="screen-reader-text">“Maya Curve Rigger｜线条类道具一键绑定（Rope / Tail / Tentacle Rig）”</span></a></p>
<p><a href="https://www.miaodonghua.com/3550.html">Maya Curve Rigger｜线条类道具一键绑定（Rope / Tail / Tentacle Rig）</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<pre class="wp-block-code has-vivid-green-cyan-color has-black-background-color has-text-color has-background has-link-color wp-elements-bf4ee23ff0e4304b7adaed6a619ed75e"><code>import maya.cmds as cmds
import maya.mel as mel
import math
import sys
from maya.cmds import*
class Curve_Rigger:
    def __init__(self):
        print("请选择起始和结束定位器 / Select start and end locators")
        pass

    def FolRivet(self):
        sel=ls(sl=1)
        index=len(sel)
        shape=listRelatives(sel&#91;-1],type='shape')&#91;0]
        if objExists("Rivet_Fol_Group"):
            grp="Rivet_Fol_Group"
        else:
            grp=group(em=1,n="Rivet_Fol_Group")
        for i in range(0,index-1):
            loc=sel&#91;i]
            decompNode=shadingNode('decomposeMatrix',au=1)
            if nodeType(shape)=='mesh':
                cposNode=shadingNode('closestPointOnMesh',au=1)
                connectAttr(shape+'.worldMatrix&#91;0]',cposNode+'.inputMatrix')
                connectAttr(shape+'.outMesh',cposNode+'.inMesh')
            elif nodeType(shape)=='nurbsSurface':
                cposNode=shadingNode('closestPointOnSurface',au=1)
                connectAttr(shape+'.worldSpace',cposNode+'.inputSurface')
            connectAttr(loc+'.worldMatrix&#91;0]',decompNode+'.inputMatrix')
            connectAttr(decompNode+'.outputTranslate',cposNode+'.inPosition')
            UVal=getAttr(cposNode+'.parameterU')
            VVal=getAttr(cposNode+'.parameterV')
            follicle_name = loc + "_fol"
            follicle_shape_name = follicle_name + "Shape"
            # 创建毛囊 / Create follicle
            follicle_shape = createNode("follicle", name=follicle_shape_name)
            follicle = listRelatives(follicle_shape, p=1)&#91;0]
            rename(follicle,follicle_name)
            if nodeType(shape)=='mesh':
                connectAttr(shape + ".worldMesh&#91;0]", follicle_shape + ".inputMesh")
                connectAttr(shape+'.worldMatrix&#91;0]',follicle_shape+'.inputWorldMatrix')
            elif nodeType(shape)=='nurbsSurface':
                connectAttr(shape + ".local", follicle_shape + ".inputSurface")
                connectAttr(shape+'.worldMatrix&#91;0]',follicle_shape+'.inputWorldMatrix')
            setAttr(follicle_shape+'.parameterU',UVal)
            setAttr(follicle_shape+'.parameterV',VVal)
            connectAttr(follicle_shape+'.outTranslate',follicle+'.t')
            connectAttr(follicle_shape+'.outRotate',follicle+'.r')
            parentConstraint(follicle,loc,mo=1)
            parent(follicle,grp)
            delete(cposNode,decompNode)
    def create_cylinder_with_locators(self,name, start_locator, end_locator, radius=0.5, divisions=20):
        # 获取定位器的位置 / Get the positions of the locators
        start_position = cmds.xform(start_locator, query=True, translation=True, worldSpace=True)
        end_position = cmds.xform(end_locator, query=True, translation=True, worldSpace=True)
    
        # 计算圆柱体的高度和方向向量 / Calculate the height and direction vector of the cylinder
        height = math.sqrt(sum((end - start) ** 2 for start, end in zip(start_position, end_position)))
        direction = &#91;(end - start) / height for start, end in zip(start_position, end_position)]
    
        # 使用指定参数创建圆柱体 / Create a cylinder with the specified parameters
        cylinder = cmds.polyCylinder(n=name, radius=radius, height=height, subdivisionsY=divisions, axis=direction)&#91;0]
    
        # 将圆柱体定位在两个定位器的中点 / Position the cylinder at the midpoint between the locators
        midpoint = &#91;(start + end) / 2 for start, end in zip(start_position, end_position)]
        cmds.move(midpoint&#91;0], midpoint&#91;1], midpoint&#91;2], cylinder)
    
        return cylinder
    
    def create_curve_from_objects(self,objects):
        store_positions = &#91;cmds.xform(obj, query=True, worldSpace=True, translation=True) for obj in objects]
        degree = 3  # 如需更改度数请在此修改 / Change the degree here if needed
        
        build_curve = "curve -d {} ".format(degree)
        for pos in store_positions:
            build_curve += "-p {} {} {} ".format(pos&#91;0], pos&#91;1], pos&#91;2])
        
        return mel.eval(build_curve)
    
    def create_locator_in_direction(self,locator1, locator2):
        pos1 = cmds.pointPosition(locator1)
        pos2 = cmds.pointPosition(locator2)
        # 计算方向和距离 / Calculate direction and distance
        direction_vector = &#91;(pos2&#91;0] - pos1&#91;0]), (pos2&#91;1] - pos1&#91;1]), (pos2&#91;2] - pos1&#91;2])]
        direction_length = math.sqrt(sum(v ** 2 for v in direction_vector))
        normalized_direction = &#91;(v / direction_length) * 10 * direction_length for v in direction_vector]
        new_locator_pos = &#91;pos1&#91;i] + normalized_direction&#91;i] for i in range(3)]
        
        # 创建新定位器 / Create new locator
        new_locator = cmds.spaceLocator(name="new_locator")&#91;0]
        cmds.move(new_locator_pos&#91;0], new_locator_pos&#91;1], new_locator_pos&#91;2], new_locator)
        return new_locator
    
    def create_control_curve(self,pref, shape_type,obj,col):
        if shape_type == 'circle':
            ctrl = cmds.circle(ch=0, n=pref +obj.replace('Jnt','Ctrl'), r=0.5, nr=(1, 0, 0))&#91;0]
        elif shape_type == 'cube':
            mel.eval('$ctrl =`curve -d 1 -p 0.5 0.5 0.5 -p 0.5 0.5 -0.5 -p -0.5 0.5 -0.5 -p -0.5 -0.5 -0.5 -p 0.5 -0.5 -0.5 -p 0.5 0.5 -0.5 -p -0.5 0.5 -0.5 -p -0.5 0.5 0.5 -p 0.5 0.5 0.5 -p 0.5 -0.5 0.5 -p 0.5 -0.5 -0.5 -p -0.5 -0.5 -0.5 -p -0.5 -0.5 0.5 -p 0.5 -0.5 0.5 -p -0.5 -0.5 0.5 -p -0.5 0.5 0.5 -k 0 -k 1 -k 2 -k 3 -k 4 -k 5 -k 6 -k 7 -k 8 -k 9 -k 10 -k 11 -k 12 -k 13 -k 14 -k 15 -n "'+pref +obj.replace('Jnt','Ctrl')+'" `;')  # 立方体曲线定义 / Replace with cube curve definition
            ctrl = cmds.rename(pref +obj.replace('Jnt','Ctrl'))
            shape = cmds.pickWalk(d="down")&#91;0]
            cmds.rename(shape, ctrl + "Shape")
        
        ctrl_sdk_grp = cmds.group(n=pref +obj.replace('Jnt','Ctrl')+'_SdkGrp')
        ctrl_off_grp = cmds.group(n=pref +obj.replace('Jnt','Ctrl')+'_OffGrp')
        
        # 自定义控制器外观 / Customize control appearance
        cmds.setAttr(ctrl + '.overrideEnabled', 1)
        cmds.setAttr(ctrl + '.overrideColor', col)
        
        sub_ctrl = cmds.circle(ch=0, n=pref +obj.replace('Jnt','Sub_Ctrl'))&#91;0]
        sub_ctrl_sdk_grp = cmds.group(n=pref +obj.replace('Jnt','Sub_Ctrl')+'_SdkGrp')
        sub_ctrl_off_grp = cmds.group(n=pref +obj.replace('Jnt','Sub_Ctrl')+'_OffGrp')
        
        # 自定义子控制器外观 / Customize sub control appearance
        cmds.setAttr(sub_ctrl + '.overrideEnabled', 1)
        cmds.setAttr(sub_ctrl + '.overrideColor', 13)
        
        cmds.parent(sub_ctrl_off_grp, ctrl)
        cmds.addAttr(ctrl, ln='Sub_Ctrl_Vis', at='bool', h=0, k=1, r=1)
        cmds.connectAttr(ctrl + '.Sub_Ctrl_Vis', sub_ctrl_off_grp + '.v')
        delete(parentConstraint(obj,ctrl_off_grp,mo=0))
        return ctrl
    
    def create_joints_between_objects(self,num_joints, prefix=''):
        joints = &#91;]
        selObj=ls(sl=1)
        start_obj = selObj&#91;0]
        end_obj = selObj&#91;1]
        for i in range(num_joints + 2):
            select(cl=1)
            t = float(i) / float(num_joints + 1)
            joint_name = '{}_{:02d}_Jnt'.format(prefix, i)
            new_joint = cmds.joint(name=joint_name)
            
            start_pos = cmds.xform(start_obj, q=1, t=1, worldSpace=True)
            end_pos = cmds.xform(end_obj, q=1, t=1, worldSpace=True)
            joint_pos = &#91;
                start_pos&#91;0] + (end_pos&#91;0] - start_pos&#91;0]) * t,
                start_pos&#91;1] + (end_pos&#91;1] - start_pos&#91;1]) * t,
                start_pos&#91;2] + (end_pos&#91;2] - start_pos&#91;2]) * t
            ]
            cmds.xform(new_joint, translation=joint_pos, worldSpace=True)
            if not i ==  num_joints + 1:
                delete(aimConstraint(end_obj,new_joint,aim=(1,0,0),u=(0,1,0),wut='vector',wu=(0,1,0)))
            else:
                delete(aimConstraint(start_obj,end_obj,new_joint,aim=(-1,0,0),u=(0,1,0),wut='vector',wu=(0,1,0)))
            select(new_joint)
            if i>>0:
                makeIdentity( a = True, t =0, r= 1, s =0, n= 0, pn= 1);
            joints.append(new_joint)
        
        return joints
    
    def create_ui(self,path=''):
        window_name = "RigNet_Tools"
        if cmds.window(window_name, exists=True):
            cmds.deleteUI(window_name)
        cmds.window(window_name, title="曲线绑定工具 / Curve Rigger Tools",w=350,h=430)
        
        form=cmds.formLayout(numberOfDivisions=100,w = 350)
        txt=cmds.text(label="蒙皮关节数量 / Number of Skn Joints:")
        skn_joints_field = cmds.intField(minValue=1, value=10,w=80)
        
        FkTxt=cmds.text(label="控制器数量 / Number of Controls:")
        flst_field = cmds.intField(minValue=1, value=5,w=80)
        IKChk=cmds.checkBox(label="IK控制器 / IK Controls")
        TwistChk=cmds.checkBox(label="启用扭曲 / Enable Twist")
        FKChk=cmds.checkBox(label="FK控制器 / FK Controls")
        RevChk=cmds.checkBox(label="反向FK控制器 / RevFK Controls")
        PathChk=cmds.checkBox(label="路径控制器 / Path Controls",cc=lambda state: self.enable_flst_field(state,path_joints_field))
        PText = cmds.text(label="路径控制器数量 / Number of Path Controls:",)
        path_joints_field = cmds.intField(minValue=1, value=50,en=0,w=80)
        but=cmds.button(label="创建绑定 / Create Rig", command=lambda args: self.create_rig(skn_joints_field, flst_field,path_joints_field,IKChk,FKChk,RevChk,PathChk,TwistChk),bgc=(0.15,0.15,0.15),w=310,h=40)
        transfer_but=cmds.button(label="转移蒙皮权重 / Transfer Skin Weights", command=lambda args: self.transfer_skin_weights(),bgc=(0.2,0.3,0.2),w=310,h=40)
        cmds.formLayout( form, edit=True,
    	
    	attachForm=&#91;
    	(txt, 'top',20),
    	
    	(txt, 'left',20),
    	
    	(skn_joints_field, 'top',17),
    	
    	(skn_joints_field, 'left',260),
    	(flst_field, 'top',37),
    	
    	(flst_field, 'left',260),
        (IKChk, 'top', 65),
    	(IKChk, 'left', 20),
    	
    	(TwistChk, 'top', 65),
    	
    	(TwistChk, 'left', 200),
    	
    	(FkTxt, 'top', 40),
    	
    	(FkTxt, 'left', 20),
    	(FKChk, 'top', 90),
    	
    	(FKChk, 'left', 20),
    	(RevChk, 'top', 90),
    	
    	(RevChk, 'left', 200),
    	
    	(PathChk, 'top', 140),
    	
    	(PathChk, 'left', 20),
    	(PText, 'top', 115),
    	
    	(PText, 'left', 20),
    	(path_joints_field, 'top', 112),
    	
    	(path_joints_field, 'left', 260),
    	(but, 'top', 170),
    	
    	(but, 'left', 20),
    	(transfer_but, 'top', 220),
    	
    	(transfer_but, 'left', 20)
    
    
    
    
    
    	])
        cmds.showWindow(window_name)
    def enable_flst_field(self,state, path_joints_field):
        cmds.intField(path_joints_field, edit=True, enable=state,bgc=(0.0,0.0,0.0))
    
    def transfer_skin_weights(self):
        # 获取选择的模型 / Get selected model
        selection = cmds.ls(sl=True)
        if not selection:
            cmds.warning("请先选择目标模型 / Please select target model first")
            return
        
        # 检查源模型是否存在 / Check if source model exists
        source_mesh = "Dummy_Mesh"
        if not cmds.objExists(source_mesh):
            cmds.warning("源模型 'Dummy_Mesh' 不存在 / Source model 'Dummy_Mesh' does not exist")
            return
        
        # 获取源模型的蒙皮簇 / Get skin cluster from source mesh
        source_skin_cluster = None
        # 获取网格的shape节点 / Get mesh shape node
        source_shapes = cmds.listRelatives(source_mesh, shapes=True, type='mesh')
        if not source_shapes:
            cmds.warning("源模型没有网格形状 / Source model has no mesh shape")
            return
        
        source_shape = source_shapes&#91;0]
        # 从shape节点查找蒙皮簇 / Find skin cluster from shape node
        skin_clusters = cmds.listConnections(source_shape, type='skinCluster', source=True, destination=False)
        if not skin_clusters:
            # 尝试从历史记录中查找 / Try to find from history
            history = cmds.listHistory(source_shape, pruneDagObjects=True)
            if history:
                for node in history:
                    if cmds.nodeType(node) == 'skinCluster':
                        skin_clusters = &#91;node]
                        break
        
        if skin_clusters:
            source_skin_cluster = skin_clusters&#91;0]
        else:
            cmds.warning("源模型没有蒙皮簇 / Source model has no skin cluster")
            return
        
        # 获取蒙皮关节 / Get skin joints
        skin_joints = cmds.skinCluster(source_skin_cluster, query=True, influence=True)
        if not skin_joints:
            cmds.warning("无法获取蒙皮关节 / Cannot get skin joints")
            return
        
        # 处理每个选择的对象 / Process each selected object
        for target_mesh in selection:
            # 检查是否为网格 / Check if it's a mesh
            target_shapes = cmds.listRelatives(target_mesh, shapes=True, type='mesh')
            if not target_shapes:
                cmds.warning("'{}' 不是网格对象 / '{}' is not a mesh object".format(target_mesh, target_mesh))
                continue
            
            target_shape = target_shapes&#91;0]
            
            # 检查目标模型是否已有蒙皮簇 / Check if target already has skin cluster
            target_skin_cluster = None
            target_skin_clusters = cmds.listConnections(target_shape, type='skinCluster', source=True, destination=False)
            if not target_skin_clusters:
                # 尝试从历史记录中查找 / Try to find from history
                history = cmds.listHistory(target_shape, pruneDagObjects=True)
                if history:
                    for node in history:
                        if cmds.nodeType(node) == 'skinCluster':
                            target_skin_clusters = &#91;node]
                            break
            if target_skin_clusters:
                target_skin_cluster = target_skin_clusters&#91;0]
            
            # 如果目标模型没有蒙皮簇，创建一个 / Create skin cluster if target doesn't have one
            if not target_skin_cluster:
                # 创建新的蒙皮簇 / Create new skin cluster
                target_skin_cluster = cmds.skinCluster(skin_joints, target_mesh, tsb=True, mi=1)&#91;0]
            
            # 转移蒙皮权重 / Transfer skin weights
            try:
                cmds.copySkinWeights(
                    sourceSkin=source_skin_cluster,
                    destinationSkin=target_skin_cluster,
                    noMirror=True,
                    surfaceAssociation='closestPoint',
                    influenceAssociation=&#91;'oneToOne', 'closestJoint']
                )
                print("成功转移蒙皮权重: {} / Successfully transferred skin weights: {}".format(target_mesh, target_mesh))
            except Exception as e:
                cmds.warning("转移蒙皮权重失败: {} / Failed to transfer skin weights: {}".format(str(e), str(e)))
        
        cmds.select(selection, r=True)
    def create_rig(self,skn_joints_field, flst_field,path_joints_field,IKChk,FKChk,RevChk,PathChk,TwistChk):
        IK=cmds.checkBox(IKChk,q=1,v=1)
        FK=cmds.checkBox(FKChk,q=1,v=1)
        Rev=cmds.checkBox(RevChk,q=1,v=1)
        Path=cmds.checkBox(PathChk,q=1,v=1)
        Twst=cmds.checkBox(TwistChk,q=1,v=1)
        print (IK,FK,Rev,Path)
        num_skn_joints = cmds.intField(skn_joints_field, query=True, value=True)
        num_flst = cmds.intField(flst_field, query=True, value=True)
        pth_jnt=cmds.intField(path_joints_field,q=1,value=1)
        initLoc=ls(sl=1)
        
        # 创建蒙皮关节 / Create SKn Jnt #
        Jlst=self.create_joints_between_objects(num_skn_joints-2,'Skn')
        select(Jlst&#91;0])
        FreezeTransformations()
        
        # 将'locator1'和'locator2'替换为你的定位器名称 / Replace 'locator1' and 'locator2' with the names of your locators
        start_locator = initLoc&#91;0]
        end_locator = initLoc&#91;1]
        
        # 调用函数创建圆柱体 / Call the function to create the cylinder
        cylinder = self.create_cylinder_with_locators("Dummy_Mesh", start_locator, end_locator, radius=0.3, divisions=num_skn_joints-1)
        select(Jlst,cylinder)
        SmoothBindSkin()
        a=-1
        select(cl=1)
        for i in range(len(Jlst)):
            jj=cmds.duplicate(Jlst&#91;a],n='IK_'+Jlst&#91;a])
            grp = group(em=1,n= Jlst&#91;a]+'_Conn_Grp')
            delete(parentConstraint(Jlst&#91;a],grp,mo=0))
            parent(Jlst&#91;a],grp)
            if i>0:
                parent('IK_'+Jlst&#91;a+1],'IK_'+Jlst&#91;a])
                #parent(Jlst&#91;a+1]+'_Conn_Grp',Jlst&#91;a])
            parentConstraint('IK_'+Jlst&#91;a],grp,mo=1)
            #connectAttr('IK_'+Jlst&#91;a]+'.r',grp+'.r')
            a=a-1
        Ik_OffGrp=group(em=1,n='IK_Offset_Val_Grp')
        delete(parentConstraint(Jlst&#91;0],Ik_OffGrp,mo=0))        
        select(initLoc)
        
        # 创建驱动IK关节 / Create Drv IK Jnt #
        Dlst=self.create_joints_between_objects(num_flst-2,'Drv')  
        self.create_curve_from_objects(Dlst)
        crv=ls(sl=1)&#91;0]
        refresh()
        select('IK_'+Jlst&#91;0])
        FreezeTransformations()
        select('IK_'+Jlst&#91;0],'IK_'+Jlst&#91;-1])
        refresh()    
        
        # 创建IK样条 / Create IK Spline #
        ik=ikHandle(sol = 'ikSplineSolver', pcv=0, c=crv, ccv= 0)
        select(Dlst,crv)
        SmoothBindSkin()
        main_Ctrl = circle(ch=0,n='Main_Ctrl',r=5,nr=(0,1,0))&#91;0]
        main_grp = group(n='Main_Ctrl_Grp')
        #delete(parentConstraint(initLoc&#91;0],initLoc&#91;1],main_grp,mo=0))  
        parent('IK_'+Jlst&#91;0],Ik_OffGrp)
        for jJnt in Jlst:
            parent(jJnt+'_Conn_Grp',Ik_OffGrp)
        # 添加拉伸功能 / Adding Stretch #
        addAttr(main_Ctrl,ln='Maintain_Length',min=0,max=1,k=1,at='float')
        crvInfo=arclen(crv,ch=1)
        dis=getAttr(crvInfo+'.arcLength')
        StrGlMD=createNode('multiplyDivide',n='Wire_StrVGlobal_MD')
        connectAttr(crvInfo+'.arcLength',StrGlMD+'.input1X')
        connectAttr(main_Ctrl+'.sx',StrGlMD+'.input2X')
        setAttr(StrGlMD+'.operation',2)
        StrMD=createNode('multiplyDivide',n='Wire_StrVal_MD')
        setAttr(StrMD+'.input2X',dis)
        connectAttr(StrGlMD+'.outputX',StrMD+'.input1X')
        setAttr(StrMD+'.operation',2)
        Tval=getAttr('IK_'+Jlst&#91;-1]+'.tx')
        StrValMD=createNode('multiplyDivide',n='Wire_Stretchy_MD')
        setAttr(StrValMD+'.input1X',Tval)
        connectAttr(StrMD+'.outputX',StrValMD+'.input2X')
        setAttr(StrValMD+'.operation',1)
        StrcBlnd=createNode('blendColors',n='Wire_StrSwitch_MD')
        setAttr('Wire_StrSwitch_MD.color2R',Tval)
        connectAttr(StrValMD+'.outputX','Wire_StrSwitch_MD.color1R')
        connectAttr(main_Ctrl+'.Maintain_Length',StrcBlnd+'.blender')
        for i in range(1,len(Jlst)):
            connectAttr(StrcBlnd+'.outputR','IK_'+Jlst&#91;i]+'.tx')
        FkLst=&#91;]
        IKCtrl=&#91;]
        RevLst=&#91;]
        ConsLst=&#91;]
        IKGrp=&#91;]
        pthLst=&#91;]
        i=0
        if FK == True:
            if Rev == True:
                IK = True
        if IK == True:
            for DJnt in Dlst:
                Ctrl = self.create_control_curve('IK_', 'circle',DJnt,17)
                IKCtrl.append(Ctrl)
        if FK == True:
            Flst=list(Dlst)  
            print(Dlst) 
            Flst.reverse()
            print(Flst)  
            for DJnt in Flst:
                Ctrl = self.create_control_curve('FK_', 'cube',DJnt,20)
                FkLst.append(Ctrl)
                Grp=group(em=1,n=Ctrl.replace('Ctrl','Drv_OffGrp'))
                delete(parentConstraint(Ctrl,Grp,mo=0))
                if i>>0:
                    parent(FkLst&#91;i-1].replace('Ctrl','Ctrl_OffGrp'),Ctrl)
                    parent(FkLst&#91;i-1].replace('Ctrl','Drv_OffGrp'),Grp)
                i=i+1
                connectAttr(Grp+'.t',Ctrl.replace('Ctrl','Ctrl_OffGrp')+'.t')
                connectAttr(Grp+'.r',Ctrl.replace('Ctrl','Ctrl_OffGrp')+'.r')
                connectAttr(Grp+'.s',Ctrl.replace('Ctrl','Ctrl_OffGrp')+'.s')
        i=0
        if Rev == True:
            Revlst=list(Dlst)   
            print(Revlst)     
            for DJnt in Revlst:
                Ctrl = self.create_control_curve('RevFK_', 'cube',DJnt,21)
                RevLst.append(Ctrl)
                Grp=group(em=1,n=Ctrl.replace('Ctrl','Drv_OffGrp'))
                delete(parentConstraint(Ctrl,Grp,mo=0))
                if i>>0:
                    parent(RevLst&#91;i-1].replace('Ctrl','Ctrl_OffGrp'),Ctrl)
                    parent(RevLst&#91;i-1].replace('Ctrl','Drv_OffGrp'),Grp)
                i=i+1
                connectAttr(Grp+'.t',Ctrl.replace('Ctrl','Ctrl_OffGrp')+'.t')
                connectAttr(Grp+'.r',Ctrl.replace('Ctrl','Ctrl_OffGrp')+'.r')
                connectAttr(Grp+'.s',Ctrl.replace('Ctrl','Ctrl_OffGrp')+'.s')
        if IK ==  True:
            i=0
            for DJnt in Dlst:
                parentConstraint(IKCtrl&#91;i].replace('Ctrl','Sub_Ctrl'),DJnt,mo=1)
                i=i+1
        elif FK == True:
            i=0
            for DJnt in Flst:
                parentConstraint(FkLst&#91;i].replace('Ctrl','Sub_Ctrl'),DJnt,mo=1)
                i=i+1
        elif Rev == True:
            i=0
            for DJnt in Dlst:
                parentConstraint(RevLst&#91;i].replace('Ctrl','Sub_Ctrl'),DJnt,mo=1)
                i=i+1
        
        # 其余绑定创建代码 / Rest of the rig creation code #
        a=0
        b=-1
        if IK:
         for ikctrl in IKCtrl:
            IKGrp.append(ikctrl.replace('Ctrl','Ctrl_OffGrp'))
            par=&#91;]
            if FkLst:
                par.append(FkLst&#91;b])
            if RevLst:
                par.append(RevLst&#91;a])
            if par:
                constraint = cmds.parentConstraint(par, ikctrl.replace('Ctrl','Ctrl_OffGrp'),mo=1)&#91;0]
                ConsLst.append(constraint)
            a=a+1
            b=b-1    
        
        if FK and Rev == True:
            addAttr(main_Ctrl,ln='RevFK',min=0,max=10,k=1,at='float')
            inVal=0
            val=10.0/len(Dlst)
            sel =list(IKCtrl)
            for obj in sel:    
                Nobj=obj
                obj = obj.replace('Ctrl','Ctrl_OffGrp')
                par=parentConstraint(Nobj.replace('IK_','FK_'),Nobj.replace('IK_','RevFK_'),obj,mo=1)&#91;0]
                select(par)
                setDrivenKeyframe(cd="Main_Ctrl.RevFK",at=Nobj.replace('IK_','FK_')+'W0',dv=inVal,v=1)
                setDrivenKeyframe(cd="Main_Ctrl.RevFK",at=Nobj.replace('IK_','FK_')+'W0',dv=inVal+val,v=0)
                select(Nobj.replace('IK_','FK_')+'Shape')
                setDrivenKeyframe(cd="Main_Ctrl.RevFK",at='.v',dv=inVal,v=1)
                setDrivenKeyframe(cd="Main_Ctrl.RevFK",at='.v',dv=inVal+val,v=0)
                select(par)
                setDrivenKeyframe(cd="Main_Ctrl.RevFK",at=Nobj.replace('IK_','RevFK_')+'W1',dv=inVal,v=0)
                setDrivenKeyframe(cd="Main_Ctrl.RevFK",at=Nobj.replace('IK_','RevFK_')+'W1',dv=inVal+val,v=1)
                select(Nobj.replace('IK_','RevFK_')+'Shape')
                setDrivenKeyframe(cd="Main_Ctrl.RevFK",at='.v',dv=inVal,v=0)
                setDrivenKeyframe(cd="Main_Ctrl.RevFK",at='.v',dv=inVal+val,v=1)
                inVal=inVal+val
        select (Jlst)
        sets(n='Bind_Skin')
        pJnt=listRelatives(Jlst&#91;0],p=1)&#91;0]
        select(Dlst)
        group(n='Jnt_Grp')
        scaleConstraint('Main_Ctrl','Jnt_Grp',mo=1)
        select(crv,ik&#91;0])
        group(n='Extra_Grp')
        dGrp=group(em=1,n='Drv_Null_Group')
        parent(dGrp,'Extra_Grp')
        parentConstraint('Main_Ctrl',dGrp,mo=1)
        scaleConstraint('Main_Ctrl',dGrp,mo=1)
        if IK == True:
            addAttr(main_Ctrl,ln='IK_Ctrl_Vis',at='bool',k=1)
            group(IKGrp,n='IK_Ctrl_Grp')
            connectAttr('Main_Ctrl.IK_Ctrl_Vis','IK_Ctrl_Grp.v')
            parent('IK_Ctrl_Grp','Main_Ctrl')
        if FK == True:
            
            select(FkLst&#91;-1].replace('Ctrl','Ctrl_OffGrp'))
            FkGrp=group(n='Fk_Ctrl_Group')
            addAttr(main_Ctrl,ln='FK_Ctrl_Vis',at='bool',k=1)
            connectAttr('Main_Ctrl.FK_Ctrl_Vis',FkGrp+'.v')
            parent(FkGrp,'Main_Ctrl')
            parent("FK_Drv_00_Drv_OffGrp",dGrp)         
        if Rev == True:
            select(RevLst&#91;-1].replace('Ctrl','Ctrl_OffGrp'))
            RevFkGrp=group(n='RevFk_Ctrl_Group')
            try:
                addAttr(main_Ctrl,ln='FK_Ctrl_Vis',at='bool',k=1)
            except: pass
            connectAttr('Main_Ctrl.FK_Ctrl_Vis',RevFkGrp+'.v')
            parent(RevFkGrp,'Main_Ctrl')
            parent(RevLst&#91;-1].replace('Ctrl','Drv_OffGrp'),dGrp)                
        if Path == True:
            addAttr(main_Ctrl,ln='Path_Ctrl_Vis',at='bool',k=1)
            self.create_locator_in_direction(initLoc&#91;0],initLoc&#91;1])
            select(initLoc&#91;0],"new_locator")
            PathLst=self.create_joints_between_objects(pth_jnt-2,'Path')
            select(PathLst)
            Pcrv=self.create_curve_from_objects(PathLst)
            noCv=len(PathLst)-1
            rebuildCurve(Pcrv,ch=1,rpo=1,rt=0,end=1,kr=0,kcp=0,kep=1,kt=0,s=noCv,d=3,tol=0.01)
            i=0
            PathLst.reverse()
            for PJnt in PathLst:
                Ctrl = self.create_control_curve('', 'circle',PJnt,13)
                pthLst.append(Ctrl)
                parent(PJnt,Ctrl.replace('Ctrl','Sub_Ctrl'))
                if i>>0:
                    try:
                        parent(pthLst&#91;i-1].replace('Ctrl','Ctrl_OffGrp'),Ctrl)
                    except:
                        pass
                i=i+1
            parent(pthLst&#91;-1].replace('Ctrl','Ctrl_OffGrp'),'Main_Ctrl')
            connectAttr('Main_Ctrl.Path_Ctrl_Vis',pthLst&#91;-1].replace('Ctrl','Ctrl_OffGrp')+'.v')
            DummyJnt=duplicate(PathLst&#91;0],n='Dummy_Jnt')&#91;0]
            delete(pointConstraint(Pcrv,DummyJnt,mo=0))
            DCrv=duplicate(Pcrv)&#91;0]
            setAttr(DummyJnt+'.tz',0.5)
            delete(pointConstraint(DummyJnt,Pcrv,mo=0))
            setAttr(DummyJnt+'.tz',-0.5)
            delete(pointConstraint(DummyJnt,DCrv,mo=0))
            delete(DummyJnt)
            select(Pcrv,DCrv,PathLst)
            SmoothBindSkin()
            select(Pcrv,DCrv)
            surf=loft(n='Path_Surface')
            DeleteHistory(surf&#91;0])
            select(surf&#91;0],PathLst)
            skinCluster(PathLst,surf&#91;0],tsb=1,mi=1)        
            print(surf)
            if not FK and not Rev == True:
                if not IK:
                    select(Dlst,surf&#91;0])
                    self.FolRivet()  
                else: 
                    print('不存在 / Not Present')
                    select(IKGrp,surf&#91;0])
                    self.FolRivet() 
            if FK:
                select("FK_Drv_*_Drv_OffGrp",surf&#91;0])
                self.FolRivet() 
            if Rev:
                select("RevFK_Drv_*_Drv_OffGrp",surf&#91;0])
                self.FolRivet() 
            
            # 创建移动绑定设置 / Create traveling Rig setup
            addAttr(main_Ctrl,ln='Travel',min=0,max=10,k=1,at='float')
            if FK:
                fol=ls("FK_Drv_*_Drv_OffGrp_fol")
                initval=getAttr(fol&#91;-1]+"Shape.parameterV")
                Diff=1.0-initval
                for follicle in fol:
                    shp=listRelatives(follicle,c=1,f=1)&#91;0]
                    inValue=getAttr(shp+'.parameterV')
                    select(shp)
                    setDrivenKeyframe(cd="Main_Ctrl.Travel",at='parameterV',dv=0,v=inValue)
                    setDrivenKeyframe(cd="Main_Ctrl.Travel",at='parameterV',dv=10,v=inValue+Diff)
            if Rev:
                fol=ls("RevFK_Drv_*_Drv_OffGrp_fol")
                initval=getAttr(fol&#91;-1]+"Shape.parameterV")
                Diff=1.0-initval
                for follicle in fol:
                    shp=listRelatives(follicle,c=1,f=1)&#91;0]
                    inValue=getAttr(shp+'.parameterV')
                    select(shp)
                    setDrivenKeyframe(cd="Main_Ctrl.Travel",at='parameterV',dv=0,v=inValue)
                    setDrivenKeyframe(cd="Main_Ctrl.Travel",at='parameterV',dv=10,v=inValue+Diff)
            if not FK and not Rev == True:
                if not IK:
                    fol=ls("Drv_*_Jnt_fol")
                    initval=getAttr(fol&#91;-1]+"|follicleShape.parameterV")
                    Diff=1.0-initval
                    for follicle in fol:
                        shp=listRelatives(follicle,c=1,f=1)&#91;0]
                        inValue=getAttr(shp+'.parameterV')
                        select(shp)
                        setDrivenKeyframe(cd="Main_Ctrl.Travel",at='parameterV',dv=0,v=inValue)
                        setDrivenKeyframe(cd="Main_Ctrl.Travel",at='parameterV',dv=10,v=inValue+Diff)
                if IK:
                    fol=ls("IK_Drv_*_Ctrl_OffGrp_fol")
                    initval=getAttr(fol&#91;-1]+"Shape.parameterV")
                    Diff=1.0-initval
                    for follicle in fol:
                        shp=listRelatives(follicle,c=1,f=1)&#91;0]
                        inValue=getAttr(shp+'.parameterV')
                        select(shp)
                        setDrivenKeyframe(cd="Main_Ctrl.Travel",at='parameterV',dv=0,v=inValue)
                        setDrivenKeyframe(cd="Main_Ctrl.Travel",at='parameterV',dv=10,v=inValue+Diff)
        # 在蒙皮关节上添加扭曲 / Adding Twist on Skin Jnts
        if Twst:                
            select(cl=1)
            DummyJnt=duplicate(Jlst&#91;0],n='Dummy_Jnt')&#91;0]
            delete(pointConstraint(crv,DummyJnt,mo=0))
            TlocLst=&#91;]
            DnumJnt=len(Dlst)-1
            LCrv=duplicate(crv)&#91;0]
            rebuildCurve(LCrv,ch=1,rpo=1,rt=0,end=1,kr=0,kcp=0,kep=1,kt=0,s=DnumJnt,d=3,tol=0.01)
            RCrv=duplicate(LCrv)&#91;0]
            axis=&#91;'tx','ty','tz']
            for ax in axis:
                setAttr(LCrv+'.'+ax,l=0)
                setAttr(RCrv+'.'+ax,l=0)
            setAttr(DummyJnt+'.tz',0.5)
            delete(pointConstraint(DummyJnt,LCrv,mo=0))
            setAttr(DummyJnt+'.tz',-0.5)
            delete(pointConstraint(DummyJnt,RCrv,mo=0))
            delete(DummyJnt)
            select(LCrv,RCrv)
            Tsurf=loft(n='Twist_Surface')
            DeleteHistory(Tsurf&#91;0])
            select(Tsurf&#91;0],Dlst)
            skinCluster(Dlst,Tsurf&#91;0],tsb=1,mi=1)
            for SknJnt in Jlst:
                Tloc=spaceLocator(n=SknJnt.replace('Jnt','_Twst_Loc'))&#91;0]
                Tgrp=group(n=Tloc+'_Grp')
                TlocLst.append(Tgrp)
                delete(parentConstraint(SknJnt,Tgrp,mo=0))
                parentConstraint(Tloc,SknJnt,st=('x','y','z'),sr=('y','z'),mo=1)
            
            select(TlocLst,Tsurf&#91;0])
            self.FolRivet()  
            for grp in TlocLst:
                delete(listRelatives(grp,ad=1,type='constraint'))
                parent(grp,grp+'_fol')   
            parent('Twist_Surface','Extra_Grp')
            delete(LCrv,RCrv)
        
        # 创建外层控制器并整理层级 / Create outer controllers and organize hierarchy
        select(cl=True)  # 清除选择，确保组是空的 / Clear selection to ensure groups are empty
        # 创建中间层控制器 / Create middle layer controller
        middle_Ctrl = circle(ch=0,n='Middle_Ctrl',r=6,nr=(0,1,0))&#91;0]
        select(cl=True)  # 清除选择 / Clear selection
        middle_Ctrl_off_grp = group(em=True, n='Middle_Ctrl_OffGrp')
        delete(parentConstraint(main_Ctrl,middle_Ctrl_off_grp,mo=0))
        setAttr(middle_Ctrl + '.overrideEnabled', 1)
        setAttr(middle_Ctrl + '.overrideColor', 14)  # 黄色 / Yellow
        
        # 创建最外层控制器 / Create outer layer controller
        select(cl=True)  # 清除选择 / Clear selection
        outer_Ctrl = circle(ch=0,n='Outer_Ctrl',r=7,nr=(0,1,0))&#91;0]
        select(cl=True)  # 清除选择 / Clear selection
        outer_Ctrl_off_grp = group(em=True, n='Outer_Ctrl_OffGrp')
        delete(parentConstraint(middle_Ctrl,outer_Ctrl_off_grp,mo=0))
        setAttr(outer_Ctrl + '.overrideEnabled', 1)
        setAttr(outer_Ctrl + '.overrideColor', 18)  # 红色 / Red
        
        # 建立层级关系 / Establish hierarchy: Outer_Ctrl -> Middle_Ctrl -> Main_Ctrl_Grp
        # 从内到外建立层级：先将 Main_Ctrl_Grp 放入 Middle_Ctrl
        main_grp_parent = listRelatives(main_grp, p=True)
        if not main_grp_parent or main_grp_parent&#91;0] != middle_Ctrl:
            parent(main_grp, middle_Ctrl)
        # 然后将 Middle_Ctrl 放入其 OffGrp
        middle_Ctrl_parent = listRelatives(middle_Ctrl, p=True)
        if not middle_Ctrl_parent or middle_Ctrl_parent&#91;0] != middle_Ctrl_off_grp:
            parent(middle_Ctrl, middle_Ctrl_off_grp)
        # 将 Middle_Ctrl_OffGrp 放入 Outer_Ctrl
        middle_off_grp_parent = listRelatives(middle_Ctrl_off_grp, p=True)
        if not middle_off_grp_parent or middle_off_grp_parent&#91;0] != outer_Ctrl:
            parent(middle_Ctrl_off_grp, outer_Ctrl)
        # 将 Outer_Ctrl 放入其 OffGrp
        outer_Ctrl_parent = listRelatives(outer_Ctrl, p=True)
        if not outer_Ctrl_parent or outer_Ctrl_parent&#91;0] != outer_Ctrl_off_grp:
            parent(outer_Ctrl, outer_Ctrl_off_grp)
                 
        # 清理 / Cleanup
        parent(Ik_OffGrp,'Jnt_Grp')    
        delete(initLoc)
        if Path:
            delete(Pcrv,DCrv,'new_locator')
            parent('Path_Surface','Extra_Grp')
        try:
            parent('Rivet_Fol_Group','Extra_Grp')
        except:pass
        select('Dummy_Mesh')
        RefGrp=group(n='Ref_Geo_Grp')
        select(RefGrp,'Outer_Ctrl_OffGrp','Jnt_Grp','Extra_Grp')
        group(n='Rig_Grp')
        setAttr('Extra_Grp.v',0)
        setAttr('Jnt_Grp.v',0)
                           
 
rope_rig_instance = Curve_Rigger()
rope_rig_instance.create_ui()</code></pre>



<p>This video demonstrates how to rig flexible props such as rope, tail, tentacle, chain and vine in Maya using Curve Rigger.<br>本期视频演示如何在 Maya 中，使用 Curve Rigger 快速绑定线条类道具（绳子、尾巴、触手、藤蔓等）。</p>



<p>With only 2 locators, you can automatically create joints, FK / IK, Reverse FK, stretch, twist and path animation controls.<br>只需要 2 个定位器，即可一键生成骨骼、FK / IK、反向 FK、拉伸、扭曲以及路径动画控制。</p>



<p>⚠️ This tool rigs flexible props USING curves, not rigging the curve itself.<br>⚠️ 本工具是“通过曲线来绑定道具”，而不是去绑定曲线本身。</p>



<p>🔹 Tool Features / 功能亮点</p>



<ul class="wp-block-list">
<li>One-click rig for flexible props (Rope / Tail / Tentacle / Vine / Chain)</li>



<li>FK / IK / Reverse FK switching</li>



<li>Stretch &amp; twist control</li>



<li>Path-based animation control</li>



<li>Auto skin and one-click skin weight transfer</li>



<li>Bilingual UI (Chinese / English)</li>



<li>线条类道具一键自动绑定</li>



<li>支持 FK / IK / 反向 FK 切换</li>



<li>支持拉伸、扭曲控制</li>



<li>路径动画控制系统</li>



<li>蒙皮权重一键转移</li>



<li>中英文双语界面优化</li>
</ul>



<p>🔹 Suitable For / 适用类型</p>



<ul class="wp-block-list">
<li>Rope / Cable / Hose rig</li>



<li>Tail &amp; Tentacle rig</li>



<li>Vine, whip and flexible props</li>



<li>Animation, game and cartoon production</li>



<li>绳子、管线、软管类道具</li>



<li>尾巴、触手类结构</li>



<li>藤蔓、鞭子等线条道具</li>



<li>动画、游戏、卡通项目</li>
</ul>



<p>🔹 Workflow Overview / 绑定流程</p>



<ol class="wp-block-list">
<li>Place 2 locators to define the start and end of the prop</li>



<li>Run Curve Rigger to generate the rig automatically</li>



<li>Animate using FK, IK or path controls</li>



<li>放置 2 个定位器作为起点和终点</li>



<li>运行 Curve Rigger 自动生成绑定</li>



<li>使用 FK / IK 或路径控制进行动画</li>
</ol>



<p>🔹 Software<br>Autodesk Maya 2018+</p>
<p><a href="https://www.miaodonghua.com/3550.html">Maya Curve Rigger｜线条类道具一键绑定（Rope / Tail / Tentacle Rig）</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Maya 一键创建控制器与关节 &#124; Auto Rig Controller &#038; Joint Script Tutorial</title>
		<link>https://www.miaodonghua.com/3545.html</link>
		
		<dc:creator><![CDATA[喵喵动画屋]]></dc:creator>
		<pubDate>Sun, 09 Nov 2025 19:18:29 +0000</pubDate>
				<category><![CDATA[MAYA动画教学]]></category>
		<guid isPermaLink="false">https://www.miaodonghua.com/?p=3545</guid>

					<description><![CDATA[<p><a href="https://www.miaodonghua.com/3545.html">Maya 一键创建控制器与关节 | Auto Rig Controller &amp; Joint Script Tutorial</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<pre class="wp-block-code has-vivid-green-cyan-color has-black-background-color has-text-color has-background has-link-color wp-elements-dfa86e40e614239d1a63ea3f5e80a7dc"><code>



// 获取选中的对象
string $selectedObjects&#91;] = `ls -selection`;

    // 查询选定对象的真实坐标
    float $location&#91;] = `xform -q -ws -rp $selectedObjects&#91;0]`;

if (size($selectedObjects) == 0) {
    warning "请选中一个对象";
} else {
    // 使用第一个选中的对象名创建关节
    string $jointName = $selectedObjects&#91;0] + "_joint";
    joint -name $jointName;
    
    


    // 移动控制器到目标点
    xform -ws -t $location&#91;0] $location&#91;1] $location&#91;2] $jointName;

    // 删除历史记录
    delete -ch $jointName;

    // 冻结变换
    makeIdentity -apply true -t 1 -r 1 -s 1 -n 0 $jointName;


    // 创建主控制器
    string $mainControllerName = $selectedObjects&#91;0] + "_main_ctrl";
    string $mainController&#91;] = `circle -name $mainControllerName -normal 0 1 0 -radius 20`;  // 将半径改为10倍



    // 移动控制器到目标点
    xform -ws -t $location&#91;0] $location&#91;1] $location&#91;2] $mainControllerName;

    // 删除历史记录
    delete -ch $mainControllerName;

    // 冻结变换
    makeIdentity -apply true -t 1 -r 1 -s 1 -n 0 $mainControllerName;
    
    
 // 设置主控制器颜色为黄色
    setAttr ($mainController&#91;0] + ".overrideEnabled") 1; 
    setAttr ($mainController&#91;0] + ".overrideColor") 17;  // 17 代表黄色

    // 设置主控制器外圈宽度为2
    setAttr ($mainController&#91;0] + ".lineWidth") 2;

    // 创建副控制器
    string $secondaryControllerName = $selectedObjects&#91;0] + "_secondary_ctrl";
    string $secondaryController&#91;] = `circle -name $secondaryControllerName -normal 0 1 0 -radius 15`;  // 将半径改为10倍的一半
    
    


    // 移动控制器到目标点
    xform -ws -t $location&#91;0] $location&#91;1] $location&#91;2] $secondaryControllerName;

    // 删除历史记录
    delete -ch $secondaryControllerName;

    // 冻结变换
    makeIdentity -apply true -t 1 -r 1 -s 1 -n 0 $secondaryControllerName;

    
    
// 设置副控制器颜色为黄色
   setAttr ($secondaryController&#91;0] + ".overrideEnabled") 1;
    setAttr ($secondaryController&#91;0] + ".overrideColor") 17;  // 17 代表黄色

    // 将副控制器放置在主控制器的层级下
    parent $secondaryController&#91;0] $mainController&#91;0];

    // 将关节约束到副控制器
    parentConstraint -maintainOffset $secondaryController&#91;0] $jointName;

// 将关节缩放约束到副控制器
scaleConstraint -maintainOffset $secondaryController&#91;0] $jointName;

    // 将关节加入到选择的对象中
     select -r $selectedObjects&#91;0] $jointName;

    // 执行 smoothBind 将关节绑定到选中的对象上
    SmoothBindSkin;
    
       // 创建组并重命名
    string $group = `group -em -name ($selectedObjects&#91;0] + "_ctrl_grp")`;  // 创建空组并命名
    parent $mainController&#91;0] $group;  // 将主控制器放入组中
    parent $selectedObjects&#91;0] $group;  // 将对象放入组中
parent $jointName $group;  // 将对象放入组中

//选中主控制器
select -r $mainControllerName;

}</code></pre>



<p></p>
<p><a href="https://www.miaodonghua.com/3545.html">Maya 一键创建控制器与关节 | Auto Rig Controller &amp; Joint Script Tutorial</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Maya 2022+ Eleven Rig绑定模型</title>
		<link>https://www.miaodonghua.com/3534.html</link>
		
		<dc:creator><![CDATA[喵喵动画屋]]></dc:creator>
		<pubDate>Wed, 01 Oct 2025 08:05:18 +0000</pubDate>
				<category><![CDATA[MAYA绑定]]></category>
		<category><![CDATA[Maya教程]]></category>
		<category><![CDATA[动画]]></category>
		<category><![CDATA[绑定]]></category>
		<guid isPermaLink="false">https://www.miaodonghua.com/?p=3534</guid>

					<description><![CDATA[<p>免费开源的 Eleven RIG 模型 —— Maya 动画练习利器 今天要和大家分享的是一个非常经典的免费开 &#8230; </p>
<p class="link-more"><a href="https://www.miaodonghua.com/3534.html" class="more-link">继续阅读<span class="screen-reader-text">“Maya 2022+ Eleven Rig绑定模型”</span></a></p>
<p><a href="https://www.miaodonghua.com/3534.html">Maya 2022+ Eleven Rig绑定模型</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-file"><a id="wp-block-file--media-dd668aec-6c39-4cd4-bdd1-b8eddfdea560" href="https://www.miaodonghua.com/wp-content/uploads/2025/10/ElevenRig_FinalTextures.7z"><br>ElevenRig_Final+Textures</a><a href="https://www.miaodonghua.com/wp-content/uploads/2025/10/ElevenRig_FinalTextures.7z" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-dd668aec-6c39-4cd4-bdd1-b8eddfdea560">下载</a></div>



<h2 class="wp-block-heading"></h2>



<h3 class="wp-block-heading">免费开源的 Eleven RIG 模型 —— Maya 动画练习利器</h3>



<p>今天要和大家分享的是一个非常经典的免费开源模型 —— <strong>Eleven RIG</strong>。<br>这个模型在动画学习圈子里非常有名，因为它简洁、优质，常被用作日常的动画练习。</p>



<p>不过，原版 Eleven RIG 在 Maya2014 之后就停止更新了，对后续版本支持不佳。为此，我对其绑定进行了全面重置，采用了更主流、更通用的 <strong>ADV 绑定系统</strong>，大大提升了兼容性与实用性。</p>



<h3 class="wp-block-heading">功能特色</h3>



<p>在保持原有基础功能的同时，我还做了一些扩展：</p>



<ol class="wp-block-list">
<li><strong>性别切换</strong> —— 一键切换男女角色，练习更灵活。</li>



<li><strong>眉毛切换</strong> —— 提供三种粗细样式，适配不同风格。</li>



<li><strong>胡子样式</strong> —— 可选择不同大小的胡子，丰富角色表现力。</li>



<li><strong>发型绑定</strong> —— 新增男女头发绑定，动画细节更饱满。</li>



<li><strong>iPhone ARKit 面捕支持</strong> —— 直接连接面部捕捉数据，让表情动画更真实。</li>
</ol>



<h3 class="wp-block-heading">版权声明</h3>



<p>出于对原作者的尊重，我在模型绑定中加入了版权归属显示。该模型依然保持开源与免费特性，可以放心用于学习与商业环境。</p>



<h3 class="wp-block-heading">写在最后</h3>



<p>绑定和优化这个模型花费了不少时间，希望它能为大家的学习和项目带来帮助。如果觉得有用，欢迎点赞与支持，谢谢</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p></p>
<p><a href="https://www.miaodonghua.com/3534.html">Maya 2022+ Eleven Rig绑定模型</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Maya剑轨工具 &#8211; 拖尾插件MEL脚本升级版</title>
		<link>https://www.miaodonghua.com/3529.html</link>
		
		<dc:creator><![CDATA[喵喵动画屋]]></dc:creator>
		<pubDate>Thu, 18 Sep 2025 16:28:16 +0000</pubDate>
				<category><![CDATA[MAYA动画教学]]></category>
		<guid isPermaLink="false">https://www.miaodonghua.com/?p=3529</guid>

					<description><![CDATA[<p><a href="https://www.miaodonghua.com/3529.html">Maya剑轨工具 &#8211; 拖尾插件MEL脚本升级版</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<pre class="wp-block-code has-vivid-green-cyan-color has-black-background-color has-text-color has-background has-link-color wp-elements-80f4e66db02b956c976b151280d2adbe"><code>//////////////////////////////////////////////////////
//kfSwordSwipe
//////////////////////////
//Written by Kiel Figgins
//www.3dfiggins.com
//////////////////////////
//Use to create a lofted surface over time between two points
//////////////////////////
//Version History
//////////////////////////
//2.01 (09-21-2021) - Simplying for public release focused only on Swipes not tron trails
//							-Changed UI order, R click on S/E frame to current frame, create mesh by default
//							-Create Shader option, changed top group name, default trail to 3, clean top group
//							-Lock mesh transforms, show mid frame of swipe, Help button added
//1.06 (05-29-2012) - Added single cv per frame non animated path obj
//1.05 (05-24-2012) - Changed naming of taper groups, fixed taper equation, added scale min/max, added tail length check, added tail length min, added sub min
//1.04 (05-23-2012) - Stripped out workflow
//1.03 (05-10-2012) - Cleaning up, trying to get to actually work
//1.02 (01-27-2012) - Added substep, clean up procs a bit
//1.01 (01-23-2012) - Added output mesh option, multiple object option, end frame half way
//1.00 (01-21-2012) - Orginal version
//////////////////////////
//Coming Soon
/*
//Coming Up



//Wishlist
-progress bar
-determine if you need to get the vert/pivot location of a target
-fade on/off
	-curves actually shrink as gets closer to end of frame range

*/




global proc kfSwordSwipe()
{

if (`window -q -ex kfSwordSwipeWin`)
    	{
    	showWindow kfSwordSwipeWin ;
    	return ;
    	}


window -w 250 -h 600 -t "剑轨工具-by Kiel Figgins（喵喵动画屋修改）" kfSwordSwipeWin ;
formLayout mainSaveForm ;

//UI pieces
text -l "1. 创建 2 个或以上定位器并约束到刀刃" txST_Locs;
text -l "2. 从尖到柄选择定位器" txST_LocSel;
text -l "3. 设置定位器" txSTDefNum;
textField -w 90 -ed false -tx "" tfSTDefNum;
button -l " &lt; " -w 20 -ann "根据选择填充" -c ("kfSTDefNumFill();") btnSTDefNumFill; 

text -l "4. 起始 / 结束帧（右键设置）" txSTStartEnd;
intField -w 40 -v 10 intSTStart;
	popupMenu();
		menuItem -l "设为当前帧" -c ("kfSS_SetFrame(1);");
intField -w 40 -v 20 intSTEnd;
	popupMenu();
		menuItem -l "设为当前帧" -c ("kfSS_SetFrame(2);");

text -l "5. 轨迹帧长度" txSTLength;
intField -min 1 -w 40 -v 3 intSTLength;

text -l "6. 轨迹缩放衰减%：" txSTScale;
floatField -min 0 -max 100 -pre 1 -w 40 -v 0 floatSTScale;

text -l "7. 帧内子步数：" txSTStep;
intField -min 0 -w 40 -v 20 intSTStep;

text -l "8. 请开启NURBS曲面与Viewport 2.0" txST_View;

text -l "9. 命名：" txSTNaming;
textField -w 140 -tx "Swipe_A" tfSTNaming;
button -l "帮助" -bgc .2 .55 .75 -w 40 -ann "" -c ("kfSS_Instruct();") btnST_Help; 

separator -style "in" -h 3 sepSTA;

button -l "创建剑轨" -w 150 -ann "" -c ("kfSTTailLen(); kfSTCreate();") btnSTCreate; 
checkBox -l "创建材质" -v 1 chBxSTShader;

// 10. 噪声颜色增益（colorGain）
text -l "10. 噪声颜色增益：" txNoiseGainLbl;
colorSliderGrp -l "" -rgb 1 1 1 -cw 1 1 -cw 2 220 -cw 3 1 -adj 2 -h 18 -cc "kfSS_OnColorGainChange()" csNoiseGain;
// 11. 噪声颜色偏移（colorOffset）
text -l "11. 噪声颜色偏移：" txNoiseOffsetLbl;
colorSliderGrp -l "" -rgb 0 0 0 -cw 1 1 -cw 2 220 -cw 3 1 -adj 2 -h 18 -cc "kfSS_OnColorOffsetChange()" csNoiseOffset;

// 12. 环境光颜色（ambientColor）
text -l "12. 环境光颜色：" txAmbientLbl;
colorSliderGrp -l "" -rgb 0 0 0 -cw 1 1 -cw 2 220 -cw 3 1 -adj 2 -h 18 -cc "kfSS_OnAmbientColorChange()" csAmbient;

// 获取与重置
button -l "从选择获取颜色" -w 120 -c ("kfSS_FetchNoiseFromSelection();") btnFetchNoise;
button -l "重置颜色" -w 120 -c ("kfSS_ResetColors();") btnResetColors;



//UI FormLayout
formLayout -e
    
    	-af txST_Locs "top" 8 
    	-an txST_Locs "bottom" 
    	-af txST_Locs "left" 5 
    	-an txST_Locs "right"
		
    	-ac txST_LocSel "top" 8 txST_Locs
    	-an txST_LocSel "bottom" 
    	-af txST_LocSel "left" 5 
    	-an txST_LocSel "right" 
		
    	-ac txSTDefNum "top" 8 txST_LocSel
    	-an txSTDefNum "bottom" 
    	-af txSTDefNum "left" 5 
    	-an txSTDefNum "right" 
    	
    	-ac tfSTDefNum "top" 8 txST_LocSel
    	-an tfSTDefNum "bottom" 
    	-ac tfSTDefNum "left" 5 txSTDefNum
    	-an tfSTDefNum "right" 
    	
    	-ac btnSTDefNumFill "top" 8 txST_LocSel
    	-an btnSTDefNumFill "bottom" 
    	-ac btnSTDefNumFill "left" 5 tfSTDefNum
    	-an btnSTDefNumFill "right" 
    	
    	-ac txSTStartEnd "top" 8 btnSTDefNumFill
    	-an txSTStartEnd "bottom" 
    	-af txSTStartEnd "left" 5 
    	-an txSTStartEnd "right" 
    	
    	-ac intSTStart "top" 8 btnSTDefNumFill
    	-an intSTStart "bottom" 
    	-ac intSTStart "left" 5 txSTStartEnd
    	-an intSTStart "right" 
   
    	-ac intSTEnd "top" 8 btnSTDefNumFill
    	-an intSTEnd "bottom" 
    	-ac intSTEnd "left" 5 intSTStart
    	-an intSTEnd "right" 
    	
    	-ac txSTLength "top" 8 intSTEnd
    	-an txSTLength "bottom" 
    	-af txSTLength "left" 5 
    	-an txSTLength "right" 
    	
    	-ac intSTLength "top" 8 intSTEnd
    	-an intSTLength "bottom" 
    	-ac intSTLength "left" 5 txSTLength
    	-an intSTLength "right" 
    	
    	-ac txSTScale "top" 8 intSTLength
    	-an txSTScale "bottom" 
    	-af txSTScale "left" 5 
    	-an txSTScale "right" 
    	
    	-ac floatSTScale "top" 8 intSTLength
    	-an floatSTScale "bottom" 
    	-ac floatSTScale "left" 5 txSTScale
    	-an floatSTScale "right" 
    	
		-ac txSTStep "top" 8 floatSTScale
    	-an txSTStep "bottom" 
    	-af txSTStep "left" 5 
    	-an txSTStep "right" 
    	
		-ac intSTStep "top" 8 floatSTScale
    	-an intSTStep "bottom" 
    	-ac intSTStep "left" 5 txSTStep
    	-an intSTStep "right" 
		
		-ac txST_View "top" 8 intSTStep
    	-an txST_View "bottom" 
    	-af txST_View "left" 5 
    	-an txST_View "right" 
    	
    	-ac txSTNaming "top" 8 txST_View
    	-an txSTNaming "bottom" 
    	-af txSTNaming "left" 5 
    	-an txSTNaming "right"
    	     
    	-ac tfSTNaming "top" 8 txST_View
    	-an tfSTNaming "bottom" 
    	-ac tfSTNaming "left" 5 txSTNaming
    	-an tfSTNaming "right" 
		
		-ac btnST_Help "top" 8 txST_View
    	-an btnST_Help "bottom" 
    	-an btnST_Help "left" 
    	-af btnST_Help "right" 5
    	
    	-ac sepSTA "top" 8 btnST_Help
    	-an sepSTA "bottom" 
    	-af sepSTA "left" 0
    	-af sepSTA "right" 0
    	
    	-ac btnSTCreate "top" 8 sepSTA
    	-an btnSTCreate "bottom" 
    	-af btnSTCreate "left" 5 
    	-an btnSTCreate "right" 
		
		-ac chBxSTShader "top" 8 sepSTA
    	-an chBxSTShader "bottom" 
    	-an chBxSTShader "left" 
    	-af chBxSTShader "right" 5
    	
    	-ac txNoiseGainLbl "top" 8 chBxSTShader
    	-an txNoiseGainLbl "bottom" 
    	-af txNoiseGainLbl "left" 5
    	-an txNoiseGainLbl "right" 

		-ac csNoiseGain "top" -2 txNoiseGainLbl
		-an csNoiseGain "bottom" 
		-ac csNoiseGain "left" 5 txNoiseGainLbl
		-af csNoiseGain "right" 5 

    	-ac txNoiseOffsetLbl "top" 8 csNoiseGain
    	-an txNoiseOffsetLbl "bottom" 
    	-af txNoiseOffsetLbl "left" 5
    	-an txNoiseOffsetLbl "right" 

		-ac csNoiseOffset "top" -2 txNoiseOffsetLbl
		-an csNoiseOffset "bottom" 
		-ac csNoiseOffset "left" 5 txNoiseOffsetLbl
		-af csNoiseOffset "right" 5 

    	-ac txAmbientLbl "top" 8 csNoiseOffset
    	-an txAmbientLbl "bottom" 
    	-af txAmbientLbl "left" 5
    	-an txAmbientLbl "right"
    	
    	-ac csAmbient "top" -2 txAmbientLbl
    	-an csAmbient "bottom" 
    	-ac csAmbient "left" 5 txAmbientLbl
    	-af csAmbient "right" 5

		-ac btnFetchNoise "top" 8 csAmbient
		-an btnFetchNoise "bottom" 
		-af btnFetchNoise "left" 5
		-an btnFetchNoise "right" 

		-ac btnResetColors "top" 8 csAmbient
		-an btnResetColors "bottom" 
		-ac btnResetColors "left" 5 btnFetchNoise
		-an btnResetColors "right"

    	mainSaveForm ;

    showWindow kfSwordSwipeWin ;

    //Resize the main window
    window -e -widthHeight 360 480 kfSwordSwipeWin;

}//end of proc kfSwordSwipe

////////////////////
//UI Specific Procs

//////////////////////////////////////////////////////////////////
global proc kfSS_SetFrame(int $which)
{
int $cF = `currentTime -q`;

if($which == 1){intField -e -v $cF intSTStart;}
if($which == 2){intField -e -v $cF intSTEnd;}


}//end of proc


//////////////////////////////////////////////////////////////////
global proc kfSTTailLen()
{

	int $startFrame = `intField -q -v intSTStart`;
	int $endFrame = `intField -q -v intSTEnd`;
	int $tailLength = `intField -q -v intSTLength`;

int $frameLength = ($endFrame - $startFrame);

if($tailLength > $frameLength){intField -e -v $frameLength intSTLength;}

}


//////////////////////////////////////////////////////////////////
global string $kfSTDefNum&#91;];

global proc kfSTDefNumFill()
{
	global string $kfSTDefNum&#91;];
	
	string $userSel&#91;] =`ls -sl`;
	
	int $theSize = size($userSel);
	
	if($theSize == 0)
	{
		$kfSTDefNum = `ls -sl`;
		textField -e -tx "" tfSTDefNum;
		//checkBox -e -en false -v 0 chBxSTMesh;
		print "\nFAIL: Please Select at Least (1) Object";
	}

	if($theSize == 1)
	{
		$kfSTDefNum = `ls -sl`;
		//checkBox -e -en false -v 0 chBxSTMesh;
		textField -e -tx $userSel&#91;0] tfSTDefNum;
	}

	if($theSize > 1)
	{
		$kfSTDefNum = `ls -sl`;
		//checkBox -e -en true -v 1 chBxSTMesh;
		textField -e -tx ($theSize + " Objects") tfSTDefNum;
	}
}//end of global proc



//////////////////////////////////////////////////////////////////
global proc kfSTDefNumSel()
{
	global string $kfSTDefNum&#91;];
	
	select $kfSTDefNum;
}//end of global proc



//////////////////////////////////////////////////////////////////
global proc kfSTCreate()
{
	global string $kfSTDefNum&#91;];
	select $kfSTDefNum;
	string $targets&#91;] = `ls -sl`;
	
	string $namer = `textField -q -tx tfSTNaming`;
	int $startFrame = `intField -q -v intSTStart`;
	int $endFrame = `intField -q -v intSTEnd`;
	int $tailLength = `intField -q -v intSTLength`;
	int $subStep = `intField -q -v intSTStep`;
	
	float $falloffPercent = `floatField -q -v floatSTScale`;

	//0 no, 1 yes
	//int $outputMesh = `checkBox -q -v chBxSTMesh`;
	int $outputMesh = 1;

	int $createShader = `checkBox -q -v chBxSTShader`;
	
	// 解锁材质组，确保在锁定场景中也能正常创建材质
	if(`objExists initialShadingGroup`){
		lockNode -l 0 -lu 0 initialShadingGroup;
	}
	

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Work - Single Stroke

//Delete if already exists
if(`objExists ($namer + "_Swipe_Elements")`){delete ($namer + "_Swipe_Elements");}

if($createShader == 1 &amp;&amp; `objExists ("mat_" + $namer)` == 1){delete ("mat_" + $namer);}



//////////////////////////
//Create Curves and Groups
group -empty -n ($namer + "_Swipe_Elements");
group -empty -n ($namer + "_Loft_Curves");
group -empty -n ($namer + "_Loft_Mesh");
group -empty -n ($namer + "_Loft_Nulls");

parent ($namer + "_Loft_Curves") ($namer + "_Loft_Mesh") ($namer + "_Loft_Nulls") ($namer + "_Swipe_Elements");
setAttr ($namer + "_Loft_Curves.v") 0;

setAttr -lock true -keyable false -channelBox false ($namer + "_Swipe_Elements.tx");
setAttr -lock true -keyable false -channelBox false ($namer + "_Swipe_Elements.ty");
setAttr -lock true -keyable false -channelBox false ($namer + "_Swipe_Elements.tz");
setAttr -lock true -keyable false -channelBox false ($namer + "_Swipe_Elements.rx");
setAttr -lock true -keyable false -channelBox false ($namer + "_Swipe_Elements.ry");
setAttr -lock true -keyable false -channelBox false ($namer + "_Swipe_Elements.rz");
setAttr -lock true -keyable false -channelBox false ($namer + "_Swipe_Elements.sx");
setAttr -lock true -keyable false -channelBox false ($namer + "_Swipe_Elements.sy");
setAttr -lock true -keyable false -channelBox false ($namer + "_Swipe_Elements.sz");


//for each target, create Nulls
int $counter;
int $sizer = `size($targets)`;

for ($counter = 0; $counter &lt; $sizer; $counter++)
{
	//////////////////////////
	//Go through timeline and create placment nulls
	currentTime $startFrame;
	
	group -empty -n ("rig_" + $namer + "_" + $counter + "_Nulls");
	string $nullGrp&#91;] = `ls -sl`;
	parent $nullGrp ($namer + "_Loft_Nulls");
	
	//////////////////
	//Go Through Frames
	int $counterF;
	int $sizerF = ($endFrame - $startFrame);
	
	for ($counterF = 0; $counterF &lt;= $sizerF; $counterF++)
	{
		//if $counterF = 0, its first frame, make all nulls at default position
		if($counterF == 0)
		{
			int $counterN;
			int $sizerN = $subStep;
			
			for ($counterN = 0; $counterN &lt;= $sizerN; $counterN++)
			{
				float $pos&#91;] = `xform -q -ws -rp $targets&#91;$counter]`;
				group -empty -n ("rig_" + $namer + "_" + $counter + "_FinderNull_" + $counterF + "_" + $counterN);
				move -ws $pos&#91;0] $pos&#91;1] $pos&#91;2] ("rig_" + $namer + "_" + $counter + "_FinderNull_" + $counterF + "_" + $counterN);
				parent ("rig_" + $namer + "_" + $counter + "_FinderNull_" + $counterF + "_" + $counterN) $nullGrp;
			}//end of create nulls
		}//end of if counterF = 0


		if($counterF &lt;= $tailLength &amp;&amp; $counterF != 0)
		{
			float $counterFFloat = $counterF;
			float $frameStep = ($counterFFloat / $subStep);
			int $counterN;
			int $sizerN = $subStep;
			
			for ($counterN = 0; $counterN &lt;= $sizerN; $counterN++)
			{
				currentTime ($startFrame + ($frameStep * $counterN));
				float $pos&#91;] = `xform -q -ws -rp $targets&#91;$counter]`;
				group -empty -n ("rig_" + $namer + "_" + $counter + "_FinderNull_" + $counterF + "_" + $counterN);
				move -ws $pos&#91;0] $pos&#91;1] $pos&#91;2] ("rig_" + $namer + "_" + $counter + "_FinderNull_" + $counterF + "_" + $counterN);
				parent ("rig_" + $namer + "_" + $counter + "_FinderNull_" + $counterF + "_" + $counterN) $nullGrp;
			}
			
		}//end of if counterF != 0


		if($counterF > $tailLength &amp;&amp; $counterF != 0)
		{
			float $counterFFloat = $counterF;
			float $frameStep = (($counterFFloat - ($counterFFloat - $tailLength))  / $subStep);
			int $counterN;
			int $sizerN = $subStep;
			
			for ($counterN = 0; $counterN &lt;= $sizerN; $counterN++)
			{
				currentTime ($startFrame + ($counterF - $tailLength) + ($frameStep * $counterN));
				float $pos&#91;] = `xform -q -ws -rp $targets&#91;$counter]`;
				group -empty -n ("rig_" + $namer + "_" + $counter + "_FinderNull_" + $counterF + "_" + $counterN);
				move -ws $pos&#91;0] $pos&#91;1] $pos&#91;2] ("rig_" + $namer + "_" + $counter + "_FinderNull_" + $counterF + "_" + $counterN);
				parent ("rig_" + $namer + "_" + $counter + "_FinderNull_" + $counterF + "_" + $counterN) $nullGrp;
			}
			
		}//end of if counterF != 0
	}//end of loop through frames
	
}//end of loop through all targets for creating nulls











//scale Nulls for taper
int $counterF;
int $sizerF = ($endFrame - $startFrame);

for ($counterF = 0; $counterF &lt;= $sizerF; $counterF++)
{
//int $subStep = `intField -q -v intSTStep`;
//float $falloffPercent = `floatField -q -v floatSTScale`;





		//if $counterF = 0, its first frame, make all nulls at default position
		if($counterF == 0)
		{
			int $counterN;
			int $sizerN = $subStep;
			
			for ($counterN = 0; $counterN &lt;= $sizerN; $counterN++)
			{
				select ("rig_" + $namer + "_*_FinderNull_" + $counterF + "_" + $counterN);
				group -n ("rig_" + $namer + "_" + $counterF + "_" + $counterN + "_Taper");
				string $taperGrp&#91;] = `ls -sl`;
				select $taperGrp;
				CenterPivot;
				
				float $subStepF = $subStep;
				float $counterNFloat = $counterN;
				setAttr ($taperGrp&#91;0] + ".sx") (1 - ($falloffPercent * .01) + ((($falloffPercent / $subStepF) * $counterNFloat) * .01));
				setAttr ($taperGrp&#91;0] + ".sy") (1 - ($falloffPercent * .01) + ((($falloffPercent / $subStepF) * $counterNFloat) * .01));
				setAttr ($taperGrp&#91;0] + ".sz") (1 - ($falloffPercent * .01) + ((($falloffPercent / $subStepF) * $counterNFloat) * .01));
			}//end of create nulls
		}//end of if counterF = 0


		if($counterF &lt;= $tailLength &amp;&amp; $counterF != 0)
		{
			float $counterFFloat = $counterF;
			float $frameStep = ($counterFFloat / $subStep);
			int $counterN;
			int $sizerN = $subStep;
			
			for ($counterN = 0; $counterN &lt;= $sizerN; $counterN++)
			{
				select ("rig_" + $namer + "_*_FinderNull_" + $counterF + "_" + $counterN);
				group -n ("rig_" + $namer + "_" + $counterF + "_" + $counterN + "_Taper");
				string $taperGrp&#91;] = `ls -sl`;
				select $taperGrp;
				CenterPivot;
				
				float $subStepF = $subStep;
				float $counterNFloat = $counterN;
				setAttr ($taperGrp&#91;0] + ".sx") (1 - ($falloffPercent * .01) + ((($falloffPercent / $subStepF) * $counterNFloat) * .01));
				setAttr ($taperGrp&#91;0] + ".sy") (1 - ($falloffPercent * .01) + ((($falloffPercent / $subStepF) * $counterNFloat) * .01));
				setAttr ($taperGrp&#91;0] + ".sz") (1 - ($falloffPercent * .01) + ((($falloffPercent / $subStepF) * $counterNFloat) * .01));
			}
			
		}//end of if counterF != 0


		if($counterF > $tailLength &amp;&amp; $counterF != 0)
		{
			float $counterFFloat = $counterF;
			float $frameStep = (($counterFFloat - ($counterFFloat - $tailLength))  / $subStep);
			int $counterN;
			int $sizerN = $subStep;
			
			for ($counterN = 0; $counterN &lt;= $sizerN; $counterN++)
			{
				select ("rig_" + $namer + "_*_FinderNull_" + $counterF + "_" + $counterN);
				group -n ("rig_" + $namer + "_" + $counterF + "_" + $counterN + "_Taper");
				string $taperGrp&#91;] = `ls -sl`;
				select $taperGrp;
				CenterPivot;
				
				float $subStepF = $subStep;
				float $counterNFloat = $counterN;
				setAttr ($taperGrp&#91;0] + ".sx") (1 - ($falloffPercent * .01) + ((($falloffPercent / $subStepF) * $counterNFloat) * .01));
				setAttr ($taperGrp&#91;0] + ".sy") (1 - ($falloffPercent * .01) + ((($falloffPercent / $subStepF) * $counterNFloat) * .01));
				setAttr ($taperGrp&#91;0] + ".sz") (1 - ($falloffPercent * .01) + ((($falloffPercent / $subStepF) * $counterNFloat) * .01));
			}
			
		}//end of if counterF != 0






}//end of scaling nulls for taper











//create/align curves
int $counter;
int $sizer = `size($targets)`;

for ($counter = 0; $counter &lt; $sizer; $counter++)
{
	curve -d 3 -p 0 0 0 -p 0 0 1 -p 0 0 2 -p 0 0 3 -p 0 0 4  -k 0 -k 0 -k 0 -k 1 -k 2 -k 2 -k 2 ;
	rename ("rig_" + $namer + "_curve_" + $counter);
	rebuildCurve -ch 1 -rpo 1 -rt 0 -end 1 -kr 0 -kcp 0 -kep 1 -kt 0 -s ($subStep - 2) -d 3 -tol 0.01 ("rig_" + $namer + "_curve_" + $counter);
	
	parent ("rig_" + $namer + "_curve_" + $counter) ($namer + "_Loft_Curves");
	
	select ("rig_" + $namer + "_curve_" + $counter);
	string $curve&#91;] = `ls -sl`;

	/////////////////////////////
	//Go through and align curve to placement nulls
	
	select ("rig_" + $namer + "_curve_" + $counter);
	string $curve&#91;] = `ls -sl`;
	
	int $counterF;
	int $sizerF = ($endFrame - $startFrame);
	
	for ($counterF = 0; $counterF &lt;= $sizerF; $counterF++)
	{
		currentTime ($startFrame + $counterF);
		
		int $counterN;
		int $sizerN = $subStep;
		
		for ($counterN = 0; $counterN &lt;= $sizerN; $counterN++)
		{
			float $pos&#91;] = `xform -q -ws -rp ("rig_" + $namer + "_" + $counter + "_FinderNull_" + $counterF + "_" + $counterN)`;
			move -ws $pos&#91;0] $pos&#91;1] $pos&#91;2] ($curve&#91;0] + ".cv&#91;" + $counterN + "]");
			setKeyframe -breakdown 0 -hierarchy none -controlPoints 0 -shape 0 {($curve&#91;0] + ".cv&#91;" + $counterN + "]")};
		}
		
	}//end of loop through time alignint curve to nulls

}//end of create/align curves







if($createShader == 1)
{
//Base Material
shadingNode -asShader lambert -n ("mat_" + $namer);
string $sColor&#91;] = `ls -sl`;

sets -renderable true -noSurfaceShader true -empty -name ($sColor&#91;0] + "SG");
connectAttr -f ($sColor&#91;0] + ".outColor") ($sColor&#91;0] + "SG.surfaceShader");

setAttr ($sColor&#91;0] + ".diffuse") 1;
setAttr ($sColor&#91;0] + ".translucence") 1;
setAttr ($sColor&#91;0] + ".ambientColor") -type double3 0 0 1 ;

//Noise
string $noise = `shadingNode -asTexture noise -n ($namer + "_Noise")`;
string $noiseP = `shadingNode -asUtility place2dTexture -n ($namer + "_Noise_Place2dText")`;

connectAttr ($noiseP + ".outUV") ($noise + ".uv");
connectAttr ($noiseP + ".outUvFilterSize") ($noise + ".uvFilterSize");
connectAttr -force ($noise + ".outColor") ($sColor&#91;0] + ".color");

setAttr ($noise + ".time") 0.45;
setAttr ($noise + ".frequency") 6;
setAttr ($noiseP + ".repeatU") 0.1;

//Transp Ramp
string $ramp = `shadingNode -asTexture ramp -n ($namer + "_Ramp")`;
string $rampP = `shadingNode -asUtility place2dTexture -n ($namer + "_Ramp_Place2dText")`;

connectAttr ($rampP + ".outUV") ($ramp + ".uv");
connectAttr ($rampP + ".outUvFilterSize") ($ramp + ".uvFilterSize");
connectAttr -force ($ramp + ".outColor") ($sColor&#91;0] + ".transparency");

removeMultiInstance -break true ($ramp + ".colorEntryList&#91;1]");

setAttr ($ramp + ".type") 1;
setAttr ($ramp + ".interpolation") 2;

setAttr ($ramp + ".colorEntryList&#91;0].color") -type double3 .5 .5 .5 ;
setAttr ($ramp + ".colorEntryList&#91;2].color") -type double3 1 1 1;

setAttr ($ramp + ".colorEntryList&#91;0].position") 1;
setAttr ($ramp + ".colorEntryList&#91;2].position") 0.3;

//Assign to Loft
//sets -e -forceElement ($sColor&#91;0] + "SG");

}






	
	select ("rig_" + $namer + "_curve_*");
	string $loftCurves&#91;] = `ls -tr -sl`;
	
	if($outputMesh)
	{
		loft -n ("mesh_" + $namer + "_LoftSurface") -ch 1 -u 1 -c 0 -ar 0 -d 3 -ss 1 -rn 0 -po 0 -rsn true $loftCurves;
		string $loftMesh&#91;] = `ls -sl`;
		
		parent $loftMesh ($namer + "_Loft_Mesh");
		
		select $loftMesh;
		
		if($createShader == 1){sets -e -forceElement ("mat_" + $namer + "SG");}
		
		setAttr -lock true -keyable false -channelBox false ($loftMesh&#91;0] + ".tx");
		setAttr -lock true -keyable false -channelBox false ($loftMesh&#91;0] + ".ty");
		setAttr -lock true -keyable false -channelBox false ($loftMesh&#91;0] + ".tz");
		setAttr -lock true -keyable false -channelBox false ($loftMesh&#91;0] + ".rx");
		setAttr -lock true -keyable false -channelBox false ($loftMesh&#91;0] + ".ry");
		setAttr -lock true -keyable false -channelBox false ($loftMesh&#91;0] + ".rz");
		setAttr -lock true -keyable false -channelBox false ($loftMesh&#91;0] + ".sx");
		setAttr -lock true -keyable false -channelBox false ($loftMesh&#91;0] + ".sy");
		setAttr -lock true -keyable false -channelBox false ($loftMesh&#91;0] + ".sz");
		
	}
	else
	{
		setAttr ($namer + "_Loft_Curves.v") 1;
	}
	
	//Key Vis on Mesh output
	setKeyframe -v 0 -t ($startFrame - 1) ($namer + "_Loft_Mesh.v");
	setKeyframe -v 1 -t $startFrame ($namer + "_Loft_Mesh.v");
	
	setKeyframe -v 1 -t $endFrame ($namer + "_Loft_Mesh.v");
	setKeyframe -v 0 -t ($endFrame + 1) ($namer + "_Loft_Mesh.v");
	
	int $midF = ((($endFrame - $startFrame) * .5) + $startFrame);
	
	currentTime $midF;
	
	print "\n成功：已创建剑轨";



}//end of global proc kfMUListCommand










//////////////////////////////////////////////////////////////////
global proc kfSTCreateObjPath()
{

	global string $kfSTDefNum&#91;];
	select $kfSTDefNum;
	string $targets&#91;] = `ls -sl`;
	
	string $namer = `textField -q -tx tfSTNaming`;
	int $startFrame = `intField -q -v intSTStart`;
	int $endFrame = `intField -q -v intSTEnd`;


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Work - Single Stroke

//for each target, create Nulls
int $counter;
int $sizer = `size($targets)`;

for ($counter = 0; $counter &lt; $sizer; $counter++)
{

	curve -d 3 -p 0 0 0 -p 0 0 1 -p 0 0 2 -p 0 0 3 -p 0 0 4  -k 0 -k 0 -k 0 -k 1 -k 2 -k 2 -k 2 ;
	rename ("rig_" + $namer + "_curve_" + $counter);
	rebuildCurve -ch 1 -rpo 1 -rt 0 -end 1 -kr 0 -kcp 0 -kep 1 -kt 0 -s ($endFrame - $startFrame - 2) -d 3 -tol 0.01 ("rig_" + $namer + "_curve_" + $counter);

	//////////////////
	//Go Through Frames
	int $counterF;
	int $sizerF = ($endFrame - $startFrame);
	
	for ($counterF = 0; $counterF &lt;= $sizerF; $counterF++)
	{
	    //Go through timeline and create placment nulls
	    currentTime ($startFrame + $counterF);

	float $pos&#91;] = `xform -q -ws -rp $targets&#91;$counter]`;
	move -ws $pos&#91;0] $pos&#91;1] $pos&#91;2] ("rig_" + $namer + "_curve_" + $counter + ".cv&#91;" + $counterF + "]");
	}//end of loop through frames
	
}//end of loop through all targets for creating nulls




}















///////////////////////////////////////////////////////////////
//Instructions



global proc kfSS_Instruct()
{

if (`window -q -ex kfSS_InstructWin`)
        {
        showWindow kfSS_InstructWin ;
        return ;
        }


window -w 850 -h 500 -t "剑轨工具 - 使用说明" kfSS_InstructWin ;
formLayout mainSaveForm ;

//UI pieces
scrollField -w 100 -h 100 -wordWrap true -tx 

(
"剑轨工具 - 作者: Kiel Figgins - 邮箱: KielFiggins22@gmail.com - 网站: 3dFiggins.com"
+ "\n\n" + 
"完整教程（含示例与图片），请访问 3dFiggins.com/Store/Support/SwordSwipe"
+ "\n\n" + 
"网站包含火焰轨迹、Tron 轨迹、故障排查等更多内容！"
)

-ed 0 scrollSS_Instruct;

//UI FormLayout
formLayout -e
    
        -af scrollSS_Instruct "top" 5 
        -af scrollSS_Instruct "bottom" 5
        -af scrollSS_Instruct "left" 5 
        -af scrollSS_Instruct "right" 5

        mainSaveForm ;

    showWindow kfSS_InstructWin ;
    
    //Resize the main window
    window -e -widthHeight 650 150 kfSS_InstructWin;
}//end of proc 

// ====== 实时颜色变化回调函数 ======
global proc kfSS_OnColorGainChange()
{
	global string $g_kfNoiseNode;
	
	// 如果还没有获取到噪声节点，尝试自动获取
	if($g_kfNoiseNode == ""){
		// 尝试从当前选择中自动获取噪声节点
		string $sel&#91;] = `ls -sl`;
		if(size($sel) > 0){
			kfSS_FetchNoiseFromSelection();
		}
	}
	
	// 如果仍然没有噪声节点，直接返回
	if($g_kfNoiseNode == ""){
		return;
	}
	
	// 实时更新噪声节点的colorGain
	float $g&#91;] = `colorSliderGrp -q -rgbValue csNoiseGain`;
	if(`objExists $g_kfNoiseNode` &amp;&amp; `attributeQuery -ex -n $g_kfNoiseNode "colorGain"`){
		setAttr ($g_kfNoiseNode+".colorGain") -type double3 $g&#91;0] $g&#91;1] $g&#91;2];
	}
}

global proc kfSS_OnColorOffsetChange()
{
	global string $g_kfNoiseNode;
	
	// 如果还没有获取到噪声节点，尝试自动获取
	if($g_kfNoiseNode == ""){
		// 尝试从当前选择中自动获取噪声节点
		string $sel&#91;] = `ls -sl`;
		if(size($sel) > 0){
			kfSS_FetchNoiseFromSelection();
		}
	}
	
	// 如果仍然没有噪声节点，直接返回
	if($g_kfNoiseNode == ""){
		return;
	}
	
	// 实时更新噪声节点的colorOffset
	float $o&#91;] = `colorSliderGrp -q -rgbValue csNoiseOffset`;
	if(`objExists $g_kfNoiseNode` &amp;&amp; `attributeQuery -ex -n $g_kfNoiseNode "colorOffset"`){
		setAttr ($g_kfNoiseNode+".colorOffset") -type double3 $o&#91;0] $o&#91;1] $o&#91;2];
	}
}

global proc kfSS_OnAmbientColorChange()
{
	global string $g_kfNoiseNode;
	
	// 如果还没有获取到噪声节点，尝试自动获取
	if($g_kfNoiseNode == ""){
		// 尝试从当前选择中自动获取噪声节点
		string $sel&#91;] = `ls -sl`;
		if(size($sel) > 0){
			kfSS_FetchNoiseFromSelection();
		}
	}
	
	// 如果仍然没有噪声节点，直接返回
	if($g_kfNoiseNode == ""){
		return;
	}
	
	// 实时更新材质的环境光颜色
	string $shader = "";
	string $hist&#91;] = `listHistory -f 1 $g_kfNoiseNode`;
	for($n in $hist){ 
		if(`nodeType $n` == "lambert" || `nodeType $n` == "blinn" || `nodeType $n` == "phong" || `nodeType $n` == "surfaceShader" || `nodeType $n` == "aiStandardSurface"){ 
			$shader = $n; 
			break; 
		} 
	}
	if($shader != ""){
		float $ambient&#91;] = `colorSliderGrp -q -rgbValue csAmbient`;
		if(`attributeQuery -ex -n $shader "ambientColor"`){ 
			setAttr ($shader+".ambientColor") -type double3 $ambient&#91;0] $ambient&#91;1] $ambient&#91;2];
		}
	}
}

// Auto-run the UI when this script is sourced
evalDeferred "kfSwordSwipe();";

// ====== 辅助：从选择获取噪声节点并读写颜色 ======
global string $g_kfNoiseNode;

global proc kfSS_FetchNoiseFromSelection()
{
	global string $g_kfNoiseNode;
	$g_kfNoiseNode = "";
	string $sel&#91;] = `ls -sl`;
	if(size($sel) == 0){ warning "请选择一个已创建的拖尾网格或其材质节点"; return; }
	string $node = $sel&#91;0];
	string $type = `nodeType $node`;

	// 如果直接选中了 noise
	if($type == "noise"){
		$g_kfNoiseNode = $node;
	}
	else
	{
		string $shader = "";
		// 如果选中的是着色器（lambert/blinn/aiStandardSurface等）
		if($type == "lambert" || $type == "blinn" || $type == "phong" || $type == "surfaceShader" || $type == "aiStandardSurface"){
			$shader = $node;
		}
		// 如果选中的是着色分配组（shadingEngine）
		else if($type == "shadingEngine"){
			string $ss&#91;] = `listConnections -s true -d false ($node+".surfaceShader")`;
			if(size($ss) > 0){ $shader = $ss&#91;0]; }
		}
		else
		{
			// 认为是几何体/transform，从其 shape 找到 SG -> Shader
			string $shape&#91;] = `listRelatives -s -pa $node`;
			if(size($shape) == 0){ $shape = stringArrayCatenate($shape, {$node}); }
			// 取第一个 shape
			if(size($shape) > 0){
				string $sgs&#91;] = `listConnections -type shadingEngine $shape&#91;0]`;
				if(size($sgs) > 0){
					string $ss&#91;] = `listConnections -s true -d false ($sgs&#91;0]+".surfaceShader")`;
					if(size($ss) > 0){ $shader = $ss&#91;0]; }
				}
			}
		}

		// 从 Shader 向上游寻找 noise
		if($shader != ""){
			// 先从 color 插口向上找 noise
			string $n1&#91;] = `listConnections -s true -d false -type noise ($shader+".color")`;
			if(size($n1) > 0){ $g_kfNoiseNode = $n1&#91;0]; }
			else{
				// 退化为整网搜索：从 shader 的历史中过滤出 noise
				string $hist&#91;] = `listHistory -f 1 $shader`;
				for($n in $hist){ if(`nodeType $n` == "noise"){ $g_kfNoiseNode = $n; break; } }
			}
		}
	}

	if($g_kfNoiseNode == ""){ warning "未找到连接的 noise 节点"; return; }
	
	// 读取 colorGain / colorOffset 并更新UI
	if(`attributeQuery -ex -n $g_kfNoiseNode "colorGain"`){
		float $gain&#91;] = `getAttr ($g_kfNoiseNode+".colorGain")`;
		// 临时禁用回调，避免在设置颜色时触发实时更新
		colorSliderGrp -e -rgb $gain&#91;0] $gain&#91;1] $gain&#91;2] -cc "" csNoiseGain;
		colorSliderGrp -e -cc "kfSS_OnColorGainChange()" csNoiseGain;
	}
	if(`attributeQuery -ex -n $g_kfNoiseNode "colorOffset"`){
		float $off&#91;] = `getAttr ($g_kfNoiseNode+".colorOffset")`;
		// 临时禁用回调，避免在设置颜色时触发实时更新
		colorSliderGrp -e -rgb $off&#91;0] $off&#91;1] $off&#91;2] -cc "" csNoiseOffset;
		colorSliderGrp -e -cc "kfSS_OnColorOffsetChange()" csNoiseOffset;
	}
	
	// 从噪声节点向上追溯找到材质，获取环境光颜色
	string $shader = "";
	string $hist&#91;] = `listHistory -f 1 $g_kfNoiseNode`;
	for($n in $hist){ 
		if(`nodeType $n` == "lambert" || `nodeType $n` == "blinn" || `nodeType $n` == "phong" || `nodeType $n` == "surfaceShader" || `nodeType $n` == "aiStandardSurface"){ 
			$shader = $n; 
			break; 
		} 
	}
	
	if($shader != ""){
		if(`attributeQuery -ex -n $shader "ambientColor"`){
			float $ambient&#91;] = `getAttr ($shader+".ambientColor")`;
			// 临时禁用回调，避免在设置颜色时触发实时更新
			colorSliderGrp -e -rgb $ambient&#91;0] $ambient&#91;1] $ambient&#91;2] -cc "" csAmbient;
			colorSliderGrp -e -cc "kfSS_OnAmbientColorChange()" csAmbient;
		}
	}
	
	print ("\n已获取噪声节点: "+$g_kfNoiseNode);
	print ("实时颜色更新已启用！现在可以直接拖动颜色滑块查看效果。");
}

// ====== 重置颜色函数 ======
global proc kfSS_ResetColors()
{
	global string $g_kfNoiseNode;
	
	// 如果还没有获取到噪声节点，尝试自动获取
	if($g_kfNoiseNode == ""){
		string $sel&#91;] = `ls -sl`;
		if(size($sel) > 0){
			kfSS_FetchNoiseFromSelection();
		}
	}
	
	// 如果仍然没有噪声节点，提示用户
	if($g_kfNoiseNode == ""){
		warning "请先选择已创建的剑轨网格或材质节点，然后点击'从选择获取颜色'";
		return;
	}
	
	// 重置噪声节点的颜色到默认值
	if(`objExists $g_kfNoiseNode`){
		// 重置colorGain到默认值 (1,1,1)
		if(`attributeQuery -ex -n $g_kfNoiseNode "colorGain"`){
			setAttr ($g_kfNoiseNode+".colorGain") -type double3 1 1 1;
			colorSliderGrp -e -rgb 1 1 1 csNoiseGain;
		}
		// 重置colorOffset到默认值 (0,0,0)
		if(`attributeQuery -ex -n $g_kfNoiseNode "colorOffset"`){
			setAttr ($g_kfNoiseNode+".colorOffset") -type double3 0 0 0;
			colorSliderGrp -e -rgb 0 0 0 csNoiseOffset;
		}
	}
	
	// 重置环境光颜色到创建时的默认值 (0,0,1) 蓝色
	string $shader = "";
	string $hist&#91;] = `listHistory -f 1 $g_kfNoiseNode`;
	for($n in $hist){ 
		if(`nodeType $n` == "lambert" || `nodeType $n` == "blinn" || `nodeType $n` == "phong" || `nodeType $n` == "surfaceShader" || `nodeType $n` == "aiStandardSurface"){ 
			$shader = $n; 
			break; 
		} 
	}
	if($shader != ""){
		if(`attributeQuery -ex -n $shader "ambientColor"`){ 
			setAttr ($shader+".ambientColor") -type double3 0 0 1;
			colorSliderGrp -e -rgb 0 0 1 csAmbient;
		}
	}
	
	print ("\n颜色已重置为创建时的默认值：");
	print ("- 噪声颜色增益: (1, 1, 1)");
	print ("- 噪声颜色偏移: (0, 0, 0)");
	print ("- 环境光颜色: (0, 0, 1) 蓝色");
}

global proc kfSS_ApplyNoiseColors()
{
	global string $g_kfNoiseNode;
	if($g_kfNoiseNode == ""){ kfSS_FetchNoiseFromSelection(); if($g_kfNoiseNode == ""){ return; } }
	float $g&#91;] = `colorSliderGrp -q -rgbValue csNoiseGain`;
	float $o&#91;] = `colorSliderGrp -q -rgbValue csNoiseOffset`;
	if(`objExists $g_kfNoiseNode`){
		if(`attributeQuery -ex -n $g_kfNoiseNode "colorGain"`){ setAttr ($g_kfNoiseNode+".colorGain") -type double3 $g&#91;0] $g&#91;1] $g&#91;2]; }
		if(`attributeQuery -ex -n $g_kfNoiseNode "colorOffset"`){ setAttr ($g_kfNoiseNode+".colorOffset") -type double3 $o&#91;0] $o&#91;1] $o&#91;2]; }
		print ("\n已应用颜色到噪声节点: "+$g_kfNoiseNode);
	}else{
		warning "噪声节点不存在，无法设置";
	}
}

global proc kfSS_ApplyAmbientColor()
{
	global string $g_kfNoiseNode;
	if($g_kfNoiseNode == ""){ kfSS_FetchNoiseFromSelection(); if($g_kfNoiseNode == ""){ return; } }
	// 从噪声节点向上追溯找到材质
	string $shader = "";
	string $hist&#91;] = `listHistory -f 1 $g_kfNoiseNode`;
	for($n in $hist){ 
		if(`nodeType $n` == "lambert" || `nodeType $n` == "blinn" || `nodeType $n` == "phong" || `nodeType $n` == "surfaceShader" || `nodeType $n` == "aiStandardSurface"){ 
			$shader = $n; 
			break; 
		} 
	}
	if($shader == ""){ warning "未找到连接的材质节点"; return; }
	
	float $ambient&#91;] = `colorSliderGrp -q -rgbValue csAmbient`;
	if(`attributeQuery -ex -n $shader "ambientColor"`){ 
		setAttr ($shader+".ambientColor") -type double3 $ambient&#91;0] $ambient&#91;1] $ambient&#91;2]; 
		print ("\n已应用环境光颜色到材质: "+$shader);
	}else{
		warning "材质节点没有ambientColor属性";
	}
}

</code></pre>



<p></p>
<p><a href="https://www.miaodonghua.com/3529.html">Maya剑轨工具 &#8211; 拖尾插件MEL脚本升级版</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Maya控制器颜色设置脚本</title>
		<link>https://www.miaodonghua.com/3476.html</link>
					<comments>https://www.miaodonghua.com/3476.html#respond</comments>
		
		<dc:creator><![CDATA[喵喵动画屋]]></dc:creator>
		<pubDate>Fri, 03 Jan 2025 14:46:55 +0000</pubDate>
				<category><![CDATA[MAYA动画教学]]></category>
		<guid isPermaLink="false">https://www.miaodonghua.com/?p=3476</guid>

					<description><![CDATA[<p><a href="https://www.miaodonghua.com/3476.html">Maya控制器颜色设置脚本</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<pre class="wp-block-code has-vivid-green-cyan-color has-black-background-color has-text-color has-background has-link-color wp-elements-8be4004b398f1830714ce1e8c0236713"><code>string $lanzi&#91;] = `ls -sl`;
int $shuliang = size($lanzi);
print($shuliang+"\n");

for($i=0;$i&lt;$shuliang;$i++)
{
 //启用绘制覆盖
setAttr ($lanzi&#91;$i]+".overrideEnabled") 1;

//8_黄色 13为红色  6为蓝色  9为紫色
setAttr ($lanzi&#91;$i]+".overrideColor") 13;
}</code></pre>
<p><a href="https://www.miaodonghua.com/3476.html">Maya控制器颜色设置脚本</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.miaodonghua.com/3476.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>arShake 相机震动脚本工具</title>
		<link>https://www.miaodonghua.com/3473.html</link>
					<comments>https://www.miaodonghua.com/3473.html#respond</comments>
		
		<dc:creator><![CDATA[喵喵动画屋]]></dc:creator>
		<pubDate>Fri, 03 Jan 2025 14:44:32 +0000</pubDate>
				<category><![CDATA[MAYA动画教学]]></category>
		<guid isPermaLink="false">https://www.miaodonghua.com/?p=3473</guid>

					<description><![CDATA[<p>arShake 相机震动 / 摇晃脚本，来自 Highend3d / Creative Crash。 这个脚本 &#8230; </p>
<p class="link-more"><a href="https://www.miaodonghua.com/3473.html" class="more-link">继续阅读<span class="screen-reader-text">“arShake 相机震动脚本工具”</span></a></p>
<p><a href="https://www.miaodonghua.com/3473.html">arShake 相机震动脚本工具</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-file"><a id="wp-block-file--media-c0782858-32cf-4a1f-868e-63c75d14ae72" href="https://www.miaodonghua.com/wp-content/uploads/2025/01/arShake.tar">arShake</a><a href="https://www.miaodonghua.com/wp-content/uploads/2025/01/arShake.tar" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-c0782858-32cf-4a1f-868e-63c75d14ae72">下载</a></div>



<p>arShake 相机震动 / 摇晃脚本，来自 Highend3d / Creative Crash。</p>



<p>这个脚本用于在时间范围内为物体创建震动、振动或摇晃的动画。</p>



<p>将 <code>arShake.py</code> 放入 <code>Documents/Maya/Scripts</code> 文件夹。 将 <code>shelfScript.txt</code> 文件的内容作为 Python 脚本添加到 Maya 的工具架上。</p>



<ul class="wp-block-list">
<li>设置 &#8211;<br>每帧：1 表示每帧震动，2 表示每隔一帧震动，3 表示每隔三帧震动，依此类推。<br>平移量：震动时将添加或减去的平移值。<br>旋转量：震动时将添加或减去的旋转值。<br>平移和旋转轴可以选择启用或禁用。<br>使用曲线：震动的量会根据提供的曲线进行渐入和渐出。</li>
</ul>



<p>所有震动值将被烘焙到一个动画层上。</p>



<p>演示视频：<a href="https://vimeo.com/93114941">点击这里</a></p>
<p><a href="https://www.miaodonghua.com/3473.html">arShake 相机震动脚本工具</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.miaodonghua.com/3473.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Maya绑定教学：一键导入胸部骨架预设</title>
		<link>https://www.miaodonghua.com/3469.html</link>
					<comments>https://www.miaodonghua.com/3469.html#respond</comments>
		
		<dc:creator><![CDATA[喵喵动画屋]]></dc:creator>
		<pubDate>Mon, 07 Oct 2024 07:55:03 +0000</pubDate>
				<category><![CDATA[MAYA动画教学]]></category>
		<guid isPermaLink="false">https://www.miaodonghua.com/?p=3469</guid>

					<description><![CDATA[<p>这段视频内容主要介绍了如何在Maya中为模型的胸部添加绑定。以下是主要步骤总结：</p>
<p><a href="https://www.miaodonghua.com/3469.html">Maya绑定教学：一键导入胸部骨架预设</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-file has-vivid-green-cyan-background-color has-background"><a id="wp-block-file--media-c183ac43-bf12-489e-90a6-82994404c032" href="https://www.miaodonghua.com/wp-content/uploads/2024/10/Breasts-rig.zip">Breasts rig</a><a href="https://www.miaodonghua.com/wp-content/uploads/2024/10/Breasts-rig.zip" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-c183ac43-bf12-489e-90a6-82994404c032">点击此处下载（Click Here Download）</a></div>



<p>这段视频内容主要介绍了如何在Maya中为模型的胸部添加绑定。以下是主要步骤总结：</p>



<ol class="wp-block-list">
<li><strong>介绍</strong>：</li>
</ol>



<ul class="wp-block-list">
<li>主持人向大家问好，并说明今天要讲解的内容是胸部的绑定。</li>



<li>提到之前的视频比较复杂，因此将提供更简单的方法。</li>
</ul>



<ol class="wp-block-list">
<li><strong>导入绑定文件</strong>：</li>
</ol>



<ul class="wp-block-list">
<li>主持人导出了一个独立的胸部绑定文件，观众可以下载并使用。</li>
</ul>



<ol class="wp-block-list">
<li><strong>操作步骤</strong>：</li>
</ol>



<ul class="wp-block-list">
<li>打开新的绑定文件，导入胸部模型。</li>



<li>调整模型大小并匹配位置，确保其与其他关节对齐。</li>



<li>选择胸部和关节，执行父对象约束。</li>



<li>为确保后期缩放时模型能够跟随，需要添加缩放约束。</li>
</ul>



<ol class="wp-block-list">
<li><strong>添加权重影响</strong>：</li>
</ol>



<ul class="wp-block-list">
<li>选择首尾骨骼，展开并选择身体进行蒙皮设置。</li>



<li>取消“使用几何体”选项，勾选权重锁定，默认值为0，然后添加影响。</li>
</ul>



<ol class="wp-block-list">
<li><strong>刷权重</strong>：</li>
</ol>



<ul class="wp-block-list">
<li>讲解了如何刷权重，确保胸部绑定效果正确。</li>
</ul>



<ol class="wp-block-list"></ol>



<ul class="wp-block-list"></ul>
<p><a href="https://www.miaodonghua.com/3469.html">Maya绑定教学：一键导入胸部骨架预设</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.miaodonghua.com/3469.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Maya清除场景中的未知节点（Maya clears unknown nodes in the scene）</title>
		<link>https://www.miaodonghua.com/3466.html</link>
					<comments>https://www.miaodonghua.com/3466.html#respond</comments>
		
		<dc:creator><![CDATA[喵喵动画屋]]></dc:creator>
		<pubDate>Thu, 01 Aug 2024 17:36:28 +0000</pubDate>
				<category><![CDATA[MAYA动画教学]]></category>
		<guid isPermaLink="false">https://www.miaodonghua.com/?p=3466</guid>

					<description><![CDATA[<p>当我们需要把Maya场景保存为ma格式的时候，如果maya自带的优化场景无效，可以尝试以下mel代码来清除。（ &#8230; </p>
<p class="link-more"><a href="https://www.miaodonghua.com/3466.html" class="more-link">继续阅读<span class="screen-reader-text">“Maya清除场景中的未知节点（Maya clears unknown nodes in the scene）”</span></a></p>
<p><a href="https://www.miaodonghua.com/3466.html">Maya清除场景中的未知节点（Maya clears unknown nodes in the scene）</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">当我们需要把Maya场景保存为ma格式的时候，如果maya自带的优化场景无效，可以尝试以下mel代码来清除。</mark></strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-black-color">（When we need to save the Maya scene in ma format, if the optimized scene that comes with Maya is invalid, you can try the following mel code to clear it.）</mark></p>



<pre class="wp-block-code has-vivid-green-cyan-color has-black-background-color has-text-color has-background has-link-color wp-elements-e8a765f379898814fddcb5518b75d525"><code>{
    string $unknownNodes&#91;] = `lsType unknown`;
    for($node in $unknownNodes){
        if($node=="&lt;done>")
            break;
        if(`objExists $node`)
        {
            int $lockState&#91;] = `lockNode -q -l $node`;
            if($lockState&#91;0]==1)
            lockNode -l off $node;
            delete $node;
        }
    }
}</code></pre>



<p>论坛作者链接：<a href="https://autodeskfeedback.az1.qualtrics.com/jfe/form/SV_1yNWNw40yATej3g">https://autodeskfeedback.az1.qualtrics.com/jfe/form/SV_1yNWNw40yATej3g</a></p>
<p><a href="https://www.miaodonghua.com/3466.html">Maya清除场景中的未知节点（Maya clears unknown nodes in the scene）</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.miaodonghua.com/3466.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Maya Mery_v3.5走路和跑步动画源文件下载</title>
		<link>https://www.miaodonghua.com/3458.html</link>
					<comments>https://www.miaodonghua.com/3458.html#respond</comments>
		
		<dc:creator><![CDATA[喵喵动画屋]]></dc:creator>
		<pubDate>Sat, 29 Jun 2024 07:49:58 +0000</pubDate>
				<category><![CDATA[MAYA动画教学]]></category>
		<guid isPermaLink="false">https://www.miaodonghua.com/?p=3458</guid>

					<description><![CDATA[<p><a href="https://www.miaodonghua.com/3458.html">Maya Mery_v3.5走路和跑步动画源文件下载</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-file"><a id="wp-block-file--media-17e7befe-14b8-427e-af93-7e448b9f282a" href="https://www.miaodonghua.com/wp-content/uploads/2024/06/Mery_v3.5（走路跑步）.7z">Mery_v3.5（WalkCycle+RunCycle）</a><a href="https://www.miaodonghua.com/wp-content/uploads/2024/06/Mery_v3.5（走路跑步）.7z" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-17e7befe-14b8-427e-af93-7e448b9f282a">下载</a></div>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="1280" height="720" src="https://www.miaodonghua.com/wp-content/uploads/2021/01/Maya跑步动画_youtube封面.webp" alt="maya%e8%b7%91%e6%ad%a5%e5%8a%a8%e7%94%bb_youtube%e5%b0%81%e9%9d%a2" class="wp-image-1738"/></figure>



<figure class="wp-block-image size-full"><img decoding="async" width="1146" height="717" src="https://www.miaodonghua.com/wp-content/uploads/2021/01/May跑步动画_B站封面.webp" alt="may%e8%b7%91%e6%ad%a5%e5%8a%a8%e7%94%bb_b%e7%ab%99%e5%b0%81%e9%9d%a2" class="wp-image-1736"/></figure>
<p><a href="https://www.miaodonghua.com/3458.html">Maya Mery_v3.5走路和跑步动画源文件下载</a>最先出现在<a href="https://www.miaodonghua.com">喵喵动画屋</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.miaodonghua.com/3458.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
