白泽图

  • 文章
    • Unity渲染
    • Unity项目开发
    • 工具
    • 数学
    • 算法
    • 网站搭建
    • 网络&操作系统
蒋程个人博客
互联网技术经验总结&分享
  1. 首页
  2. Unity项目开发
  3. 正文

使用反向深度拷贝赋值unity对象

2025-08-25 18点热度 0人点赞 0条评论

当使用ScriptableObject.CreateInstance()复制一份UnityEngine.Object对象时,会获得了一份完全独立的内容,里面的内容都是默认的,如果此时需要将已有一份UnityEngine.Object对象的内容拷贝到新的UnityEngine.Object对象时,可以使用如下代码

public static class DeepCopyByReflection
    {
        /// <summary>
        /// 安全反射创建对象,(亲测:使用反射创建一个没有默认构造(无参)函数的类对象会导致unity闪退)
        /// </summary>
        /// <param name="classType">类的类型</param>
        /// <param name="instance">反射创建出的对象</param>
        /// <returns></returns>
        static bool TryCreateClassInstance(System.Type classType, out System.Object instance)
        {
            var ctors = classType.GetConstructors();
            var hasDefaultConstructor = classType.IsArray;
            if (!hasDefaultConstructor)
            {
                foreach (var ctor in ctors)
                {
                    if (ctor.GetParameters().Length == 0)
                    {
                        hasDefaultConstructor = true;
                        break;
                    }
                }
            }

            if (hasDefaultConstructor)
            {
                instance = System.Activator.CreateInstance(classType);
                return true;
            }
            instance = null;
            TBCDebug.LogError("classType:{0} has no default constructor! Please add it!", classType);
            return false;
        }


        /// <summary>
        /// 是否可以直接引用赋值(包括Unity序列化对象(比例:AnimationCurve、UnityEngine.Object)、值类型、字符串)
        /// </summary>
        /// <returns></returns>
        static bool CanRefAssignment(System.Type fieldType, object fromValue, out object toValue)
        {
            toValue = null;
            if (fieldType.IsClass)
            {
                if (typeof(UnityEngine.Object).IsAssignableFrom(fieldType))
                {
                    //如果是UnityEngine.Object,需要进行判空,否则会导致闪退
                    var go_Value = fromValue as UnityEngine.Object;
                    if (go_Value != null)
                        toValue = go_Value;
                    return true;
                }
                if (fieldType.Assembly.FullName.StartsWith("UnityEngine.")) //如果这个类是Unity引擎的序列化对象,比如AnimationCurve也得是引用关系,否则会导致闪退
                {
                    //直接引用
                    toValue = fromValue;
                    return true;
                }
            }
            if (fieldType.IsValueType || fieldType == typeof(string))
            {
                //值类型与字符串类型直接赋值
                toValue = fromValue;
                return true;
            }
            return false;
        }


        /// <summary>
        /// 深度拷贝两个对象中的所有成员值
        /// </summary>
        /// <param name="from">从form对象</param>
        /// <param name="to">拷贝到to对象</param>
        public static void Copy(System.Object fromObj, System.Object toObj)
        {
            if (fromObj == null || toObj == null)
                return;

            var type = fromObj.GetType();
            FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            foreach (var field in fields)
            {
                var fromValue = field.GetValue(fromObj);
                if (CanRefAssignment(field.FieldType, fromValue, out var toValue))
                {
                    field.SetValue(toObj, toValue);
                    continue;
                }

                if (!field.FieldType.IsClass)
                {
                    TBCDebug.LogError("未知类型:{0}", field.FieldType);
                    continue;
                }

                if (fromValue == null)
                {
                    field.SetValue(toObj, null);
                    continue;
                }

                //1.处理数组
                if (fromValue is System.Array fromArray)
                {
                    System.Array toArray = toValue as System.Array;
                    if (toArray == null)
                    {
                        //new 数组
                        toArray = System.Array.CreateInstance(field.FieldType, fromArray.Length);
                        field.SetValue(toObj, toValue);
                    }

                    var itemType = fromArray.GetType().GetElementType();
                    for (int i = 0; i < fromArray.Length; i++)
                    {
                        var itemFromValue = fromArray.GetValue(i);
                        if (CanRefAssignment(itemType, itemFromValue, out var itemToValue))
                        {
                            toArray.SetValue(itemToValue, i);
                            continue;
                        }
                        if (!itemType.IsClass)
                        {
                            TBCDebug.LogError("未知类型:{0}", itemType);
                            continue;
                        }
                        if (TryCreateClassInstance(itemType, out var newItemValue))
                        {
                            Copy(itemFromValue, newItemValue);
                            toArray.SetValue(newItemValue, i);
                        }
                    }
                    continue;
                }

                //2.处理List与类
                if (toValue == null)
                {
                    if (TryCreateClassInstance(field.FieldType, out toValue))
                        field.SetValue(toObj, toValue);
                    else
                        continue;
                }

                //2.1 处理List
                if (fromValue is IList fromList)
                {
                    var toList = toValue as IList;
                    toList.Clear();
                    foreach (var itemFromValue in fromList)
                    {
                        var itemType = itemFromValue.GetType();
                        if (CanRefAssignment(itemType, itemFromValue, out var itemToValue))
                        {
                            toList.Add(itemToValue);
                            continue;
                        }
                        if (!itemType.IsClass)
                        {
                            TBCDebug.LogError("未知类型:{0}", itemType);
                            continue;
                        }
                        if (TryCreateClassInstance(itemType, out var newItemValue))
                        {
                            Copy(itemFromValue, newItemValue);
                            toList.Add(newItemValue);
                        }
                    }
                    continue;
                }
                //2.2.处理常规类
                Copy(fromValue, toValue);
            }
        }
    }

下面总结使用反射赋值导致Unity闪退的几个点

1.UnityEngine.Object 重载了==,判断是否为null,实际是指它对象的资源是否为空,在反射时由于会转化成System.Object,所以空的UnityEgnine.Object对象并不为空,直接赋值会导致Untiy闪退,解决方法是 先转成UnityEngine.Object,然后判空,如果是空,直接设置null

2.Unity除了UnityEngine.Object类型资源外,其他可序列化的数据,比如AnimationCurve数据,它内部有指针指向C++内存,如果new出来,反射赋值里面的指针,会导致Unity闪退,原因如下

这里的创建资源,内容保存在C++,C#只是持有指针,但是在析构时,会触发资源销毁,而且原来的Unity的C#对象还是持有该指针,所以会导致Unity闪退

标签: 暂无
最后更新:2025-08-25

蒋程

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

您需要 登录 之后才可以评论

COPYRIGHT © 2023 白泽图. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

登录
注册|忘记密码?