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

相关推荐
死也不注释5 小时前
【Unity UGUI 交互组件——Dropdown(TMP版本)(10)】
java·unity·交互
SmalBox6 小时前
【光照】[光照模型]是什么?以UnityURP为例
unity·渲染
死也不注释19 小时前
【Unity UGUI 交互组件——Scrollbar(8)】
unity·游戏引擎·交互
九章云极AladdinEdu1 天前
绿色算力技术栈:AI集群功耗建模与动态调频系统
人工智能·pytorch·深度学习·unity·游戏引擎·transformer·gpu算力
伽蓝_游戏1 天前
UGUI源码剖析(15):Slider的运行时逻辑与编辑器实现
游戏·ui·unity·性能优化·c#·游戏引擎·.net
lrh30252 天前
Custom SRP - Complex Maps
unity·srp·render pipeline
m0_497214152 天前
unity中通过拖拽,自定义scroll view中子物体顺序
unity·游戏引擎
地狱为王2 天前
在Unity中实现DTLN-AEC处理音频文件的功能
unity·aec·降噪
SmalBox2 天前
【URP】Shader绘制棋盘格对比内置管线
unity·渲染
EQ-雪梨蛋花汤3 天前
【Unity笔记】Unity 编辑器扩展:打造一个可切换 Config.assets 的顶部菜单插件
unity·编辑器·游戏引擎