目标
Unity开发中可能会用到一些强大的第三方库,而基于跨平台的库多是使用C/C++开发的,为了方便在 Unity中使用,我们想将C库做成一个插件,暴露几个接口函数在Unity中使用C#直接调用。关键要做的是将源代码库编译成一个动态库,例如windows下的dll等,然后是实现C#与C的一个函数通信。
PC平台库
使用VS可以直接编译出dll库,即将已有的依赖的源码库的输出看做动态库,创建一个动态库工程引用这些动态库,编写一个头文件进行封装,解决方案输出一个dll供unity调用。
但是这种dll只能在unity发布的.exe包PC平台上运行,只有打PC包时该dll才会build到包中,在安卓、iOS等平台移动平台上是无法使用的。其中安卓系统是基于linux的,需要编译.so包才能在安卓上跑。
注意1:在出PC包时,如果使用了这种第三方dll库,要看一下该dll库依赖了那些系统的dll库,一同放到exe的所在目录下,否则在其他windows电脑上找不到所依赖的系统库会调用dll失败无法正常运行。
例如下面打出的PC包,使用了下面自己编译的一个dll库,就需要看一下那个sdggnugo.dll依赖那些系统库,一同放入包中:
可以使用Dependency Walker等工具查看dll的依赖库情况:
这里依赖MSVCR120D.DLL和KEREL32.DLL两个系统库,都可以在本地C盘system32目录中找到,复制到包中即可:
这样即使别人的电脑确实这两个dll系统库也可以正常运行该软件。
注意2: 上面所说的C/C++库制作的dll库是只能在windows上使用的,但如果是C#写的库做成的dll,可以在各平台上使用的,直接将这种dll库放到unity的Plugins根目录下即可。
发布到安卓平台
要想将第三方C库应用到安卓设备上,就需要编译.so库了,而不是dll,dll是windows平台上的动态库,而so是linux平台上的动态库。将C库做成so可以在linux系统中编译,也可以在widnows平台上交叉编译。
在windows平台交叉编译so库,对于比较复杂的库需要使用cmake语法将工程组织起来,然后使用ndk进行编译即可得到相应的so库。
注意1: ndk的版本不宜过新;
注意2:库中C语言(.c文件)的函数是可以直接在unity运行时调用的,如果是C++或者是用C++封装C库,则需要使用extern “C”包一下,具体见unity文档中关于native plugin的介绍。
C++封装C库暴露接口示例
.h
#if defined (EXPORTBUILD)
# define _DLLExport __declspec (dllexport)
# else
# define _DLLExport __declspec (dllimport)
#endif
extern "C"{
#include "gnugo.h"
}
extern "C" int _DLLExport Add(int x,int y);
.cpp
#define EXPORTBUILD
#include "xx.h"
int _DLLExport Add(int x, int y){
return x + y;
}
假设打出的库文件为’myadd.dll’,在unity中调用:
using System.Runtime.InteropServices;
// 运行时加载动态库
[DllImport("myadd")]
public static extern int Add(int x, int y);
void Start(){
int result = Add(1,2); // 调用
}
直接暴露C函数
直接新建一个.c文件,里面直接编写想暴露的函数,将该c文件一同编译到库中即可:
xx.c
// 可以include第三方库的头文件
int Add(int x,int y){
// 可以在这里调用第三方库的函数
return x+y;
}
在unity中调用的方法一样:
using System.Runtime.InteropServices;
// 运行时加载动态库
[DllImport("myadd")]
private static extern int Add(int x, int y);
void Start(){
int result = Add(1,2); // 调用
}
总结
- 制作windows平台的C语言动态库直接在VS中打出dll库拖入unityPlugins目录即可;
- 要打包到安卓平台,则需要编译.so库;