前面我们第二篇文章Unity Timeline学习笔记(2) - PlayableTrack是一个初步的PlayableTrack使用方法,有时候可能会个性化定制专属轨道。
OnCreateClip的例子
下面我们做一个例子:
首先是轨道
csharp
//FeatureTrack.cs
using System.ComponentModel;
using UnityEngine.Timeline;
[DisplayName("(Feature)开关轨道")]
[TrackClipType(typeof(FeaturePlayableAsset))]
[TrackColor(0.53f, 0.0f, 0.08f)]//表示在编辑器中,Track轨道前端的标识颜色(不重要啦)
public class FeatureTrack : TrackAsset
{
protected override void OnCreateClip(TimelineClip clip)
{
base.OnCreateClip(clip);
clip.displayName = "Clip显示名称";
}
}
这里有TrackClipType表示我们只能添加类型为FeaturePlayableAsset的对象。
我们把PlayableAsset代码也放上:
csharp
//FeaturePlayableAsset.cs
using System;
using System.ComponentModel;
using UnityEngine;
using UnityEngine.Playables;
[DisplayName("(Feature)开关Clip")]
[Serializable]
public class FeaturePlayableAsset : PlayableAsset
{
public string featureName;
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
var p = ScriptPlayable<FeaturePlayableBehaviour>.Create(graph);
var b = p.GetBehaviour();
b.featureName = featureName;
return p;
}
}
public class FeaturePlayableBehaviour : PlayableBehaviour
{
public string featureName;
// 当进入区域内触发Play
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
Debug.Log($"触发 OnBehaviourPlay ,name: {featureName}");
}
// 当出了区域触发,或者开始的时候触发,或者停止运行的时候
public override void OnBehaviourPause(Playable playable, FrameData info)
{
Debug.Log($"触发 OnBehaviourPause , name: {featureName}");
}
在区域内每个Frame地方都会触发
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
var p = (float)(playable.GetTime() / playable.GetDuration());
//Debug.Log($"{featureName} - {p.ToString("F2")} , time:{playable.GetTime()} ,dur:{playable.GetDuration()}");
}
}
我们这时候可以在轨道上点击鼠标右键,就只能看到只有这个对象了。
OnBehaviourPlay进入clip就触发。
OnBehaviourPause出了clip就触发。
当然Pause在运行时候会触发一次。
当前滑块可以用playable.GetTime() / playable.GetDuration()的方式获得到百分比。
这种就是OnCreateClip的用法。
下面我们来看CreateTrackMixer的用法
CreateTrackMixer的用法
因为目前我没有用到需要Mixer的算法,可能认识上有点偏差,我的理解是Mixer就是如图
交叉的这部分,如果某个值需要混合可以按照自己的需求去计算数值,权重可以这样获取:
csharp
float inputWeight = playable.GetInputWeight(i);
我们先放上代码,然后再做对比。
首先是轨道
csharp
//FeatureMixerTrack.cs
using System.ComponentModel;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
[DisplayName("(FeatureMixer)开关轨道")]
[TrackClipType(typeof(FeatureMixerPlayableAsset))]
[TrackColor(0.53f, 0.0f, 0.08f)]//表示在编辑器中,Track轨道前端的标识颜色(不重要啦)
public class FeatureMixerTrack : TrackAsset
{
public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
{
var temp = ScriptPlayable<FeatureMixerPlayableBehaviour>.Create(graph, inputCount);
temp.GetBehaviour().fixedData = "传入的数据";
return temp;
}
}
这里调用了CreateTrackMixer的方法。
csharp
//FeatureMixerPlayableAsset.cs
using System;
using System.ComponentModel;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
[DisplayName("(FeatureMixer)开关Clip")]
[Serializable]
public class FeatureMixerPlayableAsset : PlayableAsset
{
public FeatureBehaviour template = new FeatureBehaviour();
public ClipCaps clipCaps
{
get
{
return ClipCaps.Blending;
}
}
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
return ScriptPlayable<FeatureBehaviour>.Create(graph, template);
}
}
[Serializable]
public class FeatureBehaviour : PlayableBehaviour
{
public string featureName; //开启某个feature
public Material material; //对应的材质球
public AnimationCurve alphaCurve; //修改某个参数
[HideInInspector]
public float passtime; //计算当前块的播放进度
[HideInInspector]
public bool started; //是否刚进入
}
public class FeatureMixerPlayableBehaviour : PlayableBehaviour
{
public string fixedData;
// 当进入区域内触发Play
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
Debug.Log("触发 OnBehaviourPlay");
}
// 当出了区域触发,或者开始的时候触发,或者停止运行的时候
public override void OnBehaviourPause(Playable playable, FrameData info)
{
int inputCount = playable.GetInputCount();
for (int i = 0; i < inputCount; i++)
{
ScriptPlayable<FeatureBehaviour> inputPlayable = (ScriptPlayable<FeatureBehaviour>)playable.GetInput(i);
FeatureBehaviour input = inputPlayable.GetBehaviour();
if (input.started)
{
input.started = false;
Debug.Log($"{input.featureName}出去了!!");
}
}
}
// 在区域内每个Frame地方都会触发
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
int inputCount = playable.GetInputCount();
for (int i = 0; i < inputCount; i++)
{
ScriptPlayable<FeatureBehaviour> inputPlayable = (ScriptPlayable<FeatureBehaviour>)playable.GetInput(i);
FeatureBehaviour input = inputPlayable.GetBehaviour();
float inputWeight = playable.GetInputWeight(i);
if (!Mathf.Approximately(inputWeight, 0f))
{
input.passtime += info.deltaTime;
// maxTime 当前clip的时长
float maxTime = (float)PlayableExtensions.GetDuration(playable.GetInput(i));
// curTime 当前clip的执行到哪个时刻
float nowValue = input.alphaCurve.Evaluate(input.passtime / maxTime);
//Debug.Log("" + input.featureName + "," + (input.passtime / maxTime) + ",nowValue:" + nowValue);
//更新材质的变量
//input.material.SetFloat("xxxx", nowValue);
if (!input.started)
{
input.started = true;
Debug.Log($"{input.featureName} 进入了");
}
}
else
{
input.passtime = 0f;
if (input.started)
{
input.started = false;
Debug.Log($"{input.featureName} 出去了");
}
}
}
}
}
Mixer轨道在使用中发现和普通的轨道有区别:
函数OnBehaviourPlay和OnBehaviourPause失效
OnBehaviourPlay函数在TimeLine播放就执行了,并不会在clip进入才播放
OnBehaviourPause函数在整个TL播放结束才会进入。所以这两个函数失效了。
解决办法
我是这样解决的:
所以这里如果有进入和退出clip的时候要处理逻辑的,我研究可下加了两个变量来处理
started : 表示开始播放了
passtime :表示过去了多久
如果是Frame里的最后一帧刚好可能执行不到关闭,所以再OnBehaviourPause里也添加了结束.
具体就不描述了,参考代码中的ProcessFrame函数。
那么我们如果要处理每个clip,需要通过遍历来处理:
csharp
int inputCount = playable.GetInputCount();
for (int i = 0; i < inputCount; i++)
获取当前的有几个inputCount也是clip,然后遍历进行访问计算。
本文写道这里就结束,其实很多地方还没搞懂,例如ClipCaps设置为Blending后这个如何自定使用,后面有时间再研究吧。