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

相关推荐
benben0442 小时前
Unity3d动画插件DoTween使用指南
游戏·unity
_时侍4 小时前
【unity游戏开发】彻底理解AnimatorStateInfo,获取真实动画长度
服务器·unity·哈希算法
Unity官方开发者社区17 小时前
Unity 如何在 iOS 新增键盘 KeyCode 响应事件
unity·ios·计算机外设
向宇it1 天前
【unity进阶知识10】从零手搓一个UI管理器/UI框架,自带一个提示界面,还有自带DOTween动画效果
ui·unity·游戏引擎
杳戢1 天前
技术美术百人计划 | 《5.4 水体渲染》笔记
人工智能·笔记·深度学习·算法·unity·c#·技术美术
charon87781 天前
Unity Shader Graph基础包200+节点及术语解释
unity·游戏引擎
程序员正茂2 天前
Unity2017在安卓下获取GPS位置时闪退的解决办法
android·unity·gps·定位·闪退
向宇it2 天前
【unity进阶知识9】序列化字典,场景,vector,color,Quaternion
开发语言·unity·游戏引擎
一丝晨光2 天前
Web技术简史、前后端分离、游戏
前端·javascript·css·游戏·unity·前后端分离·cocos