Timeline的timelineAsset文件只会保留轨道的编辑信息,而不会保存轨道的绑定列表,这些绑定的信息是和场景一同绑定的。
如果我们在制作的过程中没有保存场景,或者在协作的过程中只传送了timelineAsset而没有传送场景信息,这些绑定信息就会丢失,就像如下这种情况:
我们可以通过创建Asset文件来保存和恢复绑定信息,具体思路是创建Asset来存储每个轨道绑定物体的名称,通过实现函数将名称信息保存到Asset文件里,在恢复的时候再通过读取Asset文件,根据其中存取的名称来恢复轨道的绑定。
Asset的创建:
这一步非常简单,就是定义一个类,继承自ScriptableObject即可,当然,我们可以为这个类添加CreateMenu,以便于直接创建,但是这里我们没有太大的必要。
Bindings信息存储:
List bindingnames;
PlayableDirector playableDirector = GameObject.FindGameObjectWithTag("director").GetComponent();
bindingnames = new List();
//获取timelineAsset中所有轨道的迭代器
var bindings = playableDirector.playableAsset.outputs.GetEnumerator();
//复制轨道中的对象名称
while (bindings.MoveNext())
{
//获取当前轨道的绑定键
Object sourceObject = bindings.Current.sourceObject;
string bindingname;
//根据绑定键获取当前轨道绑定物体的名称
if (playableDirector.GetGenericBinding(sourceObject))
{
bindingname = playableDirector.GetGenericBinding(sourceObject).name;
}
else
bindingname = "";
bindingnames.Add(bindingname);
}
bindings.Current.sourceObject充当绑定键的作用,timeline通过游戏对象和绑定键来绑定Binding信息,该绑定键由timelineAsset中的当前轨道来确定。通过获取每一个轨道的绑定键,再通过绑定键来获得绑定物体的名称,这些物体的名称就是我们要存取的信息。之后我们创建BindingAsset文件,根据这些信息来初始化文件:
//实例化类
BindingAsset bindingAsset = ScriptableObject.CreateInstance();
//赋初值
bindingAsset.playableName = new string[bindingnames.Count];
for (int i = 0; i < bindingnames.Count; i++)
{
bindingAsset.playableName[i] = bindingnames[i];
}
Bindings信息恢复:
PlayableDirector playableDirector = GameObject.FindGameObjectWithTag("director").GetComponent();
string path = string.Format("Assets/BindingAssets/{0}.asset", playableDirector.playableAsset.name);
if (!File.Exists(path))
return;
BindingAsset bindingAsset = AssetDatabase.LoadAssetAtPath(path);
//获取timelineAsset中所有轨道的迭代器
var bindings = playableDirector.playableAsset.outputs.GetEnumerator();
int i = 0;
//根据对象名称恢复timeline的轨道
while (bindings.MoveNext())
{
//获取当前轨道的绑定键
Object sourceObject = bindings.Current.sourceObject;
//通过名称找到需要绑定的物体
GameObject gameObject = FindObject2(bindingAsset.playableName[i]);
//通过绑定键和绑定物体的键值对,绑定Binding信息
playableDirector.SetGenericBinding(sourceObject,
gameObject);
i++;
}
即先根据名称找到要绑定的物体,然后用playableDirector.SetGenericBinding方法,将键值对进行绑定。其中,寻找物体的方法如下:
private static GameObject FindObject2(string name)
{
foreach(GameObject go in Resources.FindObjectsOfTypeAll(typeof(GameObject)))
{
if(!EditorUtility.IsPersistent(go.transform.root.gameObject) && !(go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave))
{
if (go.name == name)
return go;
}
}
return null;
}
关于这种寻找方法,可以参照我的另一篇博客:https://blog.csdn.net/weixin_43347688/article/details/107322746
知道了怎么存储和恢复,我们还要能够把这些信息保存起来,这个不用多解释了,代码如下
//自定义资源保存路径
string path = Application.dataPath + "/BindingAssets";
//如果项目不包含该路径,创建一个
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
path = string.Format("Assets/BindingAssets/{0}.asset", playableDirector.playableAsset.name);
//生成自定义资源到指定路径
AssetDatabase.CreateAsset(bindingAsset, path);
最后,贴一下完整的代码:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using System.IO;
using UnityEditor;
public class BindingOperator : MonoBehaviour
{
///
/// 将当前timeline中的对象名称存储到asset中
///
[MenuItem("TimelineTools/存储timeline绑定 %_i")]
private static void Fork()
{
List bindingnames;
PlayableDirector playableDirector = GameObject.FindGameObjectWithTag("director").GetComponent();
bindingnames = new List();
//获取timelineAsset中所有轨道的迭代器
var bindings = playableDirector.playableAsset.outputs.GetEnumerator();
//复制轨道中的对象名称
while (bindings.MoveNext())
{
//获取当前轨道的绑定键
Object sourceObject = bindings.Current.sourceObject;
string bindingname;
//根据绑定键获取当前轨道绑定物体的名称
if (playableDirector.GetGenericBinding(sourceObject))
{
bindingname = playableDirector.GetGenericBinding(sourceObject).name;
}
else
bindingname = "";
bindingnames.Add(bindingname);
}
string path0 = string.Format("Assets/BindingAssets/{0}.asset", playableDirector.playableAsset.name);
//如果已经存在有binding的绑定,则决定是否替换或者保留
if (File.Exists(path0))
{
if (EditorUtility.DisplayDialog("注意", "替换原来的binding?", "是", "否"))
{
File.Delete(path0);
}
else
return;
}
//实例化类
BindingAsset bindingAsset = ScriptableObject.CreateInstance();
//赋初值
bindingAsset.playableName = new string[bindingnames.Count];
for (int i = 0; i < bindingnames.Count; i++)
{
bindingAsset.playableName[i] = bindingnames[i];
}
//如果实例化为空,返回
if (!bindingAsset)
{
Debug.LogWarning("bindingAsset not found");
return;
}
//自定义资源保存路径
string path = Application.dataPath + "/BindingAssets";
//如果项目不包含该路径,创建一个
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
path = string.Format("Assets/BindingAssets/{0}.asset", playableDirector.playableAsset.name);
//生成自定义资源到指定路径
AssetDatabase.CreateAsset(bindingAsset, path);
}
///
/// 利用asset中存储的名称恢复timeline的对象
///
///
[MenuItem("TimelineTools/恢复timeline绑定 %_k")]
private static void Restore()
{
PlayableDirector playableDirector = GameObject.FindGameObjectWithTag("director").GetComponent();
string path = string.Format("Assets/BindingAssets/{0}.asset", playableDirector.playableAsset.name);
if (!File.Exists(path))
return;
BindingAsset bindingAsset = AssetDatabase.LoadAssetAtPath(path);
//获取timelineAsset中所有轨道的迭代器
var bindings = playableDirector.playableAsset.outputs.GetEnumerator();
int i = 0;
//根据对象名称恢复timeline的轨道
while (bindings.MoveNext())
{
//获取当前轨道的绑定键
Object sourceObject = bindings.Current.sourceObject;
//通过名称找到需要绑定的物体
GameObject gameObject = FindObject2(bindingAsset.playableName[i]);
//通过绑定键和绑定物体的键值对,绑定Binding信息
playableDirector.SetGenericBinding(sourceObject,
gameObject);
i++;
}
}
//根据名称在hierarchy里找到物体,包括隐藏的物体
private static GameObject FindObject2(string name)
{
foreach(GameObject go in Resources.FindObjectsOfTypeAll(typeof(GameObject)))
{
if(!EditorUtility.IsPersistent(go.transform.root.gameObject) && !(go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave))
{
if (go.name == name)
return go;
}
}
return null;
}
}