Unity 克隆Timeline并保留引用

Timeline的资源是.playable文件,简单的复制不会保留引用关系。

下面的脚本可以复制引用关系。

csharp 复制代码
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using UnityEditor;

public class CopyTimeline : MonoBehaviour
{
    [MenuItem("JFrameWork/Resources/克隆 Timeline", true)]
    private static bool DupTimelineValidate()
    {
        if (!(Selection.activeObject is GameObject playableDirectorObj))
        {
            Debug.Log("Null active object");
            return false;
        }

        var playableDirector = playableDirectorObj.GetComponent<PlayableDirector>();
        if (playableDirector == null)
        {
            Debug.Log("Null playableDirector");
            return false;
        }

        var timelineAsset = playableDirector.playableAsset as TimelineAsset;
        if (timelineAsset == null)
        {
            Debug.Log("Null timelineAsset");
            return false;
        }

        var path = AssetDatabase.GetAssetPath(timelineAsset);
        if (string.IsNullOrEmpty(path))
        {
            Debug.Log("Null timeline asset path");
            return false;
        }

        return true;
    }

    [MenuItem("JFrameWork/Resources/克隆 Timeline")]
    public static void DupTimeline()
    {
        if (!(Selection.activeObject is GameObject playableDirectorObj))
        {
            Debug.LogError("Invalid selection. Please select a GameObject with a PlayableDirector.");
            return;
        }

        var playableDirector = playableDirectorObj.GetComponent<PlayableDirector>();
        if (playableDirector == null)
        {
            Debug.LogError("No PlayableDirector component found on the selected GameObject.");
            return;
        }

        var timelineAsset = playableDirector.playableAsset as TimelineAsset;
        if (timelineAsset == null)
        {
            Debug.LogError("Selected GameObject does not have a TimelineAsset.");
            return;
        }

        var path = AssetDatabase.GetAssetPath(timelineAsset);
        var newPath = path.Replace(".playable", "(Clone).playable");
        if (!AssetDatabase.CopyAsset(path, newPath))
        {
            Debug.LogError("Couldn't clone asset.");
            return;
        }

        // Copy Bindings
        var newTimelineAsset = AssetDatabase.LoadMainAssetAtPath(newPath) as TimelineAsset;
        if (newTimelineAsset == null)
        {
            Debug.LogError("Failed to load cloned timeline asset.");
            return;
        }

        var oldBindings = timelineAsset.outputs.ToArray();
        var newBindings = newTimelineAsset.outputs.ToArray();
        for (int i = 0; i < oldBindings.Length; i++)
        {
            playableDirector.playableAsset = timelineAsset;
            var boundTo = playableDirector.GetGenericBinding(oldBindings[i].sourceObject);

            playableDirector.playableAsset = newTimelineAsset;
            playableDirector.SetGenericBinding(newBindings[i].sourceObject, boundTo);
        }

        // Copy Exposed References
        playableDirector.playableAsset = newTimelineAsset;
        foreach (var newTrackAsset in newTimelineAsset.GetRootTracks())
        {
            foreach (var newClip in newTrackAsset.GetClips())
            {
                foreach (var fieldInfo in newClip.asset.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
                {
                    if (fieldInfo.FieldType.IsGenericType && fieldInfo.FieldType.GetGenericTypeDefinition() == typeof(ExposedReference<>))
                    {
                        var exposedReference = fieldInfo.GetValue(newClip.asset);
                        var oldExposedName = (PropertyName)fieldInfo.FieldType.GetField("exposedName").GetValue(exposedReference);

                        // Fetch Old Exposed Value
                        if (!playableDirector.GetReferenceValue(oldExposedName, out bool isValid))
                        {
                            Debug.LogError("Failed to copy exposed references. Could not find: " + oldExposedName);
                            return;
                        }

                        var oldExposedValue = playableDirector.GetReferenceValue(oldExposedName, out isValid);

                        // Replace exposedName on struct
                        var newExposedName = new PropertyName(GUID.Generate().ToString());
                        fieldInfo.FieldType.GetField("exposedName").SetValue(exposedReference, newExposedName);

                        // Set ExposedReference
                        fieldInfo.SetValue(newClip.asset, exposedReference);

                        // Set Reference on Playable Director
                        playableDirector.SetReferenceValue(newExposedName, oldExposedValue);
                    }
                }
            }
        }

        Debug.Log("Timeline duplicated successfully.");
    }
}

我们选中PlayableDirector对象,然后点击克隆Timeline菜单,就直接复制了。

然后我们可以把TL1复制一份,这样就2个Timeline都有引用关系了。

到这里就结束了。

引用https://discussions.unity.com/t/duplicating-a-timeline-loses-all-the-bindings-unity-v2017-2-0b6/674168/35

相关推荐
纯属个人爱好1 小时前
Unity2020+PicoNeo3Pro开发
unity·vr
__water5 小时前
RHK《Unity接入DeepSeek问答》
unity·游戏引擎·智能问答·deepseek接入·deepseekapikey
康de哥6 小时前
MCP Unity + Claude Code 配置关键步骤
unity·mcp·claude code
美团骑手阿豪6 小时前
Unity3D大规模点击检测:GPU Picking vs MeshCollider + Raycast
unity
在路上看风景6 小时前
1.4 Unity运行时路径
unity·游戏引擎
在路上看风景1 天前
1.2 Unity资源分类
unity·游戏引擎
one named slash1 天前
BMFont在Unity中生成艺术字
unity·游戏引擎
郝学胜-神的一滴1 天前
图形学中的纹理映射问题:摩尔纹与毛刺的深度解析
c++·程序人生·unity·游戏引擎·图形渲染·unreal engine
在路上看风景1 天前
10. CPU-GPU协作渲染
unity
程序员agions1 天前
Unity 游戏开发邪修秘籍:从入门到被策划追杀的艺术
unity·cocoa·lucene