c# -使用反射复制UnityEvent信息



我需要将事件从一个UnityEvent复制到另一个UnityEvent,因为一旦我弄清楚了这一点,我将在运行时将目标切换到另一个对象,到目前为止我所拥有的是:

MethodInfo info = UnityEventBase.GetValidMethodInfo (event1.GetPersistentTarget (i), event1.GetPersistentMethodName (i), Type.EmptyTypes);
UnityAction action = Delegate.CreateDelegate (typeof (UnityAction), info) as UnityAction;
event2.AddListener (action);

我得到ArgumentNullException: Argument cannot be null.,如果我把Type.EmptyTypes变成new Type[] { typeof (float) },我得到ArgumentException: method argument length mismatch

问题是,我不知道在那里放什么,因为我不知道类型是什么(因为Unity Events可以发送bool, float等)

Unity文档没有涉及到这一点,所以希望其他人在过去已经取得了成功。

这可以通过反射和递归复制UnityEvent类的每个值字段来实现。由于性能影响,我不会在运行时使用它,但对于编辑器来说,它非常有用。我使用一个静态助手类和一个扩展来克隆列表。

ReflectionHelper.cs

using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
namespace CEUtilities.Helpers
{
    public static class ReflectionHelper
    {
        /// <summary>
        /// Gets all fields from an object and its hierarchy inheritance.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="flags">The flags.</param>
        /// <returns>All fields of the type.</returns>
        public static List<FieldInfo> GetAllFields(this Type type, BindingFlags flags)
        {
            // Early exit if Object type
            if (type == typeof(System.Object))
            {
                return new List<FieldInfo>();
            }
            // Recursive call
            var fields = type.BaseType.GetAllFields(flags);
            fields.AddRange(type.GetFields(flags | BindingFlags.DeclaredOnly));
            return fields;
        }
        /// <summary>
        /// Perform a deep copy of the class.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">The object.</param>
        /// <returns>A deep copy of obj.</returns>
        /// <exception cref="System.ArgumentNullException">Object cannot be null</exception>
        public static T DeepCopy<T>(T obj)
        {
            if (obj == null)
            {
                throw new ArgumentNullException("Object cannot be null");
            }
            return (T)DoCopy(obj);
        }

        /// <summary>
        /// Does the copy.
        /// </summary>
        /// <param name="obj">The object.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentException">Unknown type</exception>
        private static object DoCopy(object obj)
        {
            if (obj == null)
            {
                return null;
            }
            // Value type
            var type = obj.GetType();
            if (type.IsValueType || type == typeof(string))
            {
                return obj;
            }
            // Array
            else if (type.IsArray)
            {
                Type elementType = type.GetElementType();
                var array = obj as Array;
                Array copied = Array.CreateInstance(elementType, array.Length);
                for (int i = 0; i < array.Length; i++)
                {
                    copied.SetValue(DoCopy(array.GetValue(i)), i);
                }
                return Convert.ChangeType(copied, obj.GetType());
            }
            // Unity Object
            else if (typeof(UnityEngine.Object).IsAssignableFrom(type))
            {
                return obj;
            }
            // Class -> Recursion
            else if (type.IsClass)
            {
                var copy = Activator.CreateInstance(obj.GetType());
                var fields = type.GetAllFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                foreach (FieldInfo field in fields)
                {
                    var fieldValue = field.GetValue(obj);
                    if (fieldValue != null)
                    {
                        field.SetValue(copy, DoCopy(fieldValue));
                    }
                }
                return copy;
            }
            // Fallback
            else
            {
                throw new ArgumentException("Unknown type");
            }
        }
    }
}

UnityEventExtension.cs

using UnityEngine.Events;
using CEUtilities.Helpers;
namespace UnityEngine
{
    public static class UnityEventExtension
    {
        /// <summary>
        /// Clones the specified unity event list.
        /// </summary>
        /// <param name="ev">The unity event.</param>
        /// <returns>Cloned UnityEvent</returns>
        public static T Clone<T>(this T ev) where T : UnityEventBase
        {
            return ReflectionHelper.DeepCopy(ev);
        }
    }
}

然后可以这样使用

this.OnStart = target.OnStart.Clone();

我知道这是旧的,但我花了大部分时间在网上搜索,以帮助我写一些东西。最后我得到了这个简单的函数。这只会在编辑器中起作用(但话又说回来,你什么时候会想要这样做呢?)。

只要这是一个UnityEvent,它就会将unity事件及其参数值(对象,字符串,整数,浮点数,void和bool)从一个目标UnityEvent复制到另一个不同或相同组件上的UnityEvent。

如果你愿意,欢迎每个人下载它并在此基础上构建。点击这里查看:https://gist.github.com/wesleywh/1c56d880c0289371ea2dc47661a0cdaf

对于将来偶然发现这个的人来说,这是有效的:

MethodInfo info = UnityEventBase.GetValidMethodInfo (event1.GetPersistentTarget (i), event1.GetPersistentMethodName (i), new Type[] { typeof (float) });
            UnityAction execute = () => info.Invoke (event1.GetPersistentTarget (i), new object[] { 180f });
            event2.AddListener (execute);

它只是没有在检查器中暴露复制的侦听器,所以仍在寻找一个完美的解决方案。

相关内容

  • 没有找到相关文章

最新更新