14 Unity 3D的脚本编译

主要介绍Unity 3D脚本编译流程,JIT编译、AOT编译、如何将项目发布到Android与IOS上。

一、Unity 3D脚本编译流程

我们都知道,用高级编程语言写成的源代码并不能直接被机器识别并运行,而是通过编译成原生码,也就是二进制机器码,才能运行。Unity使用的三种脚本语言编写的代码,则需要经过两次编译,第一次,将源代码编译成.Net的dll文件,第二次是运行时,进行JIT编译,从CIL编译成机器码(IOS系统除外)。

1. 第一次编译

这一次编译的实际作用是语法检查以及“正确代码”分析,并输出对源代码行为的表述代码。Unity会利用Mono将所有脚本编译成.Net的dll文件,此时生成的是CIL代码中间语言。  因为CIL代码需要运行时来控制它的执行,所以也称为托管模块,托管模块包括CIL代码和metadata元数据

metadata元数据:描述模块定义了什么,如类型与成员,有时也会描述引用了哪些内容。这样的优点时在第二次编译时不需要额外生成原生的C/C++头文件与库文件支持,直接利用CIL与metadata就可以获得编译时的全部信息。

但是其实这一次编译后得到并非托管模块,而是一些文件名以Assembly开头的dll文件,实际上,运行时是通过这些dll文件,也就是程序集和托管模块接触的。程序集是重用、安全性以及版本控制的最小单元。所以,编译器会将托管模块转化为程序集。程序集包括PE32或PE32+文件与所有元数据表manifest清单。

为什么要引入程序集呢?因为程序集可重用,可以将常用的类型,如hero,solider等类型放在一个程序集中,不常用的,如每年的节日活动放到另一个程序集中。

因为没有直接编译成二进制文件,所以这一步的速度很快。

而在编译时,必须按照某种顺序才能正确地编译,不然这个脚本被编译的时候,它引用的脚本还未被编译,则会报错。在Unity中有一些保留文件夹会被先编译,Unity的编译顺序为:

a. Standard Assets文件夹:这个文件夹最先编译,里面会放一些Unity自带的组件,如3rd Person Controller等。

a. Pro Standard Assets文件夹,这个文件夹紧接着上一个编译,但是存放的是专业版才有的功能。

a. Plugins文件夹:存放一些可以在游戏脚本中访问的原生插件,它只能放在Assets文件夹的根目录下。

以上三个文件夹中的脚本都不会调用其他文件夹中的脚本,所以被优先编译不会报错,但是它们可以通过GameObject.SendMessage()和其他脚本通信。

b. Editor文件夹:该文件夹在项目发布时不会被打包,只能在编辑器中被编译,主要实现一些对编辑器的自定义功能。如果Editor文件夹位于以上三个文件夹中,则接下来编译这个文件夹。

c. 接下来编译开发者自己写的不在Editor文件夹中的脚本文件。

d. 最后编译不在特殊文件夹下的Editor文件夹中的脚本。

2. 第二次编译

(1)JIT编译

JIT(just-in-time)编译,也就是即时编译,是在运行时的环境中发生的编译,游戏启动后,首先将Mono运行时载入内存,然后加载所需的程序集,然后调用入口方法。接下来就是对游戏脚本进行JIT编译了。

在运行一个方法之前,Mono运行时会检测出这个方法中需要的对象,为它们分配内部数据结构,并且调用的对象方法有个记录项,Mono运行时将这些记录项放在JITCompiler的未编档函数中,之后在运行到相关函数时,会调用JITCompiler函数来进行JIT编译,将CIL编译成原生代码。这里需要注意的是,如果一个方法中多次调用了另一个方法,则只会在第一次调用时需要编译,之后会直接从内存块中读取之前编译好的该方法的原生码。

虽然这种把编译拆成两段的编译方式比直接从高级语言翻译成机器码更加低效,但是它也有一些好处:

a. 支持多平台,因为先编译成了CIL,就利用了CIL的跨平台能力。

b. JIT会对原生代码进行性能优化。

其实经过Mono的优化,JIT即时编译虽然对运行效率有影响,但是影响却不大。

因为IOS封了内存(或者说是堆)的可执行权,这相当于变相封锁了JIT这种编译方式,所以不能进行热更新。

(2) AOT提前编译

不是在运行时编译脚本。AOT并非将所有的CIL都编译完成,它和JIT并非对立,使用AOT会减少JIT的工作量。

(3) Full-AOT

这种编译方式主要是为了适应ios系统,它保证在运行时不进行任何JIT编译。但是有一些限制:

a. 不支持泛型虚方法

b. 不允许动态生成代码因此无法使用System.Reflection.Emit,也就无法动态创建类。无法使用DLR或基于DLR的任何语言。

c. 不支持对泛型类的P\Invoke

d. 目前不能使用反射中的Property.SetInfo给非空类型赋值。

e. 值类型作为Dictionary的Key时会有问题。

不要混淆了反射与System.Reflection.Emit,所有反射与相关API均可用。

这种编译的优点有:

a. 减少了启动时间

b. 有利于内存共享以节约内存(如果一个程序集被同时被多个进程加载,就可以通过内存映射的方式同时映射到多个进程的地址空间)

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值