unity3d PuppetMaster 布娃娃插件在学习
一、核心目标:从布娃娃到 “动画 + 物理” 双骨骼角色
这份指南的核心是将已有的 布娃娃(Ragdoll) 转化为 PuppetMaster 的 “双骨骼系统”(Animated Target + Ragdoll Puppet),实现 “动画驱动物理、物理反馈动画” 的高效联动,同时规避单骨骼系统的性能问题。
二、完整实操步骤(图文式拆解)
步骤 1:准备合格的布娃娃(前置条件)
首先需确保布娃娃满足 PuppetMaster 的核心要求 ——必须使用 ConfigurableJoints(可配置关节)。
- 若你的布娃娃已用 ConfigurableJoints:直接拖入 Unity 场景即可。
- 若用的是其他关节(如 CharacterJoints):选中布娃娃的 根 GameObject,点击顶部菜单 GameObject > Convert To ConfigurableJoints,自动将所有关节转换为可配置关节。
补充:若没有现成布娃娃,可先用
BipedRagdollCreator
组件生成(见此前对话),再执行后续步骤。
步骤 2:添加 PuppetMaster 组件
选中场景中布娃娃的 根 Transform(最顶层父对象),在 Inspector 面板点击 Add Component,搜索并添加 PuppetMaster
组件。
步骤 3:指定 “动画目标(Animated Target)”
“Animated Target” 是负责播放动画的角色(双骨骼系统中的 “Target”),有 3 种常见配置方式,根据你的需求选择:
配置方式 | 操作方法 | 适用场景 |
---|---|---|
复用布娃娃本身 | 将 “Animated Target” 字段拖入当前布娃娃的根对象。 | 快速测试,无需额外角色模型;工具会自动复制布娃娃,原对象变 Target(删物理组件),副本变 Puppet(删动画 / 模型组件)。 |
指定同角色的另一实例 | 提前在场景中放一个带动画的角色实例,将其根对象拖入 “Animated Target”。 | 动画与物理分离(如 Target 有精细模型 / 动画,Puppet 用简化骨骼)。 |
指定不同角色(骨骼匹配) | 拖入另一个角色(需骨骼位置匹配,旋转可不同)。 | 共享布娃娃(如多个角色共用一套物理骨骼);解决 Mixamo 角色脚部不贴地问题(旋转 Puppet 脚部,不影响 Target 动画)。 |
步骤 4:执行自动搭建(Set Up PuppetMaster)
点击 PuppetMaster
组件面板中的 “Set Up PuppetMaster” 按钮,工具会自动完成以下工作(无需手动干预):
- 创建一个 “Root” 根对象(命名为 “角色名 + PuppetMaster”);
- 将 “Animated Target”(动画角色)和 “Puppet”(布娃娃)作为子对象归入 Root;
- 自动清理两层结构:
- Target:保留模型、动画器、角色控制器等,删除刚体、碰撞体、关节;
- Puppet:保留骨骼、刚体、碰撞体、关节,删除模型、动画器等非物理组件;
- 自动配置碰撞层(后续需手动禁用层间碰撞,见下文 “关键注意事项”)。
三、关键注意事项(避坑指南)
1. 碰撞层配置(必须做,否则穿模)
PuppetMaster 会给 Target 分配 “Character Controller Layer”,给 Puppet 分配 “Ragdoll Layer”,但需手动禁用两层的碰撞:打开 Edit > Project Settings > Physics > Layer Collision Matrix,找到这两个层对应的行 / 列,取消勾选交叉处的复选框(避免 Target 和 Puppet 自身碰撞穿模)。
2. 核心参数调试(运行时必试)
搭建完成后,进入 Play 模式,在 PuppetMaster
组件面板调试以下关键参数:
- Pin Weight(吸附权重):
- 1 = Puppet 完全吸附到 Target 位置(物理无自由度);
- 滑动到 0 = Puppet 释放到 “肌肉空间”(仅受肌肉力驱动,物理效果最真实,如角色被碰撞后自然晃动)。
- Muscles(单个肌肉权重):展开 “Muscles” 列表,可单独调整某块骨骼(如手臂、腿部)的
muscleWeight
(肌肉强度)、pinWeight
(吸附权重),实现 “局部物理效果”(如让腿部更软、更容易被推开)。 - Mode(模式):
- Active:物理模拟生效;
- Kinematic:Puppet 变运动学刚体(仅跟随动画,无物理);
- Disabled:禁用 Puppet(无性能消耗,适合角色隐藏时使用)。
3. 性能优势:为什么必须用双骨骼系统?
指南特别强调 “双骨骼优于单骨骼”,核心原因是 性能优化:
- 单骨骼系统:动画和物理共用一套骨骼,每帧需移动带碰撞体的对象,且 IK 计算会额外消耗性能;
- 双骨骼系统:
- Target 负责动画 / IK(无碰撞体,计算轻量);
- Puppet 负责物理(简化骨骼,计算高效);两者仅通过 “肌肉力” 和 “吸附力” 联动,避免了 “带碰撞体对象频繁变换” 的性能损耗。
四、快速测试流程(5 分钟验证)
- 拖入一个带 ConfigurableJoints 的布娃娃到场景;
- 给布娃娃根对象加
PuppetMaster
组件,将布娃娃自身拖入 “Animated Target”; - 点击 “Set Up PuppetMaster”,等待自动搭建;
- 进入 Play 模式,滑动 “Pin Weight” 到 0,观察角色是否从 “吸附状态” 变为 “自然物理晃动”;
- 调整 “muscleWeight”(肌肉强度),观察角色物理反馈的 “软硬” 变化。
通过以上步骤,即可快速验证 PuppetMaster 双骨骼系统的核心功能。
实践步揍
1.重置角色的位置和旋转置0,先删除角色的动画组件
2.将预制体实例转为独立对象:选中场景中的角色(预制体实例),右键选择 “Unpack Prefab Completely”(完全解包预制体),将其转为场景中的独立对象(不再关联原预制体)。
- 注意:Unity 2020+ 中 “Prebabs“ ->“Unpack Completely”,。
- 解包原因:
- PuppetMaster 的
SetUpTo
流程中,会执行RemoveUnnecessaryBones
函数,试图调整骨骼的父节点(如将多余骨骼移到新的层级),但预制体实例的 Transform 被 Unity 保护,不允许修改父节点,因此操作失败。
3.添加组件 BipedRagdollCreator,单击Create a Ragdoll,调整colliderLengthOverlap
调整碰撞体重叠度(避免穿模);weight
设角色总重量,然后点击Done
4.添加组件 PuppetMaster,
点击组件的 “Set Up PuppetMaster” 按钮
5.为动画层添加Animator组件
6.PuppetMaster
组件中单击右上角选项选择 Fix Muscle Positions 修复一下.
如果启用了Animatior的Apply Root Motion,就不用PuppetMaster组件中的“Support Translation Animation”(支持平移动画)在 PuppetMaster 语境中,通常指 让双骨骼系统(Target 与 Puppet)正确响应角色的 “位置移动类动画”(如走、跑、跳跃时的根骨骼平移、肢体位移等)
可以在这两个中尝试勾选
一、层级设置的核心目的
PuppetMaster 的层级配置本质是解决 “哪些对象应该碰撞,哪些应该忽略” 的问题,尤其针对:
- 避免角色自身的 “动画骨骼(Characters)” 与 “布娃娃骨骼(Ragdoll)” 互相碰撞(否则会导致模型抖动、穿模);
- 确保布娃娃只与 “地面(Ground)” 和 “可交互物体(Collision)” 碰撞,实现真实的物理交互。
二、层级划分与配置步骤(必做项)
按文档要求,需创建并配置 4 个核心层级,步骤如下:
1. 创建 4 个基础层级
在 Unity 的 “Edit > Project Settings > Tags and Layers” 中,添加以下层级(名称需严格匹配,避免插件识别失败):
Characters
:用于角色的 “动画骨骼” 和角色控制器(Character Controller);Ragdoll
:用于 Puppet 的 “布娃娃骨骼”(物理碰撞体所在层级);Ground
:用于可站立的地面、平台等 “行走表面”;Collision
:用于布娃娃需要碰撞的其他物体(如障碍物、投射物、墙壁等)。
2. 为对象分配层级
- 角色整体:将角色的根节点(包含动画组件的层级)设为
Characters
层; - 布娃娃系统:将 Puppet 的根节点(物理骨骼层级)设为
Ragdoll
层; - 地面:将所有可站立的地面、地板对象设为
Ground
层; - 交互物体:将障碍物、道具等需要与布娃娃碰撞的对象设为
Collision
层。
3. 配置 PuppetMaster 的层级引用
在 PuppetMaster 组件中,指定层级关联(确保插件知道 “哪些层是地面 / 可碰撞对象”):
Walkable Layers
:选择Ground
层(用于判断角色是否在地面上,影响起身逻辑);Collision Layers
:选择Collision
层(用于定义布娃娃需要碰撞的物体层级)。
4. 配置物理碰撞矩阵(关键避坑点)
在 “Edit > Project Settings > Physics” 的 “Layer Collision Matrix” 中,按以下规则勾选 / 取消勾选碰撞关系:
层级组合 | 是否允许碰撞 | 原因说明 |
---|---|---|
Characters ↔ Ragdoll | ❌ 不勾选 | 避免角色的动画骨骼与布娃娃骨骼互相碰撞(会导致模型抖动、穿模)。 |
Characters ↔ Collision | ❌ 不勾选 | 让动画角色(非布娃娃状态)忽略障碍物(避免角色控制器与障碍物碰撞冲突)。 |
Ragdoll ↔ Ground | ✅ 勾选 | 允许布娃娃与地面碰撞(确保站立、摔倒时能与地面交互)。 |
Ragdoll ↔ Collision | ✅ 勾选 | 允许布娃娃与障碍物碰撞(实现碰撞响应,如被障碍物绊倒)。 |
Ragdoll ↔ Ragdoll | ❌ 不勾选 | 避免布娃娃自身骨骼互相碰撞(如手臂碰身体,会导致物理异常)。 |
三、进阶配置:多阵营角色(如敌对派系)
若项目中有多个阵营的角色(如 “友方” 和 “敌方”),需扩展层级设置以支持阵营间的碰撞:
创建阵营专属层级:
- 友方:
Characters1
(动画骨骼)、Ragdoll1
(布娃娃); - 敌方:
Characters2
(动画骨骼)、Ragdoll2
(布娃娃);保留Ground
和Collision
层共用。
- 友方:
调整碰撞矩阵:
- 同一阵营内:
Characters1
↔Ragdoll1
不碰撞(同之前规则); - 阵营间:
Ragdoll1
↔Ragdoll2
勾选碰撞(允许敌对布娃娃互相碰撞); PuppetMaster
的Collision Layers
需包含敌方布娃娃层(如Ragdoll2
)。
- 同一阵营内:
四、关键说明
- “Layer Setup 组件” 的作用:文档提到的
LayerSetup
组件是一个 “快捷工具”,可自动配置上述层级和碰撞矩阵(无需手动修改 Project Settings),适合快速运行 Demo。但正式项目建议手动配置层级(避免多人协作时的层级冲突)。 - 层级名称的严格性:PuppetMaster 的部分逻辑(如
SubBehaviourCOM
的地面检测)依赖层级名称匹配,若自定义层级名,需确保插件参数(如Walkable Layers
)同步更新。 - 2D 项目适配:若使用 2D 物理,需在 “Physics 2D” 中配置碰撞矩阵,规则与 3D 一致(替换
Physics
为Physics 2D
即可)。
总结
层级配置是 PuppetMaster 物理交互的 “地基”,核心原则是:明确区分 “动画骨骼” 与 “布娃娃骨骼”,仅让布娃娃与必要对象(地面、障碍物)碰撞。按上述步骤配置后,可解决绝大多数 “自碰撞穿模”“布娃娃不与地面交互” 等基础问题,为后续行为系统(如 BehaviourPuppet
的碰撞响应)提供正确的物理环境。
力反馈事件
在布娃娃物体下物体里 在Behaviours物体下新建空物体,添加BehaviourPuppet.cs脚本
在其他物体上可以发送力,如下面例子通过鼠标射线方向发射了一个100力作用
using UnityEngine;
using System.Collections;
using RootMotion.Dynamics;namespace RootMotion.Demos {public class RaycastShooter : MonoBehaviour {public LayerMask layers;public float unpin = 10f;public float force = 10f;public ParticleSystem blood;// Update is called once per framevoid Update () {if (Input.GetMouseButtonDown(0)) {Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);// Raycast to find a ragdoll colliderRaycastHit hit = new RaycastHit();if (Physics.Raycast(ray, out hit, 100f, layers)) {var broadcaster = hit.collider.attachedRigidbody.GetComponent<MuscleCollisionBroadcaster>();if (broadcaster != null) {broadcaster.Hit(unpin, ray.direction * force, hit.point);blood.transform.position = hit.point;blood.transform.rotation = Quaternion.LookRotation(-ray.direction);blood.Emit(5);}}}}}
}
一、核心概念:Puppet Behaviours(布娃娃行为)
Puppet Behaviours
是继承自抽象类 BehaviourBase.cs
的一系列功能类,核心目标是 “动态调整布娃娃(Puppet)的物理参数(肌肉力、吸附权重等)或动画目标姿态,实现多样化的物理 - 动画交互逻辑”。
设计理念
- 功能模块化:每个行为(Behaviour)专注于单一核心逻辑(如 “碰撞响应”“摔倒处理”“起身控制”),避免代码冗余。
- 低耦合可复用:行为类不包含外部对象引用(如不直接绑定特定模型或动画),可直接复制到其他布娃娃上复用。
- 可切换性:支持在运行时动态切换不同行为(如 “正常状态”→“摔倒状态”→“起身状态”),实现复杂交互流程。
二、核心组成:行为、事件与子行为
1. 基础行为(Puppet Behaviours)
(1)核心行为示例:BehaviourPuppet.cs
作为最关键的内置行为,它实现了布娃娃的 “基础物理 - 动画绑定逻辑”,具体功能包括:
- 维持布娃娃与动画目标(Target)的吸附(Pinning);
- 碰撞时释放碰撞部位(及父子骨骼)的吸附权重,让角色 “松弛”;
- 检测骨骼与目标的偏移量,判断是否 “失衡”;
- 失衡时触发摔倒动画,倒地后支持自动起身。
(2)未来扩展行为
文档提到未来将开发的行为(初始版本暂缺),体现了框架的可扩展性:
BehaviourCatchFall
:可能用于 “角色试图抓住物体避免摔倒” 的逻辑;BehaviourWindmill
:可能用于 “角色失衡时挥舞手臂维持平衡” 的物理反馈。
2. 行为切换(Switching Behaviours)
多个行为可共存,但同一时间仅能有一个 “激活状态”,切换规则如下:
(1)编辑器配置
- 初始状态下,仅启用 “首个运行的行为”(如仅启用
BehaviourPuppet
,禁用BehaviourFall
),确保角色从 “正常动画状态” 启动。
(2)代码切换(唯一正确方式)
必须通过调用抽象类 BehaviourBase
的静态方法 Activate()
切换行为,该方法会自动禁用其他所有行为,避免逻辑冲突。示例代码:
csharp
// 切换到“摔倒行为”(假设已创建 BehaviourFall 实例)
BehaviourBase.Activate(behaviourFallInstance);
3. 事件系统(Events)
行为在特定触发条件下(如失衡、倒地、起身)会抛出 PuppetEvent,用于关联动画、切换行为或触发自定义逻辑。每个事件包含 3 个可配置项:
配置项 | 作用说明 |
---|---|
switchToBehaviour | 事件触发时切换到的目标行为(需填写行为的完整类型名,注意拼写准确)。 |
animations | 事件触发时交叉淡入的动画(独立于 UnityEvent,支持 Animator.CrossFade 等多参数调用)。 |
unityEvent | 事件触发时调用的 UnityEvent,可关联自定义脚本的方法(如播放音效、触发粒子)。 |
示例场景
- 当
BehaviourPuppet
检测到 “失衡” 时,触发On Loose Balance
事件:- 通过
switchToBehaviour
切换到BehaviourFall
; - 通过
animations
交叉淡入 “摔倒动画”; - 通过
unityEvent
调用自定义脚本播放 “摔倒音效”。
- 通过
4. 子行为(Sub-Behaviours)
定义与作用
子行为是 “可复用的功能模块”,用于封装多个行为共需的逻辑,避免代码重复,核心特点是 “自我包含、跨行为共享”。
示例:SubBehaviourCOM
该子行为专注于 “重心(Center of Mass, COM)相关计算”,为所有依赖重心的行为提供数据支持,计算内容包括:
- 压力中心(Center of Pressure);
- 重心向量的方向与角度;
- 布娃娃是否 “接地” 的状态检测。
使用价值
- 无需在
BehaviourPuppet
、BehaviourFall
等多个行为中重复编写重心计算代码; - 修改
SubBehaviourCOM
即可全局更新重心逻辑,提升维护效率。
三、扩展能力:创建自定义行为
PuppetMaster 设计之初就支持自定义扩展,创建自定义行为的核心步骤如下:
1. 继承抽象类
创建新类并继承 BehaviourBase.cs
(或复制 BehaviourTemplate.cs
模板),模板已包含基础框架,只需添加核心逻辑。
2. 实现关键方法
抽象类 BehaviourBase
会要求实现必要方法(如初始化、帧更新、行为激活 / 禁用逻辑),示例框架:
csharp
// 自定义“被击打后摇晃”的行为
public class BehaviourHitReaction : BehaviourBase {// 摇晃强度参数(可在 Inspector 配置)public float shakeIntensity = 0.5f;// 行为激活时调用(如被击打时触发)protected override void OnActivate() {// 初始化摇晃逻辑}// 每帧更新(动态调整肌肉权重实现摇晃)protected override void UpdateBehaviour() {foreach (var muscle in puppetMaster.muscles) {// 随机调整肌肉吸附权重,模拟摇晃muscle.pinWeight = Mathf.Lerp(muscle.pinWeight, 1 - shakeIntensity, Time.deltaTime);}}// 行为禁用时调用(摇晃结束后恢复)protected override void OnDeactivate() {// 重置肌肉权重}
}
3. 关联子行为(可选)
若自定义行为需要重心、碰撞等通用数据,可直接引用子行为(如 SubBehaviourCOM
),无需重复开发:
csharp
// 在自定义行为中引用重心子行为
private SubBehaviourCOM comSubBehaviour;protected override void Awake() {base.Awake();// 获取布娃娃上的重心子行为实例comSubBehaviour = GetComponent<SubBehaviourCOM>();
}protected override void UpdateBehaviour() {// 使用子行为提供的“是否接地”数据if (comSubBehaviour.isGrounded) {// 接地时的摇晃逻辑}
}
四、总结:行为系统的核心价值
PuppetMaster 的行为系统通过 “基础行为(核心逻辑)+ 事件(交互触发)+ 子行为(功能复用)+ 自定义扩展(灵活适配)” 的架构,解决了 “物理模拟与动画控制的耦合难题”:
- 对开发者:无需从零编写碰撞检测、平衡判断等复杂逻辑,直接复用内置行为;
- 对项目:支持根据需求扩展自定义行为(如 “被击打反应”“攀爬失衡”),适配多样化玩法;
- 对维护:模块化设计降低代码冗余,子行为确保逻辑一致性。
理解该系统的关键是抓住 “行为切换的唯一性”(必须通过 BehaviourBase.Activate()
)和 “事件的桥梁作用”(连接物理状态与动画 / 自定义逻辑)。