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

相关推荐
老朱佩琪!18 小时前
Unity桥接模式
unity·设计模式·c#·桥接模式
陈言必行18 小时前
Unity 之 物理引擎中三种刚体力施加方式详解
unity·游戏引擎
foreveryao12319 小时前
Unity渲染流程(底层逻辑)
unity·游戏引擎·图形渲染
small-pudding19 小时前
Unity中的PBR(基于物理的渲染)
unity·游戏引擎
CreasyChan20 小时前
3D游戏数学基础指南
游戏·3d·unity·数学基础
平行云2 天前
Enscape × Paraverse | 从设计到一键发布、网页分享、实时交互的全新体验
unity·ue5·xr·3dsmax·webgl·实时云渲染·云桌面
老朱佩琪!2 天前
Unity迭代器模式
unity·设计模式·迭代器模式
程序猿多布2 天前
Unity 多语言系统实现
unity·多语言
CreasyChan2 天前
Unity中C#状态模式详解
unity·c#·状态模式
鹿野素材屋2 天前
动作游戏网游:帧同步下的动画同步
unity·游戏引擎