签到

05月06日
尚未签到

共有回帖数 0

    爱琴海

    等级:
    版本准备
    前文详细的介绍了IL2CPP的来龙去脉,这里用一个实际的例子来看看Unity3D里的IL2CPP都为我们做了哪些工作以及在使用的过程中会遇到哪些问题。
    IL2CPP应用的第一个平台是WebGL,为了让游戏可以一键部署到基于WebGL的浏览器中,Unity3D Script工作组的大牛们找到了一个绝妙的解决方案:不仅解决了C#,Unity Script语言兼容问题,还解决了客户端源码泄漏问题。这个功能在Unity5.0 Beta版中提供了测试。
    IL2CPP的第二个试用平台是iOS 64位版。大家都知道苹果已经发了最后通牒,全新App必须在15年2月1日支持64位CPU,而已经上架的游戏也必须在15年6月1日更新的时候支持64位。这个64位编译就是交由IL2CPP完成的。具体到版本是 Unity 4.6.1 p5,Unity4.6.2和Unity 4.6.2 p1。本文后面都使用4.6.2 p1版本来进行演示。

    创建项目,加入代码创建一个空的项目,加入两个cs文件,一个叫IL2CPPCompatible.cs,另外一个是IL2CPPStudy.cs。前者主要用来测试代码在IL2CPP中的兼容性,后者用来产生C++代码,用来做对比分析。
    以下是两个文件的详细内容:



    IL2CPPCompatible.cs这个文件中有两个兼容性测试函数。一个函数使用ThreadPool.QueueUserWorkItem启动一个新的线程。另一个则是SSL认证函数:ssl.AuthenticateAsClient (hosturl); 之所以写这两个函数是因为上述4.6.x IL2CPP对他们支持的还不是很好,会产生问题,这个我们在后面会详细讲到。



    IL2CPPStudy.cs
    using UnityEngine;using System.Collections;using System.IO;using System.Threading;
    public class CoconutClassStudy{        public int inta;        public int intb;
           public int Add()        {                return inta + intb;        }
           public CoconutClassStudy(int a, int b)        {                inta = a;                intb = b;        }
           public void IOTest(string filename)        {                if (File.Exists (filename)) {                        FileStream fs = File.Open(filename, FileMode.Create);                        fs.Close();
                   }        }
           public void ThreadTest()        {                Thread a =new Thread(delegate(object state) {                        Debug.Log ("Thread Started");                });                               a.Start ();        }}

    public class IL2CPPStudy : MonoBehaviour {
           // Use this for initialization        void Start () {                Debug.Log (CoconutFuncStudy (10 , 20));                CoconutClassStudy cc = new CoconutClassStudy (50, 60);                Debug.Log (cc.Add ());                cc.IOTest("test.txt");                cc.ThreadTest ();                Debug.Log (cc.GetType ());        }                // Update is called once per frame        void Update () {                }
           int CoconutFuncStudy(int a, int b)        {                return a + b;        }}


    这个文件里面的内容就更简单了:一个CoconutClassStudy类,里面有一个构造函数,一个Add函数和一个IOTest函数。另外在MonoBehaviour的Start()中,创建这个类的实例,并调用这两个函数。这个源码可以让我们研究以下几个方面:1.cs的类在经过IL2CPP以后如何在CPP文件中表达2.C#的IO操作经过IL2CPP以后如何在CPP文件中表达3.当调用new关键字在堆里产生一个实例的时候CPP文件又是如何做的4.开启一个线程的操作IL2CPP会如何翻译5.调用cc.GetType()的行为IL2CPP如何处理
    有了这连个文件后我们要做的第一件事情是生成XCode项目:

    选择IL2CPP编译模块,然后Build,生成XCode项目。打开项目,在项目结构中打开Classes目录,可以看到多了一个Native的子目录

    IL2CPP转换出的所有文件都在其中。我们写的逻辑代码,都在Assembly-CSharp.cpp中,除了这个文件,Native文件夹中还有很多以Bulk开头的文件,这些其实是IL2CPP把一些必要C#库翻译到CPP形成的文件。

    像Bulk_Generics_x.cpp和System.Collections.Generic有关。Bulk_UnityEngine.UI_x.cpp则和Unity自带的UI有关。
    让我们粗略的分析下在CPP文件中前面的5条都是如何实现的:1.cs的类在经过IL2CPP以后如何在CPP文件中表达



    我们的CoconutClassStudy类在CPP文件中变成了一个Struct,继承于Object_t4。那这个Object_t4又是什么呢?



    聪明的你一看注释就知道了吧,没错,这个就是C#中的万物之源,System.Object。
    既然我们C#的类变成了Struct,那类里面的函数都去哪里了呢?带着这个疑问,我们来看第二条。
    2.C#的IO操作经过IL2CPP以后如何在CPP文件中表达在CoconutClassStudy类中有一个成员函数:IOTest。在CPP中,我们看到了如下的实现:


    类中的函数变成了一般的全局函数,函数名字是类名加上函数名,最后加上一个后缀而成。而原本C#中的File.Exists和File.Open函数都有了相应的C++实现。

    3.当调用new关键字在堆里产生一个实例的时候CPP文件又是如何做的?C#代码中我们在Start函数中有一个显示的New,找到相应C++代码:


    可以看到代码调用了一个叫il2cpp_codegen_object_new的函数。而这个函数最终调用了IL2CPP VM中的New函数,分配了属于GC管理的内存。
    接下来第四条4.开启一个线程的操作IL2CPP会如何翻译


    C#中的System.Thread,在C++中是一个Thread_t26相当巨大的结构,在New出了这个结构之后,设置线程入口函数,最后调用Thread_Start_m47启动线程。这个函数就是对应System.Threading.Thread.Start()



           我们看最后一条5.调用cc.GetType()的行为IL2CPP如何处理。找到C++中的对应Start函数:


    首先我们看到了对应C#中的Type的C++实现:Type_t28,其次是GetType()这个函数在C++中的实现,最后发现是调到了IL2CPP的VM函数:



    在这些C++的实现中,细心的读者可能会发现他们时时刻刻都在使用MethodInfo和TypeInfo这样的信息。这个就是Unity Script项目组提到的Metadata。Metadata指的是非逻辑代码,而是函数,结构,变量以及类本省的一些信息。比如名字,类型等。这个Metadata提供C++代码和后台的IL2CPP VM运行时必要的信息。
    以上5条只是很简单的例子,大家如果对IL2CPP的转换感兴趣,可以自己写出想要了解的测试代码,然后再对比CPP文件看其实现。

    前方有坑,请小心新的事物总是伴随着问题,特别是在软件行业,Bug是不可避免的。就目前阶段而言,IL2CPP还有不少问题。这个就是项目中IL2CPPCompatible.cs存在的意义:做兼容性测试。大家在实际的项目中如果遇到了问题,可以在这个文件中追加测试代码。下面的表格把我们遇到的已知问题做一个列举,供参考。

    链接可执行文件zlib报错ThreadPool.QueueUserWorkItem运行报错SSL.AuthenticateAsClient运行报错
    Unity 4.6.1 p5发生发生发生
    Unity 4.6.2 f1发生发生发生
    Unity 4.6.2 p1修正修正发生








    IL2CPP总结以及我们的建议IL2CPP是Unity核心进行的很重要的进化之一。就现在来看,好处有以下几点:
    1.运行速度加快,游戏安装尺寸减小,内存占用降低
    2.除了可以Mono调试C#之外,我们又多了一种选择:Native C++ 源码级调试(不知道你们什么感觉,我对Unity C#调试颇有意见,经常连不上调试器,而且调试过程中常常宕机。换成用原生IDE调试C++代码,就爽很多啦)。3.可以快速的支持新的平台,当然这点对我们关系不大。
    带来的问题:1.由于原来由Mono VM的IL代码全部变成了CPP,导致项目中多了很多CPP代码,编译时间会显著增加。
    2.IL2CPP还有各种Bug,可能会导致原来的代码不能很好的编译运行。需要等待Unity版本迭代。
    3.鉴于C++静态语言的特性,我们不能使用诸如System.Reflection.Emit这样的动态代码。(C# ATO方式编译)
    给使用Unity开发者的建议:IL2CPP是大势所趋,加上苹果强制使用64位支持,意味着到了6月1号,所有用Unity开发的游戏都要用到新的编译方式。如果你的项目比较大,应该立刻开始尝试IL2CPP,以便发现问题,并开始解决。本文项目在:https://github.com/CoconutIslandStudio/IL2CPP_Test

    楼主 2015-06-23 12:01 回复

共有回帖数 0
  • 回 帖
  • 表情 图片 视频
  • 发表

登录直线网账号

Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号 意见反馈 | 关于直线 | 版权声明 | 会员须知