Unity中的原生插件及平台交互原理是什么

发布时间:2021-12-03 16:44:10 作者:柒染
来源:亿速云 阅读:262

这篇文章给大家介绍Unity中的原生插件及平台交互原理是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

原生插件/平台交互

虽然大多时候使用Unity3D进行游戏开发时,只需要使用C#进行逻辑编写。但有时候不可避免的需要使用和编写原生插件,例如一些第三方插件只提供C/C++原生插件、复用已有的C/C++模块等。有一些功能是Unity3D实现不了,必须要调用Android/iOS原生接口,比如获取手机的硬件信息(UnityEngine.SystemInfo没有提供的部分)、调用系统的原生弹窗、手机震动等等

1 、C/C++插件

编写和使用原生插件的几个关键点:

创建C/C++原生插件

在C#中标识C/C++的函数并调用

在C#中创建回调函数,C/C++调用C#回调函数

那么C#与原生插件之间是如何实现互相调用的呢?

1.将源码编译为托管模块;

2.将托管模块组合为程序集;

3.加载公共语言运行时CLR;

4.执行程序集代码。

注:CLR(公共语言运行时,Common LanguageRuntime)和Java虚拟机一样也是一个运行时环境,它负责资源管理(内存分配和垃圾收集),并保证应用和底层操作系统之间必要的分离。

为了提高平台的可靠性,以及为了达到面向事务的电子商务应用所要求的稳定性级别,CLR还要负责其他一些任务,比如监视程序的运行。按照.NET的说法,在CLR监视之下运行的程序属于"托管"(managed)代码,而不在CLR之下、直接在裸机上运行的应用或者组件属于"非托管"(unmanaged)的代码。

这几个过程我总结为下图:
Unity中的原生插件及平台交互原理是什么
图.NET上的程序运行

回调函数是托管代码C#中的定义的函数,对回调函数的调用,实现从非托管C/C++代码中调用托管C#代码。那么C/C++是如何调用C#的呢?大致分为2步,可以用下图表示:
Unity中的原生插件及平台交互原理是什么

将回调函数指针注册到非托管C/C++代码中(C#中回调函数指委托delegate)

调用注册过的托管C#函数指针

相比较托管调用非托管,回调函数方式稍微复杂一些。回调函数非常适合重复执行的任务、异步调用等情况下使用。

由上面的介绍可以知道CLR提供了C#程序运行的环境,与非托管代码的C/C++交互调用也由它来完成。CLR提供两种用于与非托管C/C++代码进行交互的机制

平台调用依赖于元数据在运行时查找导出的函数并封送(Marshal)其参数。下图显示了这一过程。
Unity中的原生插件及平台交互原理是什么
注意:

1. 除涉及回调函数时以外,平台调用方法调用从托管代码流向非托管代码,而绝不会以相反方向流动。虽然平台调用的调用只能从托管代码流向非托管代码,但是数据仍然可以作为输入参数或输出参数在两个方向流动。

2. 图中DLL表示动态库,Windows平台指.dll文件、Linux/Android指.so文件、Mac OS X指.dylib/framework文件、iOS中只能使用.a。后文都使用DLL代指,并且DLL使用C/C++编写。

当"平台调用"调用非托管函数时,它将依次执行以下操作:

注意:  只在第一次调用函数时,才会查找和加载 DLL并查找函数在内存中的地址。iOS中使用的是.a已经静态打包到最终执行文件中。

2、 Android插件

Java同样提供了这样一个扩展机制JNI(Java NativeInterface),能够与C/C++互相通信。

注:

C#/Java都可以和C/C++通信,那么通过编写一个C/C++模块作为桥接,就使得C#与Java通信成为了可能,如下图所示:
Unity中的原生插件及平台交互原理是什么
注:C/C++桥接器本身跟Unity3D没有直接关系,不属于Android和Unity3D,图中放在Unity3D中是为了代指libunity.so中实现的桥接器以表示真实的情况。

通过JNI既可以用于Java代码调用C/C++代码,也可用于C/C++代码与Java(Dalvik/ART虚拟机)的交互。JNI定义了2个关键概念/结构:JavaVM、JNIENV。JavaVM提供虚拟机创建、销毁等操作,Java中一个进程可以创建多个虚拟机,但是Android一个进程只能有一个虚拟机。JNIENV是线程相关的,对应的是JavaVM中的当前线程的JNI环境,只有附加(attach)到JavaVM的线程才有JNIENV指针,通过JNIEVN指针可以获取JNI功能,否则不能够调用JNI函数。

Unity中的原生插件及平台交互原理是什么

C/C++要访问的Java代码,必须要能访问到Java虚拟机,获取虚拟机有2中方法:

所以,我们只需要在编写C/C++桥接器so的时候定义JNI_OnLoad(JavaVM* jvm, void*reserved)方法即可,然后把JavaVM指针保存起来作为上下文使用。

获取到JavaVM之后,还不能直接拿到JNI函数去获取Java代码,必须通过线程关联的JNIENV指针去获取。所以,作为一个好的开发习惯在每次获取一个线程的JNI相关功能时,先调用AttachCurrentThread();又或者每次通过JavaVM指针获取当前的JNIENV:java_vm->GetEnv((void**)&jni_env, version),一定是已经附加到JavaVM的线程。通过JNIENV可以获取到Java的代码,例如你想在本地代码中访问一个对象的字段(field),你可以像下面这样做:

1.对于类,使用jni_env->FindClass获得类对象的引用

2.对于字段,使用jni_env->GetFieldId获得字段ID

3.使用对应的方法(例如jni_env->GetIntField)获取字段的值

类似地,要调用一个方法,你

step1.得获得一个类对象的引用obj,

:Unity3D中对应的C/C++桥接器包含在libunity.so中。

3、iOS插件

iOS编写插件比Android要简单很多,因为Objective-C也是C-compatible的,完全兼容标准C语言。这些就可以非常简单的包一层 extern"c"{},用C语言封装调用iOS功能,暴露给Unity3D调用。并且可以跟原生C/C++库一样编成.a插件。C#与iOS(Objective-C)通信的原理跟C/C++完全一样:
Unity中的原生插件及平台交互原理是什么

除此之外,UnityiOS支持插件自动集成方式。所有位于Asset/Plugings/iOS文件夹中后缀名为.m , .mm , .c ,.cpp的文件都将自动并入到已生成的Xcode项目中。然而,最终编进执行文件中。后缀为.h的文件不能被包含在Xcode的项目树中,但他们将出现在目标文件系统中,从而使.m/.mm/.c/.cpp文件编译。这样编写iOS插件,除了需要对iOSObjective-C有一定了解之外,与C/C++插件没有差异,反而更简单。

关于Unity中的原生插件及平台交互原理是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

推荐阅读:
  1. [unity3d]unity平台的预处理
  2. Android中js和原生交互的示例代码

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

unity

上一篇:Java中Error与Exception的区别有哪些

下一篇:WPF与Silverlight的关键区别是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》