Unity拷贝预制体和Playable资源,并恢复PlayableDirector丢失的Bindings数据

文章描述了在Unity中,如何在复制预制体时确保新的Prefab引用的新Playable资源,并保持PlayableDirector的Bindings信息不丢失。方法包括复制Playable资源、恢复Bindings数据并进行适配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

由于预制体和Playable资源分开存放, 当需要拷贝一份新资源重新编辑时, 新的预制体绑定的Playable仍是旧资源, 且手动替换到新资源后, PlayableDirector上的Bindings信息会丢失

  • 拷贝后, 新预制体的Playable仍引用旧资源
    拷贝后, 新预制体的Playable仍引用旧资源

  • 手动替换Playable资源后, Bindings丢失
    手动替换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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值