用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更新
- 放入Plugins目录中的dll不能重新加载
放入unity Plugins中以后,unity会自动加载它、并锁定,导致在unity重启之前无法更新。 - 如果dll代码中启动了线程,也会阻止dll卸载。
想办法在卸载之前停止它所启动的线程。这个花了我不少时间才意识到的。
第三方插件
2020.5.10更新
这两种插件,工作都不是很稳定。如果时不时的不能编译cpp dll会让人心情烦躁。
两个动态更新插件
https://github.com/mcpiroman/UnityNativeTool
https://github.com/forrestthewoods/fts_unity_native_plugin_reloader
合理流程
2020.7.14
- dll放在unity工程外
- 用LoadLibrary等API加载、卸载。
- 实时用GetProcAddress获取函数地址,转成c#delegate进行调用,不要缓存这个delegate,这样出错的时候会有个c#异常,而不是直接崩了。
- 卸载之前,要合理清理完所有native对象,并关闭dll中启动的线程。需要dll提供一个类似close的函数。
- 卸载之后不要在调用dll 中的函数,会报错。
这个流程在开发工程中很流畅。编译加载本地代码要比编译c#快,因为unity本身不需要对本地库做任何解析。
注意:动态加载的机制只应用在开发中,正式代码还要用dllimport,用条件编译语句区分两种模式就好。利用partial class机制,把他们放在不同的文件。