3.Playable创建动画混合树
3.1 知识点
思路和步骤
用 Playable API 创建动画混合树,可以理解为:创建一个混合器节点,把多个动画 Clip 接进去,用权重控制混合比例。
核心步骤:
- 创建图并设置更新模式
- 创建动画输出,绑定到 Animator
- 创建混合器节点(两种创建方式)
- 将混合器连接到输出
- 创建动画剪辑节点
- 连接动画剪辑到混合器(两种连接方式)
- 播放图
- 在 Update 中动态设置混合权重
- 清理资源
代码实现
步骤1:创建图并设置更新模式
// 创建 Playable 图 并设置更新模式为 GameTime
playableGraph = PlayableGraph.Create();
playableGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
先创建 PlayableGraph,并设置时间更新模式为游戏时间。
步骤2:创建动画输出
// 创建动画输出,连接到 Animator 组件
AnimationPlayableOutput animationPlayableOutput =
AnimationPlayableOutput.Create(playableGraph, "VayneMixIdleAndRun", GetComponent<Animator>());
创建动画输出节点,绑定到场景中的 Animator 组件,混合后的动画结果就能应用到角色上了。
步骤3:创建混合器节点
创建混合器有两种方式,差别主要在“输入端口数什么时候确定”:
方式1:创建时指定输入端口数
// 方式1:创建时指定输入端口数(使用 Connect 方法连接时需要)
animationMixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);
创建时直接指定输入端口数量(这里是 2 个),适合用 Connect() 这种需要明确端口索引的连接方式。
方式2:创建时不指定输入端口数
// 方式2:创建时不指定输入端口数(使用 AddInput 方法时会自动添加输入端口)
animationMixerPlayable = AnimationMixerPlayable.Create(playableGraph);
创建时不指定输入端口数,用 AddInput() 时会自动追加输入端口(按调用顺序往后排)。如果要用 Connect(),也可以创建后再设置端口数:
animationMixerPlayable.SetInputCount(2);
步骤4:将混合器连接到输出
// 将混合器设置为输出源
animationPlayableOutput.SetSourcePlayable(animationMixerPlayable);
把混合器设置为输出的数据源,混合后的结果就会写回 Animator。
步骤5:创建动画剪辑节点
// 创建两个动画剪辑的可播放项
AnimationClipPlayable clipIdlePlayable = AnimationClipPlayable.Create(playableGraph, clipIdle);
AnimationClipPlayable clipRunPlayable = AnimationClipPlayable.Create(playableGraph, clipRun);
把要混合的动画剪辑包装成 Playable 节点,每个 Clip 对应一个 AnimationClipPlayable。
步骤6:连接动画剪辑到混合器
连接动画剪辑到混合器也有两种方式:
方式1:使用 Connect 方法连接
// 方式1:使用 Connect 方法连接(需要先设置输入端口数)
// Connect 参数说明:
// 参数1:源 Playable(要连接的动画剪辑可播放项)
// 参数2:源输出端口(0 - AnimationClipPlayable 只有一个输出端口)
// 参数3:目标 Playable(animationMixerPlayable - 混合器)
// 参数4:目标输入端口(混合器的输入端口索引,0 或 1)
playableGraph.Connect(clipIdlePlayable, 0, animationMixerPlayable, 0);
playableGraph.Connect(clipRunPlayable, 0, animationMixerPlayable, 1);
用 Connect() 需要先设置混合器的输入端口数,并手动指定目标输入端口索引。连接时不会设置初始权重,需要自己补。
方式2:使用 AddInput 方法连接
// 方式2:使用 AddInput 方法连接(会自动添加输入端口,按顺序添加)
// AddInput 参数说明:
// 参数1:源 Playable(要连接的动画剪辑可播放项)
// 参数2:源输出端口(0 - AnimationClipPlayable 只有一个输出端口)
// 参数3:初始权重值(连接时的初始混合权重)
// 注意:AddInput 会按顺序自动添加输入端口,第一个 AddInput 对应索引 0,第二个对应索引 1
animationMixerPlayable.AddInput(clipIdlePlayable, 0, 1.0f - weight);
animationMixerPlayable.AddInput(clipRunPlayable, 0, weight);
AddInput() 会自动添加输入端口,按顺序分配索引(第一个为 0,第二个为 1)。连接时还能设置初始权重,代码更简洁,绝大多数场景够用。
步骤7:播放图
// 播放该图
playableGraph.Play();
开始播放图,之后每帧 Unity 会自动更新并应用到 Animator。
步骤8:设置混合权重(在 Update 中)
void Update()
{
// 限制权重值在 0-1 范围内
weight = Mathf.Clamp01(weight);
// 设置混合器输入权重,要先传入索引
// clipIdle 的权重为 (1 - weight),clipRun 的权重为 weight
// 如果权重加起来不为1,可能导致动画的播放速度异常(总和小于1会变慢,大于1则会变快)
animationMixerPlayable.SetInputWeight(0, 1.0f - weight);
animationMixerPlayable.SetInputWeight(1, weight);
}
在 Update() 中动态调整权重,实现平滑过渡。用 Mathf.Clamp01() 限制权重在 0-1 范围内。
注意:权重总和建议保持为 1。总和不为 1 时混合结果会被整体缩小或放大,姿态贡献不容易直观判断,实战中一般会做归一化。
步骤9:清理资源
void OnDisable()
{
// 销毁该图创建的所有可播放项和输出
playableGraph.Destroy();
}
组件禁用时调用 Destroy() 释放所有资源,包括节点和输出,避免内存泄漏。建议放在 OnDisable(),组件被禁用时就能及时释放。
效果展示


3.2 知识点代码
MixAnimationSample.cs
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
/// <summary>
/// 动画混合树示例组件
/// 该组件演示了如何使用 animationMixerPlayable 混合两个动画剪辑
/// 通过调整 weight 参数可以控制两个动画的混合比例
/// </summary>
[RequireComponent(typeof(Animator))]
public class MixAnimationSample : MonoBehaviour
{
/// <summary>
/// 第一个动画剪辑
/// </summary>
public AnimationClip clipIdle;
/// <summary>
/// 第二个动画剪辑
/// </summary>
public AnimationClip clipRun;
/// <summary>
/// 混合权重值,范围 0-1
/// 0 表示完全播放 clipIdle,1 表示完全播放 clipRun
/// </summary>
[Range(0f, 1f)] public float weight;
/// <summary>
/// Playable 图,用于管理动画播放流程
/// </summary>
PlayableGraph playableGraph;
/// <summary>
/// 动画混合器,用于混合两个动画剪辑
/// </summary>
AnimationMixerPlayable animationMixerPlayable;
/// <summary>
/// 初始化并创建动画混合树
/// </summary>
void Start()
{
// 创建 Playable 图 并设置更新模式为 GameTime
playableGraph = PlayableGraph.Create();
playableGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
// 创建动画输出,连接到 Animator 组件
AnimationPlayableOutput animationPlayableOutput =
AnimationPlayableOutput.Create(playableGraph, "VayneMixIdleAndRun", GetComponent<Animator>());
// 创建混合器
// 方式1:创建时指定输入端口数(使用 Connect 方法连接时需要)
// animationMixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);
// 方式2:创建时不指定输入端口数(使用 AddInput 方法时会自动添加输入端口)
animationMixerPlayable = AnimationMixerPlayable.Create(playableGraph);
// 当然不用AddInput用Connect的话也可以在创建后设置输入端口数
// animationMixerPlayable.SetInputCount(2);
// 将混合器设置为输出源
animationPlayableOutput.SetSourcePlayable(animationMixerPlayable);
// 创建两个动画剪辑的可播放项
AnimationClipPlayable clipIdlePlayable = AnimationClipPlayable.Create(playableGraph, clipIdle);
AnimationClipPlayable clipRunPlayable = AnimationClipPlayable.Create(playableGraph, clipRun);
// 方式1:使用 Connect 方法连接(需要先设置输入端口数)
// Connect 参数说明:
// 参数1:源 Playable(要连接的动画剪辑可播放项)
// 参数2:源输出端口(0 - AnimationClipPlayable 只有一个输出端口,将动画剪辑的输出连接到混合器的输入端口)
// 参数3:目标 Playable(animationMixerPlayable - 混合器)
// 参数4:目标输入端口(混合器的输入端口索引,0 或 1)
// playableGraph.Connect(clipIdlePlayable, 0, animationMixerPlayable, 0);
// playableGraph.Connect(clipRunPlayable, 0, animationMixerPlayable, 1);
// 方式2:使用 AddInput 方法连接(会自动添加输入端口,按顺序添加)
// AddInput 参数说明:
// 参数1:源 Playable(要连接的动画剪辑可播放项)
// 参数2:源输出端口(0 - AnimationClipPlayable 只有一个输出端口)
// 参数3:初始权重值(连接时的初始混合权重)
// 注意:AddInput 会按顺序自动添加输入端口,第一个 AddInput 对应索引 0,第二个对应索引 1
animationMixerPlayable.AddInput(clipIdlePlayable, 0, 1.0f - weight);
animationMixerPlayable.AddInput(clipRunPlayable, 0, weight);
// 播放该图
playableGraph.Play();
}
/// <summary>
/// 每帧更新混合权重
/// 根据 weight 值动态调整两个动画的混合比例
/// </summary>
void Update()
{
// 限制权重值在 0-1 范围内
weight = Mathf.Clamp01(weight);
// 设置混合器输入权重,要先传入索引
// clipIdle 的权重为 (1 - weight),clipRun 的权重为 weight
// 权重总和建议保持为 1,不然混合结果的观感容易怪(整体被稀释/放大)
animationMixerPlayable.SetInputWeight(0, 1.0f - weight);
animationMixerPlayable.SetInputWeight(1, weight);
}
/// <summary>
/// 组件禁用时清理资源,销毁 Playable 图及其所有可播放项和输出
/// </summary>
void OnDisable()
{
// 销毁该图创建的所有可播放项和输出
playableGraph.Destroy();
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com