问题
由于预制体和Playable资源分开存放, 当需要拷贝一份新资源重新编辑时, 新的预制体绑定的Playable仍是旧资源, 且手动替换到新资源后, PlayableDirector上的Bindings信息会丢失
-
拷贝后, 新预制体的Playable仍引用旧资源
-
手动替换Playable资源后, Bindings丢失
目标
- 使拷贝后的预制体, Playable引用新资源
- PlayableDirector的Bindings信息不丢失
代码
/// <summary>
/// 拷贝资源
/// </summary>
/// <param name="originPath"></param>
/// <param name="copyToPath"></param>
public static async void CopyAsset(string originPath, string copyToPath)
{
if (!AssetDatabase.CopyAsset(originPath, copyToPath))
{
Debug.LogError("拷贝失败");
return;
}
var originPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(originPath);
if (originPrefab.TryGetComponent<PlayableDirector>(out var originDirector))
{
var newPlayablePath = GetPlayableAssetPath(copyToPath);
var originPlayablePath = AssetDatabase.GetAssetPath(originDirector.playableAsset);
if (!AssetDatabase.CopyAsset(originPlayablePath, newPlayablePath))
{
Debug.LogError("拷贝失败");
return;
}
var copyPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(copyToPath);
var copyDirector = copyPrefab.GetComponent<PlayableDirector>();
// 新预制体绑定新Playable资源
copyDirector.playableAsset = AssetDatabase.LoadAssetAtPath<TimelineAsset>(newPlayablePath);
copyDirector.playableAsset.name = System.IO.Path.GetFileNameWithoutExtension(newPlayablePath);
// 恢复拷贝的Playable资源的bindings信息
var directorSO = new SerializedObject(copyDirector);
var sceneBindings = directorSO.FindProperty("m_SceneBindings");
// 等一帧加载
newDirector.Play();
await UniTask.NextFrame();
// 删除PlayableDirector.Bindgings的空Key项, 并把原Timeline文件的轨道Key替换到当前Timeline文件的轨道对象
var bindingsKv = new Dictionary<Object, Object>();
for (var i = 0; i < sceneBindings.arraySize; i++)
{
var bindingElement = sceneBindings.GetArrayElementAtIndex(i);
// key是源文件的轨道对象,value是当前文件的绑定value
var key = bindingElement.FindPropertyRelative("key").objectReferenceValue;
var val = bindingElement.FindPropertyRelative("value").objectReferenceValue;
if (key == null)
{
continue;
}
bindingsKv.Add(key, val);
}
// 移除无用的绑定
while (sceneBindings.arraySize > 0)
{
sceneBindings.DeleteArrayElementAtIndex(0);
}
directorSO.ApplyModifiedProperties();
// 设置有效绑定
foreach (var kv in bindingsKv)
{
copyDirector.SetGenericBinding(kv.Key, kv.Value);
}
// 设置有效绑定 (新老timeline资源outputs顺序完全一致)
var lstout = director.playableAsset.outputs.ToList();
var newout = newDirector.playableAsset.outputs.ToList();
for (int i = 0; i < lstout.Count; i++)
{
var lstTrack = lstout[i].sourceObject as TrackAsset;
var newTrack = newout[i].sourceObject as TrackAsset;
if (bindingsKv.ContainsKey(lstTrack))
{
newDirector.SetGenericBinding(newTrack, bindingsKv[lstTrack]);
}
}
AssetDatabase.SaveAssetIfDirty(copyPrefab);
}
}
/// <summary>
/// 获取PlayableAsset路径
/// Playable资源和Prefab资源在同一目录下
/// </summary>
/// <param name="savePath"></param>
/// <returns></returns>
private static string GetPlayableAssetPath(string savePath)
{
var prefabName = System.IO.Path.GetFileName(savePath);
var timelineName = System.IO.Path.GetFileNameWithoutExtension(prefabName) + ".playable";
var timelineSavePath = savePath.Replace(prefabName, "").Replace(Application.dataPath, "");
if (timelineSavePath.StartsWith("Assets"))
{
return timelineSavePath + timelineName;
}
return "Assets" + timelineSavePath + timelineName;
}
- 工具拷贝后, 引用正常
参考: How can i code to delete unused bindings in PlayableDirector?