Unity

使用 Mono.Cecil 辅助 Unity3D 手游进行性能测

2018-01-31  本文已影响0人  加菲教主

之前的方法及其局限

问题背景和最初的尝试见这里。最开始的想法比较简单,只想着利用 PostprocessBuild 这个事件,来对已经准备好的本地工程文件(iOS 或 Android)中的 .NET 程序集进行注入。但是,这样做限制很多。

首先,无法对 IL2CPP 作为 Scripting Backend 的情况进行注入。因为触发这个事件时,本地工程文件中没有 .NET 程序集,只有 C++ 代码,无法用 Cecil 进行注入。

第二,Android 平台,用 Mono2x 作为 Scripting Backend 的情况下,也需要打包为 Android Studio Project 才能使用。对于直接打包成 apk 的情况,无法简单的进行注入(除非使用解包、注入、重新签名打包的方法,比较麻烦)。

第三,iOS 平台,即使用 Mono2x 作为 Scripting Backend,也无法成功。这是因为在 iOS 平台打包需要先进行一个叫 AOT Cross Compiling 的步骤,对所有的程序集生成对应的 .dll.s 文件。这些文件包含的信息会在运行时被校验,如果我篡改了程序集,而没有理会 .dll.s 文件,在运行时会报错。错误信息类似

A script behaviour (probably XXX?) has a different seralization layout when loading. (Read ** bytes but expected ** bytes)

Did you #ifdef UNITY_EDITOR a section of your serialized properties in any of your scripts?

其中 XXX 是 .NET 脚本名称,两组星号表示两个不同值。这错误最终导致脚本加载失败,无法运行游戏。与错误信息描述不同,我并没有在出问题的脚本上写任何条件编译的代码。要想解决这个问题,估计需要篡改 .dll.s 文件才可以,仍然是很不经济的。

篡改编译器的方法

接下来一个办法,就是对 Unity 的 C# 编译器 mcs.exe 进行篡改。我没有深入实验,因为几个简单的实验就耗费了一天多的时间。我主要尝试了两种方法,当然,都没成功。

方法一,将原 mcs.exe 重命名(如 mcs1.exe),而后自己写一个 .NET 控制台应用程序,占据原来 mcs.exe 的位置,在其中用 System.Diagnostic.Process 类来启动 mcs1.exe。这个过程中,我对 Process 对象的一些配置,如环境变量(EnvironmentVariables 属性)、输入输出重定向(RedirectStandardXXX 属性)进行了多种排列组合,仍无法正确调用 mcs1.exe,就更不要说调用之后的事情了。

方法二,直接在 mcs.exe 中注入代码。因为 mcs.exe 也是一个 .NET 应用程序,并且看上去未经混淆,所以直接注入是可行的。即,「把向游戏程序集中注入代码的代码,注入到编译器中。」这样做主要的问题,是 mcs.exe 的输出目录是临时文件夹,无法保证其中有我们依赖的(如注入后写入程序集时,需要用 Mono.Cecil 的 DefaultAssemblyResolver 进行解析的)程序集。

通过 OnPostprocessScene 回调事件来进行注入

Unity 虽然没有在执行 mcs.exe 和后续步骤(IL2CPP、Android 打包 apk、iOS 上的 AOT 交叉编译等)之间提供回调,但是回调事件 OnPostprocessScene 目前是确保在它们之间至少触发一次的。多亏 https://github.com/rayosu/UnityDllInjector 提醒了我。在这个事件回调中处理 DLL,理论上在任何平台、任何 Scripting Backend 上都可以有效注入。实现过程中有几个要点需要注意:

目前我测试了 Android + Mono/IL2CPP 和 iOS + IL2CPP,都没有问题。iOS + Mono2x 可能由于我们项目本身的一些问题,在 Xcode 链接阶段有一些问题。


旧文搬运,2017-06-15 首发于博客园。

上一篇下一篇

猜你喜欢

热点阅读