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

相关推荐
_oP_i31 分钟前
Web 与 Unity 之间的交互
前端·unity·交互
Dr.long4 小时前
unity老猿随笔
unity·游戏引擎
许许前进5 小时前
问题本记录(2):Unity+机器学习
unity·游戏引擎
_乐无16 小时前
Unity 性能优化方案
unity·性能优化·游戏引擎
明明明h19 小时前
Unity Assembly Definition & Assembly Definition Reference
unity·游戏引擎
龙中舞王19 小时前
Unity学习笔记(4):人物和基本组件
笔记·学习·unity
无敌最俊朗@1 天前
unity3d————协程原理讲解
开发语言·学习·unity·c#·游戏引擎
夜色。1 天前
Unity6 + Android Studio 开发环境搭建【备忘】
android·unity·android studio
咩咩觉主1 天前
尽量通俗易懂地概述.Net && U nity跨语言/跨平台相关知识
unity·c#·.net·.netcore