游戏开发者联盟

[pinvoke]Unity动态加载Native插件

用c写的dll,Unity在加载以后,永不卸载,官方说要想卸载只能重启unity。改个bug、加两行代码,需要重启Unity等几分钟,不能接受。

最后找到了一篇文章,介绍了如何动态加载、卸载。如下:

Update C++ Without Restarting the Editor

我在这篇文章的基础上,增加了自动初始化delegate的功能,减少一些重复代码。

其次,在开发时我没有把dll复制到Plugin目录,而是在在代码中直接指向vs编译出来的路径。

此时,调用Native插件就跟普通windows程序调用普通dll一样,可以方便的重新编译、加载。

delegate自动初始化

public class Ezg
{
        //delegate有固定的开头和结束标志,初始化时,根据这些特征,只初始化这些字段。
        public delegate void ezg_SetLogCallback_Dg(IntPtr cb);
        public static ezg_SetLogCallback_Dg ezg_SetLogCallback;

        static Ezg()
        {
            LoadDll();
        }

        public static Dg GetDllFunc<Dg>() where Dg : class
        {
            string name = typeof(Dg).Name;
            name = name.Substring(0, name.LastIndexOf("_Dg"));
            return DllLoader.GetDelegate<Dg>(libraryHandle, name);
        }


        static void LoadDll()
        {
            var type = typeof(Ezg);
            var fields = type.GetFields();
            foreach (var one in fields)
            {
                var fieldType = one.FieldType;
                if (fieldType.Name.StartsWith("ezg_") && fieldType.Name.EndsWith("Dg"))
                {
                    var dllfunc = type.GetMethod("GetDllFunc").MakeGenericMethod(fieldType).Invoke(null, new object[] { });
                    one.SetValue(null, dllfunc);
                }
            }
        }
}

关键点

2020.7.10更新

  1. 放入Plugins目录中的dll不能重新加载
    放入unity Plugins中以后,unity会自动加载它、并锁定,导致在unity重启之前无法更新。
  2. 如果dll代码中启动了线程,也会阻止dll卸载。
    想办法在卸载之前停止它所启动的线程。这个花了我不少时间才意识到的。

第三方插件

2020.5.10更新
这两种插件,工作都不是很稳定。如果时不时的不能编译cpp dll会让人心情烦躁。
两个动态更新插件


合理流程

2020.7.14

  1. dll放在unity工程外
  2. 用LoadLibrary等API加载、卸载。
  3. 实时用GetProcAddress获取函数地址,转成c#delegate进行调用,不要缓存这个delegate,这样出错的时候会有个c#异常,而不是直接崩了。
  4. 卸载之前,要合理清理完所有native对象,并关闭dll中启动的线程。需要dll提供一个类似close的函数。
  5. 卸载之后不要在调用dll 中的函数,会报错。

这个流程在开发工程中很流畅。编译加载本地代码要比编译c#快,因为unity本身不需要对本地库做任何解析。

注意:动态加载的机制只应用在开发中,正式代码还要用dllimport,用条件编译语句区分两种模式就好。利用partial class机制,把他们放在不同的文件。