创建BlendAnimationNode虚拟节点,封装混合动画Mixer和PlayableList。
using GraphVisualizer;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
public class BlenderAnimationNode : AnimationNodeBase
{
public AnimationMixerPlayable blendMixer;
private List<AnimationClipPlayable> blendClipPlayableList = new List<AnimationClipPlayable>(10);
public void Init(PlayableGraph graph,AnimationMixerPlayable outputMixer,List<AnimationClip> clips,float speed, int inputPort)
{
blendMixer = AnimationMixerPlayable.Create(graph, clips.Count);
graph.Connect(blendMixer, 0, outputMixer, InputPort);
this.InputPort = inputPort;
for (int i = 0; i < clips.Count; i++)
{
CreateAndConnectBlendPlayable(graph, clips[i], i, speed);
}
}
public void Init(PlayableGraph graph, AnimationMixerPlayable outputMixer, AnimationClip clip1,AnimationClip clip2, float speed, int inputPort)
{
blendMixer = AnimationMixerPlayable.Create(graph, 2);
graph.Connect(blendMixer, 0, outputMixer, InputPort);
this.InputPort = inputPort;
CreateAndConnectBlendPlayable(graph, clip1, 0, speed);
CreateAndConnectBlendPlayable(graph, clip2, 1, speed);
}
private AnimationClipPlayable CreateAndConnectBlendPlayable(PlayableGraph graph,AnimationClip clip, int index, float speed)
{
AnimationClipPlayable clipPlayable = AnimationClipPlayable.Create(graph, clip);
clipPlayable.SetSpeed(speed);
blendClipPlayableList.Add(clipPlayable);
graph.Connect(clipPlayable, 0, blendMixer, index);
return clipPlayable;
}
public void SetBlendWeight(float clip1Weight)
{
blendMixer.SetInputWeight(0, clip1Weight);
blendMixer.SetInputWeight(1, 1 - clip1Weight);
}
public void SetBlendWeight(List<float> weightList)
{
for (int i = 0; i < blendClipPlayableList.Count; i++)
{
blendMixer.SetInputWeight(i, weightList[i]);
}
}
public override void SetSpeed(float speed)
{
for (int i = 0; i < blendClipPlayableList.Count; i++)
{
blendClipPlayableList[i].SetSpeed(speed);
}
}
public override void PushPool()
{
blendClipPlayableList.Clear();
base.PushPool();
}
}
相比SingleMode,Blend节点有多个动画的List,在回收时要清空数据,所以首先重写PushPool。
public override void PushPool()
{
blendClipPlayableList.Clear();
base.PushPool();
}
类似的,把原来在AnimationController里面的有关BlendMixer创建,连接,设置速度的逻辑转移到抽象节点下,逻辑相同。
/// <summary>
/// 播放混合动画
/// </summary>
/// <param name="clips"></param>
/// <param name="speed"></param>
/// <param name="transitionFixedTime"></param>
public void PlayBlendAnimaiton(List<AnimationClip> clips,float speed = 1,float transitionFixedTime = 0.25f)
{
BlenderAnimationNode blenderAnimationNode = PoolManager.Instance.GetObject<BlenderAnimationNode>();
// 如果是第一次播放,不存在过渡
if (currentNode == null)
{
blenderAnimationNode.Init(graph, mixer, clips, speed, inputPort0);
mixer.SetInputWeight(inputPort0, 1);
}
else
{
DestoryNode(previousNode);
blenderAnimationNode.Init(graph, mixer, clips, speed, inputPort1);
previousNode = currentNode;
StartTransitionAnimation(transitionFixedTime);
}
this.speed = speed; //只需要把记录值更新一下即可,在Init时实际每个动画都已经设置好了速度,不用使用属性再赋值
currentNode = blenderAnimationNode;
if (graph.IsPlaying() == false) graph.Play();
}
/// <summary>
/// 播放混合动画(2个)
/// </summary>
/// <param name="clips"></param>
/// <param name="speed"></param>
/// <param name="transitionFixedTime"></param>
public void PlayBlendAnimaiton(AnimationClip clip1,AnimationClip clip2, float speed = 1, float transitionFixedTime = 0.25f)
{
BlenderAnimationNode blenderAnimationNode = PoolManager.Instance.GetObject<BlenderAnimationNode>();
// 如果是第一次播放,不存在过渡
if (currentNode == null)
{
blenderAnimationNode.Init(graph, mixer, clip1,clip2, speed, inputPort0);
mixer.SetInputWeight(inputPort0, 1);
}
else
{
DestoryNode(previousNode);
blenderAnimationNode.Init(graph, mixer, clip1,clip2, speed, inputPort1);
previousNode = currentNode;
StartTransitionAnimation(transitionFixedTime);
}
this.speed = speed; //只需要把记录值更新一下即可,在Init时实际每个动画都已经设置好了速度,不用使用属性再赋值
currentNode = blenderAnimationNode;
if (graph.IsPlaying() == false) graph.Play();
}
public void SetBlendWeight(float clip1Weight)
{
(currentNode as BlenderAnimationNode).SetBlendWeight(clip1Weight);
}
public void SetBlendWeight(List<float> weightList)
{
(currentNode as BlenderAnimationNode).SetBlendWeight(weightList);
}
AnimationController里此时只需要考虑动画过渡,断连,连接的逻辑,由于我们约定了新旧两个端口,并在动画过渡里完善了交换逻辑,不需要多余的判断条件,只有两种情况,要么是第一次播放,使用0端口,要么就是非第一次播放,使用最新的1端口。设置权重的逻辑相同,调用抽象节点内封装的方法即可。
到此,我么已经完成了对AnimationController的重构,通过指定端口大大简化了判断逻辑,并通过抽象节点的封装使得代码逻辑更加清晰,同时,这种重构增加了Blend动画到Blend动画的过渡,因为我们原来实际只考虑了播放单个动画和单个到多个的过渡,如果想实现Blend到Blend的过渡必须新加判断逻辑并考虑判断的优先顺序,而现在通过抽象节点,在AnimationController里并不考虑具体是哪个节点到哪个节点切换,上层只负责根据传进来的参数调用抽象节点的方法完成及诶点断连,连接。至于节点具体如何释放,建立由抽象节点内部逻辑负责。