建设部网站办事大厅,网络优化网站,网站建设栏目这一块怎么写,博山网站seoPlayable 基本用法 Playable意思是可播放的#xff0c;可运行的。Playable整体是树形结构#xff0c;PlayableGraph相当于一个容器#xff0c;所有元素都被包含在里面#xff0c;图中的每个节点都是Playable#xff0c;叶子节点的Playable包裹原始数据#xff0c;相当于输…Playable 基本用法 Playable意思是可播放的可运行的。Playable整体是树形结构PlayableGraph相当于一个容器所有元素都被包含在里面图中的每个节点都是Playable叶子节点的Playable包裹原始数据相当于输入中间的Mixer根据权重混合多个输入最后汇总到根部的Output节点然后由PlayableGraph播放。 Playable的核心类型 Playable的输出类型
这些不同类型的Playable都是结构体所以它们之间不是继承关系但是可以隐式转换如
AnimationClipPlayable clipPlayable AnimationClipPlayable.Create(playableGraph, clip);
//隐式转换
Playable playable clipPlayable;播放单个动画片段
官方示例
[RequireComponent(typeof(Animator))]
public class PlayAnimationSample : MonoBehaviour
{public AnimationClip clip;private PlayableGraph playableGraph;void Start(){//创建PlayableGraphplayableGraph PlayableGraph.Create();playableGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);//创建AnimationClipPlayable包裹AnimationClip附加到PlayableGraph上var clipPlayable AnimationClipPlayable.Create(playableGraph, clip);//创建输出节点并把Animator设为目标Animator会处理PlayableGraphvar playableOutput AnimationPlayableOutput.Create(playableGraph, Animation, GetComponentAnimator());//连接输入源playableOutput.SetSourcePlayable(clipPlayable);// Plays the Graph.playableGraph.Play();}void OnDisable(){playableGraph.Destroy();}
}这样不使用Animator Controller通过脚本就可以控制动画播放而且Animator Controller是不允许运行时添加、删除动画的使用Playable就可以运行时添加删除动画。 使用PlayableGraph Visualizer查看Playable结构
创建动画混合树
[RequireComponent(typeof(Animator))]
public class MixAnimationSample : MonoBehaviour
{public AnimationClip clip0;public AnimationClip clip1;public float weight;private PlayableGraph playableGraph;private AnimationMixerPlayable mixerPlayable;private AnimationClipPlayable clipPlayable0;void Start(){playableGraph PlayableGraph.Create();playableGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);var playableOutput AnimationPlayableOutput.Create(playableGraph, Animation, GetComponentAnimator());//创建AnimationMixerPlayable2表示输入的数量mixerPlayable AnimationMixerPlayable.Create(playableGraph, 2);playableOutput.SetSourcePlayable(mixerPlayable);clipPlayable0 AnimationClipPlayable.Create(playableGraph, clip0);var clipPlayable1 AnimationClipPlayable.Create(playableGraph, clip1);//连接两个PlayableclipPlayable是源头mixerPlayable是目标//clipPlayable0和clipPlayable1的默认输出端口号是0分别连接到mixerPlayable输入端口0和1playableGraph.Connect(clipPlayable0, 0, mixerPlayable, 0);playableGraph.Connect(clipPlayable1, 0, mixerPlayable, 1);playableGraph.Play();}void Update(){//保证所有输入源的权重和为1weight Mathf.Clamp01(weight);mixerPlayable.SetInputWeight(0, 1.0f-weight);mixerPlayable.SetInputWeight(1, weight);//切换输入的状态if (Input.GetKeyDown(KeyCode.Space)){if (clipPlayable0.GetPlayState() PlayState.Playing){clipPlayable0.Pause();}else{clipPlayable0.Play();clipPlayable0.SetTime(0f);}}}void OnDisable(){playableGraph.Destroy();}
}调整权重在两个动画之间过渡我们还可以修改某个输入节点的状态 大型的RPG或FPS游戏没必要把大量的动画都添加到Graph中我们可以预先创建好需要的子树然后根据需要在添加到Graph中
混合AnimationClip和AnimatorController
[RequireComponent(typeof(Animator))]
public class RuntimeControllerSample : MonoBehaviour
{public AnimationClip clip;public RuntimeAnimatorController controller;public float weight;private PlayableGraph playableGraph;private AnimationMixerPlayable mixerPlayable;void Start(){playableGraph PlayableGraph.Create();var playableOutput AnimationPlayableOutput.Create(playableGraph, Animation, GetComponentAnimator());mixerPlayable AnimationMixerPlayable.Create(playableGraph, 2);playableOutput.SetSourcePlayable(mixerPlayable);var clipPlayable AnimationClipPlayable.Create(playableGraph, clip);var ctrlPlayable AnimatorControllerPlayable.Create(playableGraph, controller);playableGraph.Connect(clipPlayable, 0, mixerPlayable, 0);playableGraph.Connect(ctrlPlayable, 0, mixerPlayable, 1);playableGraph.Play();}void Update(){weight Mathf.Clamp01(weight);mixerPlayable.SetInputWeight(0, 1.0f-weight);mixerPlayable.SetInputWeight(1, weight);}void OnDisable(){playableGraph.Destroy();}
}AnimationClipPlayable包裹AnimationClip而AnimationrControllerPlayable则包裹RuntimeAnimationrController 每个角色都有的动画如走跑跳用Animator管理角色的特殊动画用Playable和Animator融合
多个输出
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(AudioSource))]
public class MultiOutputSample : MonoBehaviour
{public AnimationClip animationClip;public AudioClip audioClip;private PlayableGraph playableGraph;void Start(){playableGraph PlayableGraph.Create();var animationOutput AnimationPlayableOutput.Create(playableGraph, Animation, GetComponentAnimator());var audioOutput AudioPlayableOutput.Create(playableGraph, Audio, GetComponentAudioSource());var animationClipPlayable AnimationClipPlayable.Create(playableGraph, animationClip);var audioClipPlayable AudioClipPlayable.Create(playableGraph, audioClip, true);animationOutput.SetSourcePlayable(animationClipPlayable);audioOutput.SetSourcePlayable(audioClipPlayable);playableGraph.Play();}void OnDisable(){playableGraph.Destroy();}
}两个输出对象分别是Animator和AudioSource
自定义PlayableBehaviour实现动画队列
PlayableBehaviour 是一个用于实现自定义 Playable 的基类它可以让开发者通过继承该类来自定义 Playable 行为可以用于在播放过程中控制动画的逻辑
public class PlayQueuePlayable : PlayableBehaviour
{private int m_CurrentClipIndex -1;private float m_TimeToNextClip;private Playable mixer;public void Initialize(AnimationClip[] clipsToPlay, Playable owner, PlayableGraph graph){owner.SetInputCount(1);mixer AnimationMixerPlayable.Create(graph, clipsToPlay.Length);graph.Connect(mixer, 0, owner, 0);owner.SetInputWeight(0, 1);for (int clipIndex 0; clipIndex mixer.GetInputCount(); clipIndex){graph.Connect(AnimationClipPlayable.Create(graph, clipsToPlay[clipIndex]), 0, mixer, clipIndex);mixer.SetInputWeight(clipIndex, 1.0f);}}/// summary/// 每帧调用/// /summarypublic override void PrepareFrame(Playable owner, FrameData info){if (mixer.GetInputCount() 0)return;m_TimeToNextClip - (float)info.deltaTime;if (m_TimeToNextClip 0.0f){m_CurrentClipIndex;if (m_CurrentClipIndex mixer.GetInputCount())m_CurrentClipIndex 0;//切换到下一个动画片段var currentClip (AnimationClipPlayable)mixer.GetInput(m_CurrentClipIndex);currentClip.SetTime(0);m_TimeToNextClip currentClip.GetAnimationClip().length;}//当前片段权重设为1其他为0for (int clipIndex 0; clipIndex mixer.GetInputCount(); clipIndex){mixer.SetInputWeight(clipIndex, clipIndex m_CurrentClipIndex ? 1.0f : 0.0f);}}
}[RequireComponent(typeof (Animator))]
public class PlayQueueSample : MonoBehaviour
{public AnimationClip[] clipsToPlay;private PlayableGraph playableGraph;void Start(){playableGraph PlayableGraph.Create();var playQueuePlayable ScriptPlayablePlayQueuePlayable.Create(playableGraph);var playQueue playQueuePlayable.GetBehaviour();playQueue.Initialize(clipsToPlay, playQueuePlayable, playableGraph);var playableOutput AnimationPlayableOutput.Create(playableGraph, Animation, GetComponentAnimator());playableOutput.SetSourcePlayable(playQueuePlayable);playableOutput.SetSourceInputPort(0);playableGraph.Play();}void OnDisable(){playableGraph.Destroy();}
}ScriptPlayable T.Create 是一个静态方法用于创建一个ScriptPlayable T实例并添加到PlayableGraph中。ScriptPlayable T 是一个结构体用于创建自定义的 Playable 行为其中T需要继承 PlayableBehaviour 。ScriptPlayable 结构体还提供了一些静态方法用于创建和管理可播放对象。
ScriptPlayable T实例实际上是将泛型 T 包装在一个结构体中。这个结构体提供了一些方法使得 T 类型能够被 PlayableGraph 所使用。 随机切换动画
实现从一个默认动画随机切换到另一个动画这两个动画之间需要做融合且播放完动画后切会默认动画大致的流程如下 RandomSelector是一个随机选择器Mixer是一个混合器通过调整权重来实现切换 为了方便管理动画把每个动画片段包裹到AnimUnit管理动画状态并输出信息 使用适配器实现多态AnimAdapter里面有一个AnimBehaviour的引用适配器本身没有功能它的具体功能取决于引用AnimBehaviour的哪一个子类
/// summary
/// 适配器
/// /summary
public class AnimAdapter : PlayableBehaviour
{private AnimBehaviour _behaviour;public void Init(AnimBehaviour behaviour){_behaviour behaviour;}public void Enable(){_behaviour?.Enable();}public void Disable(){_behaviour?.Disable();}public override void PrepareFrame(Playable playable, FrameData info){_behaviour?.Execute(playable, info);}public float GetEnterTime(){return _behaviour.GetEnterTime();}public override void OnGraphStop(Playable playable){base.OnGraphStop(playable);_behaviour?.Stop();}
}/// summary
/// 组件基类
/// /summary
public abstract class AnimBehaviour
{public bool enable { get; protected set; }public float remainTime { get; protected set; }//记录这个AnimBehaviour属于那个AnimAdapterprotected Playable _adapterPlayable;protected float _enterTime;protected float _clipLength;public AnimBehaviour(){}public AnimBehaviour(PlayableGraph graph, float enterTime 0){_adapterPlayable ScriptPlayableAnimAdapter.Create(graph);((ScriptPlayableAnimAdapter)_adapterPlayable).GetBehaviour().Init(this);_enterTime enterTime;_clipLength float.NaN;}public virtual void Enable(){enable true;remainTime GetClipLength();}public virtual void Disable(){enable false;}public virtual void Execute(Playable playable, FrameData info){if (!enable)return;remainTime remainTime 0 ? remainTime - info.deltaTime : 0;}public virtual void Stop(){}public Playable GetAnimAdapterPlayable(){return _adapterPlayable;}public virtual void AddInput(Playable playable){}public void AddInput(AnimBehaviour behaviour){AddInput(behaviour.GetAnimAdapterPlayable());}public virtual float GetEnterTime(){return _enterTime;}public virtual float GetClipLength(){return _clipLength;}
}/// summary
/// 输出的子节点作为一个空节点隔开输出和实际的输入
/// Enable就启用所有子节点Disable就禁用所有子节点
/// /summary
public class Root : AnimBehaviour
{public Root(PlayableGraph graph) : base(graph){}public override void AddInput(Playable playable){_adapterPlayable.AddInput(playable, 0, 1);}public override void Enable(){base.Enable();for (int i 0; i _adapterPlayable.GetInputCount(); i){AnimHelper.Enable(_adapterPlayable.GetInput(i));}_adapterPlayable.SetTime(0f);_adapterPlayable.Play();}public override void Disable(){base.Disable();for (int i 0; i _adapterPlayable.GetInputCount(); i){AnimHelper.Disable(_adapterPlayable.GetInput(i));}_adapterPlayable.Pause();}
}public class AnimHelper
{public static void Enable(Playable playable){var adapter GetAdapter(playable);if (adapter ! null){adapter.Enable();}}public static void Enable(AnimationMixerPlayable mixer, int index){Enable(mixer.GetInput(index));}public static void Disable(Playable playable){var adapter GetAdapter(playable);if (adapter ! null){adapter.Disable();}}public static void Disable(AnimationMixerPlayable mixer, int index){Disable(mixer.GetInput(index));}public static AnimAdapter GetAdapter(Playable playable){//检查playbble类型是否继承AnimAdapterif (typeof(AnimAdapter).IsAssignableFrom(playable.GetPlayableType())){return ((ScriptPlayableAnimAdapter)playable).GetBehaviour();}return null;}public static void SetOutput(PlayableGraph graph, Animator animator, AnimBehaviour behaviour){Root root new Root(graph);root.AddInput(behaviour);var output AnimationPlayableOutput.Create(graph, Anim, animator);output.SetSourcePlayable(root.GetAnimAdapterPlayable());}public static void Start(PlayableGraph graph, AnimBehaviour behaviour){graph.Play();behaviour.Enable();}public static void Start(PlayableGraph graph){graph.Play();//获取output的子节点即root节点GetAdapter(graph.GetOutputByTypeAnimationPlayableOutput(0).GetSourcePlayable()).Enable();}public static ComputeShader LoadCompute(string name){ComputeShader computeShader Resources.LoadComputeShader(Compute/ name);//拷贝一份实例不然多个对象公用一个shader数据会冲突return Object.Instantiate(computeShader);}
}AnimUnit 组件
/// summary
/// 包裹AnimationClipPlayable
/// /summary
public class AnimUnit : AnimBehaviour
{private AnimationClipPlayable _clipPlayable;public AnimUnit(PlayableGraph graph, AnimationClip clip, float enterTime 0) : base(graph, enterTime){_clipPlayable AnimationClipPlayable.Create(graph, clip);_adapterPlayable.AddInput(_clipPlayable, 0, 1f);_clipLength clip.length;Disable();}public override void Enable(){base.Enable();_adapterPlayable.SetTime(0);_clipPlayable.SetTime(0);_adapterPlayable.Play();_clipPlayable.Play();}public override void Disable(){base.Disable();_adapterPlayable.Pause();_clipPlayable.Pause();}
}随机动画选择器组件 RandomSelector
/// summary
/// 动画选择器基类
/// /summary
public class AnimSelector : AnimBehaviour
{public int currentIndex { get; protected set; }public int clipCount { get; protected set; }private AnimationMixerPlayable _mixer;private Listfloat _enterTimes;private Listfloat _clipLengths;public AnimSelector(PlayableGraph graph) : base(graph){_mixer AnimationMixerPlayable.Create(graph);_adapterPlayable.AddInput(_mixer, 0, 1f);currentIndex -1;_enterTimes new Listfloat();_clipLengths new Listfloat();}public override void AddInput(Playable playable){_mixer.AddInput(playable, 0);clipCount;}public void AddInput(AnimationClip clip, float enterTime){AddInput(new AnimUnit(_adapterPlayable.GetGraph(), clip, enterTime));_enterTimes.Add(enterTime);_clipLengths.Add(clip.length);}public override void Enable(){base.Enable();if (currentIndex 0 || currentIndex clipCount)return;_mixer.SetInputWeight(currentIndex, 1f);AnimHelper.Enable(_mixer, currentIndex);_adapterPlayable.SetTime(0);_adapterPlayable.Play();_mixer.SetTime(0);_mixer.Play();}public override void Disable(){base.Disable();if (currentIndex 0 || currentIndex clipCount)return;_mixer.SetInputWeight(currentIndex, 0f);AnimHelper.Disable(_mixer, currentIndex);_adapterPlayable.Pause();_mixer.Pause();currentIndex -1;}/// summary/// 根据条件选择一个动画/// /summarypublic virtual int Select(){return currentIndex;}/// summary/// 直接指定索引/// /summarypublic void Select(int index){currentIndex index;}public override float GetEnterTime(){if(currentIndex 0 currentIndex _enterTimes.Count)return _enterTimes[currentIndex];return 0;}public override float GetClipLength(){if(currentIndex 0 currentIndex _clipLengths.Count)return _clipLengths[currentIndex];return 0;}
}/// summary
/// 随机动画选择器
/// /summary
public class RandomSelector : AnimSelector
{public RandomSelector(PlayableGraph graph) : base(graph){}public override int Select(){currentIndex Random.Range(0, clipCount);return currentIndex;}
}1D混合树组件 Mixer 简单的动画混合根据切换的时间计算速度Speed当前动画权重递减目标动画权重递增 速度 * 时间 权重 速度 权重 / 时间 cur动画切换到tar绿动画被tar红打断此时如果cur的权重 tar绿的权重tar绿的权重要按照2倍的速度递减tar红的权重 1 - cur权重 - tar绿权重 如果频繁打断动画就可能有多个动画的权重需要递减到0此时需要一个数组del保存被打断的动画 tar黄权重 1 - cur权重 - del数组内所有动画权重 切换打断时如果cur蓝权重 tar绿权重就交换cur和tar
public class Mixer : AnimBehaviour
{public int inputCount { get; private set; }public int currentIndex _currentIndex;public bool IsTransition _isTransition;private AnimationMixerPlayable _mixerPlayable;//当前动画索引private int _currentIndex;//目标动画索引private int _targetIndex;//递减列表private Listint _declineList;private float _timeToNext;//当前权重的递减速度private float _currentSpeed;//递减列表中权重的递减速度private float _declineSpeed;//是否在切换中private bool _isTransition;public Mixer(PlayableGraph graph) : base(graph){_mixerPlayable AnimationMixerPlayable.Create(graph, 0, true);//连接到adapter上_adapterPlayable.AddInput(_mixerPlayable, 0, 1);_targetIndex -1;_declineList new Listint();}public override void AddInput(Playable playable){base.AddInput(playable);_mixerPlayable.AddInput(playable, 0, 0f);inputCount;if(inputCount 1){_mixerPlayable.SetInputWeight(0, 1f);_currentIndex 0;}}public override void Enable(){base.Enable();if (inputCount 0){AnimHelper.Enable(_mixerPlayable, 0);}_adapterPlayable.SetTime(0);_mixerPlayable.SetTime(0);_adapterPlayable.Play();_mixerPlayable.Play();_mixerPlayable.SetInputWeight(0, 1f);_currentIndex 0;_targetIndex -1;}public override void Disable(){base.Disable();_adapterPlayable.Pause();_mixerPlayable.Pause();for (int i 0; i inputCount; i){_mixerPlayable.SetInputWeight(i, 0);AnimHelper.Disable(_mixerPlayable, i);}}public override void Execute(Playable playable, FrameData info){base.Execute(playable, info);if (!enable || !_isTransition || _targetIndex 0)return;if (_timeToNext 0f){_timeToNext - info.deltaTime;//所有递减动画的权重之和float declineWeight 0;for (int i 0; i _declineList.Count; i){float w ModifyWeight(_declineList[i], -info.deltaTime * _declineSpeed);if (w 0f){AnimHelper.Disable(_mixerPlayable, _declineList[i]);_declineList.Remove(_declineList[i]);}else{declineWeight w;}}float curWeight ModifyWeight(_currentIndex, -info.deltaTime * _currentSpeed);SetWeight(_targetIndex, 1 - declineWeight - curWeight);return;}//切换完成后_isTransition false;AnimHelper.Disable(_mixerPlayable, _currentIndex);_currentIndex _targetIndex;_targetIndex -1;}/// summary/// 切换动画/// /summarypublic void TransitionTo(int index){if (_isTransition _targetIndex 0){//切换中if (index _targetIndex)return;if (index _currentIndex){_currentIndex _targetIndex;}else if (GetWeight(_currentIndex) GetWeight(_targetIndex)){//被打断时当前权重大于目标权重_declineList.Add(_targetIndex);}else{//被打断时当前权重小于目标权重交换_declineList.Add(_currentIndex);_currentIndex _targetIndex;}}else{if (index _currentIndex) return;}_targetIndex index;//传入的targetIndex有可能已在列表里面需要移除_declineList.Remove(_targetIndex);AnimHelper.Enable(_mixerPlayable, _targetIndex);// _timeToNext GetTargetEnterTime(_targetIndex);_timeToNext GetTargetEnterTime(_targetIndex) * (1f - GetWeight(_targetIndex));_currentSpeed GetWeight(_currentIndex) / _timeToNext;_declineSpeed 2f / _timeToNext;_isTransition true;}public float GetWeight(int index){return index 0 index inputCount ? _mixerPlayable.GetInputWeight(index) : 0;}public void SetWeight(int index, float weight){if (index 0 index inputCount){_mixerPlayable.SetInputWeight(index, weight);}}/// summary/// 获取切换时间/// /summaryprivate float GetTargetEnterTime(int index){return ((ScriptPlayableAnimAdapter)_mixerPlayable.GetInput(index)).GetBehaviour().GetEnterTime();}/// summary/// 调整权重/// /summaryprivate float ModifyWeight(int index, float delta){if (index 0 || index inputCount)return 0;float weight Mathf.Clamp01(GetWeight(index) delta);_mixerPlayable.SetInputWeight(index, weight);return weight;}
}测试脚本
public class RandomSelectorExample : MonoBehaviour
{public bool isTransition;public float remainTime;public AnimationClip[] clips;private PlayableGraph _graph;private Mixer _mixer;private RandomSelector _randomSelector;void Start(){_graph PlayableGraph.Create();var idle new AnimUnit(_graph, clips[0], 0.5f);_randomSelector new RandomSelector(_graph);for(int i 1; i clips.Length; i){_randomSelector.AddInput(clips[i], 0.5f);}_mixer new Mixer(_graph);_mixer.AddInput(idle);_mixer.AddInput(_randomSelector);_randomSelector.Select();AnimHelper.SetOutput(_graph, GetComponentAnimator(), _mixer);AnimHelper.Start(_graph);}void Update(){if (Input.GetKeyDown(KeyCode.Space)){_randomSelector.Select();_mixer.TransitionTo(1);}isTransition _mixer.IsTransition;remainTime _randomSelector.remainTime;if (!_mixer.IsTransition _randomSelector.remainTime 0.5f _mixer.currentIndex ! 0){_mixer.TransitionTo(0);}}void OnDestroy(){_graph.Destroy();}
}运行时按空格键随机选择一个动画播放播放完切换到idle
2D混合树组件 BlendTree2D 使用 Compute Shader 把计算移到 GPU 上在 Resources 目录下新建 Compute Shader “BlendTree2D”
// Each #kernel tells which function to compile; you can have many kernels
// 定义主函数名称
#pragma kernel Computestruct DataPair
{float x;float y;float weight;
};float pointerX;
float pointerY;
//很小的数防止除以0
float eps;
//定义结构化缓存
RWStructuredBufferDataPair dataBuffer;float mdistance(DataPair data)
{return abs(pointerX - data.x) abs(pointerY - data.y) eps;
}//声明XYZ三个维度线程组中的线程数量
[numthreads(16,1,1)]
void Compute (uint3 id : SV_DispatchThreadID)
{dataBuffer[id.x].weight 1 / mdistance(dataBuffer[id.x]);
}[Serializable]
public struct BlendClip2D
{public AnimationClip clip;public Vector2 pos;
}public class BlendTree2D : AnimBehaviour
{private struct DataPair{public float x;public float y;public float weight;}private AnimationMixerPlayable _mixer;private DataPair[] _dataPairs;//把权重的计算移到GPU上private ComputeShader _computeShader;//传递数据private ComputeBuffer _computeBuffer;//shader中定义的计算主函数private int _kernel;private int _clipCount;private Vector2 _lastPointer;private int _pointerX;private int _pointerY;private float _total;public BlendTree2D(PlayableGraph graph, BlendClip2D[] clips, float enterTime 0f, float eps 1e-5f) : base(graph, enterTime){_mixer AnimationMixerPlayable.Create(graph);_dataPairs new DataPair[clips.Length];_adapterPlayable.AddInput(_mixer, 0, 1f);for (int i 0; i clips.Length; i){var clip clips[i].clip;var clipPlayable AnimationClipPlayable.Create(graph, clip);_mixer.AddInput(clipPlayable, 0);_dataPairs[i].x clips[i].pos.x;_dataPairs[i].y clips[i].pos.y;}_computeShader AnimHelper.LoadCompute(BlendTree2D);//stride需要设置为4的倍数_computeBuffer new ComputeBuffer(_dataPairs.Length, 12);_kernel _computeShader.FindKernel(Compute);_computeShader.SetBuffer(_kernel, dataBuffer, _computeBuffer);_computeShader.SetFloat(eps, eps);_pointerX Shader.PropertyToID(pointerX);_pointerY Shader.PropertyToID(pointerY);_clipCount clips.Length;_lastPointer.Set(1, 1);SetPointer(0,0);}public override void Enable(){base.Enable();_adapterPlayable.SetTime(0);_adapterPlayable.Play();_mixer.SetTime(0);_mixer.Play();for (int i 0; i _clipCount; i){_mixer.GetInput(i).SetTime(0);_mixer.GetInput(i).Play();}//初始化权重SetPointer(0, 0);}public override void Disable(){base.Disable();_adapterPlayable.Pause();_mixer.Pause();for (int i 0; i _clipCount; i){_mixer.GetInput(i).Pause();}}public void SetPointer(Vector2 input){SetPointer(input.x, input.y);}public void SetPointer(float x, float y){if (_lastPointer.x x _lastPointer.y y)return;_lastPointer.Set(x, y);_computeShader.SetFloat(_pointerX, x);_computeShader.SetFloat(_pointerY, y);_computeBuffer.SetData(_dataPairs);//运行计算着色器以 X、Y 和 Z 维度中的指定计算着色器线程组启动_computeShader.Dispatch(_kernel, _clipCount, 1, 1);_computeBuffer.GetData(_dataPairs);_total 0;int i;for (i 0; i _clipCount; i){_total _dataPairs[i].weight;}for (i 0; i _clipCount; i){_mixer.SetInputWeight(i, _dataPairs[i].weight / _total);}}public override void Stop(){base.Stop();_computeBuffer.Dispose();}
}测试脚本
public class BlendTree2DExample : MonoBehaviour
{public Vector2 pointer;public BlendClip2D[] clips;private PlayableGraph _graph;private BlendTree2D _blendTree2D;void Start(){_graph PlayableGraph.Create();_blendTree2D new BlendTree2D(_graph, clips);AnimHelper.SetOutput(_graph, GetComponentAnimator(), _blendTree2D);AnimHelper.Start(_graph);}void Update(){_blendTree2D.SetPointer(pointer);}void OnDestroy(){_graph.Destroy();}
}运行时修改Pointer就会根据距离在动画片段之间做混合
参考
Playable 动画系统