注:软件版本Unity 6.0 + Timeline 1.8.7
作者:CSDN @ RingleaderWang
原文:《Unity第25期——Timeline结构及其源码浅析》
文章首发Github👍:《Timeline结构及其源码浅析》
Bilibili 视频版👍👍
:《Timeline结构及其源码解析》https://www.bilibili.com/video/BV1bHjYzNE35
Behaviour 与 PlayableAsset的数据交互
我们在自定义Track 自定义PlayableAsset时,可能有这么个需求,Track或PlayableAsset自身需要拥有特殊属性,而且能在后面的生命周期中获取这个特殊属性。
比如我的技能系统用的SkillPlayableAsset,有个参数标注这个clip用于技能的哪个阶段,就需要SkiIlPhase这么一个参数,后面behaviour回调触发时就能根据这个参数针对性做处理。
这就是behaviour 与 playableAsset的数据交互问题。
如果你是官方设定的基础Playable,你可以在Playable上设置这么个特殊属性,create的时候都塞进去,生命周期回调时也能正常获取。但你自定义track,用的都是ScriptPlayable,没有额外参数,怎么办?那就是用PlayableBehaviour传参!
传参有两种方式,这同时也涉及到ScriptPlayable createPlayable的两种方式。
// T : class, IPlayableBehaviour, new()
public static ScriptPlayable<T> Create(PlayableGraph graph, int inputCount = 0)
{
return new ScriptPlayable<T>(ScriptPlayable<T>.CreateHandle(graph, default (T), inputCount));
}
public static ScriptPlayable<T> Create(PlayableGraph graph, T template, int inputCount = 0)
{
return new ScriptPlayable<T>(ScriptPlayable<T>.CreateHandle(graph, template, inputCount));
}
带 new T() 的 ScriptPlayable 创建
如上ScriptPlayable 创建Playable时,可以带T template 参数,T就是实现了IPlayableBehaviour且有无参构造函数的类。
这样自定track或playableAsset在创建playable时就可以将含有特殊属性的T传进来,behaviour回调时就能获取到你设的特殊属性。
// 存在 XXPlayableBehaviour,内含param1、param2两个参数
public class XXPlayableAsset : PlayableAsset
{
public XXPlayableBehaviour template = new XXPlayableBehaviour();
public override Playable CreatePlayable (PlayableGraph graph, GameObject owner){
var playable = ScriptPlayable<XXPlayableBehaviour>.Create(graph,template);
return playable;
}
}
XXPlayableAsset Inspector面板展示:
template
- param1
- param2
这样你在XXPlayableAsset Inspector就能展示XXPlayableBehaviour的两个参数,param1和param2,但不够好看,外面会套一层参数名,像下面这样:
解决方法有两个,一种是类似AudioTrack的处理方法,写个AudioTrackInspector手动提取出各个参数。另一个参考官方案例的自定义Attribute:NoFoldOut。
// Custom property drawer that draws all child properties inline
[CustomPropertyDrawer(typeof(NoFoldOutAttribute))]
public class NoFoldOutPropertyDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
if (!property.hasChildren)
return base.GetPropertyHeight(property, label);
property.isExpanded = true;
return EditorGUI.GetPropertyHeight(property, label, true) -
EditorGUI.GetPropertyHeight(property, label, false);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (!property.hasChildren)
EditorGUI.PropertyField(position, property, label);
else
{
SerializedProperty iter = property.Copy();
var nextSibling = property.Copy();
nextSibling.Next(false);
property.Next(true);
do
{
// We need to check against nextSibling to properly stop
// otherwise we will draw properties that are not child of this
// foldout.
if (SerializedProperty.EqualContents(property, nextSibling))
break;
float height = EditorGUI.GetPropertyHeight(property, property.hasVisibleChildren);
position.height = height;
EditorGUI.PropertyField(position, property, property.hasVisibleChildren);
position.y = position.y + height;
}
while (property.NextVisible(false));
}
}
}
这样使用时给参数加[NoFoldOut]
特性就能直接展示子属性了。
// 存在 XXPlayableBehaviour,内含param1、param2两个参数
public class XXPlayableAsset : PlayableAsset
{
[NoFoldOut]
public XXPlayableBehaviour template = new XXPlayableBehaviour();
public override Playable CreatePlayable (PlayableGraph graph, GameObject owner){
var playable = ScriptPlayable<XXPlayableBehaviour>.Create(graph,template);
return playable;
}
}
使用 default (T) 的 ScriptPlayable 创建
如果你使用 default (T) 创建,意味着你的这些特殊属性都变成默认值。
不过你也可以手动操作改变,如下代码所示,create完ScriptPlayable后,再手动赋值。
// 存在 XXPlayableBehaviour,内含param1、param2两个参数
public class XXPlayableAsset : PlayableAsset
{
public override Playable CreatePlayable (PlayableGraph graph, GameObject owner)
{
var playable = ScriptPlayable<XXPlayableBehaviour>.Create(graph);
var xxPlayableBehaviour = playable.GetBehaviour();
xxPlayableBehaviour.param1 = value1;
xxPlayableBehaviour.param2 = value2;
return playable;
}
}
不过这种在Asset还要再申明一遍param,冗杂,所以推荐用第一种。
ExposedReference用法
因为Asset不能引用scene场景中的对象,但如果你非要引用也不是不可以,Timeline提供了ExposedReference
方法让你能在Behaviour回调中获取场景中的对象。
public class XXPlayableBehaviour : PlayableBehaviour
{
public ExposedReference<Transform> exposedTransform;
public override void OnGraphStart(Playable playable){
Transform transform = exposedTransform.Resolve(playable.GetGraph().GetResolver());
Debug.Log("exposedTransform.Resolve(playable.GetGraph().GetResolver()):"+transform.position);
Debug.Log("gameobject name:" + transform.gameObject.name);
}
}
public class XXPlayableAsset : PlayableAsset
{
public XXPlayableBehaviour template = new XXPlayableBehaviour();
public override Playable CreatePlayable (PlayableGraph graph, GameObject owner){
var playable = ScriptPlayable<XXPlayableBehaviour>.Create(graph,template);
return playable;
}
}
注意使用时,一定要从点击挂载PlayableDirector的对象对应的timeline窗口操作。
运行时可以正常打印,但发现打印的对象position数据有问题,似乎有(0.22, 1.28, -1.66)的偏差,不清楚原因,暂时还是不要用ExposedReference
。