江枫

Unity 动态编译

   众所周知,在Unity中每次我们修改C#代码后,Unity都要转一会菊花,而转菊花的目的就是把新的代码重新编译成dll。
   而且在Unity中其实有一个运行时编译dll的选项,在preferences内,但是这个选项仍然不能完全满足我们的需求,因为他还是要转菊花,而且如果脚本修改了当前使用的物体,极大概率会报错999+的~

1.CodeDomProvider与动态编译

   CodeDomProvider可以帮我们动态编译dll,而这个类使用起来也很简单:这里提供了一个简单的例子。

   从上面的代码中我们可以看出来,整个的编译流程其实只涉及三个类型:CodeDomProvider、CompilerParameters和CompilerResults。其中:

  • CodeDomProvider为编译代理类,其实就是这个类型负责编译~
  • CompilerParameters为编译时需要的参数,包括了这个类型需要依赖哪些其他程序集,是否加载到内存等。
  • CompilerResults为最终编译后的结果

2.Unity与动态编译

   上面动态编译的大概流程我们已经了解了,那么在Unity内如何进行动态编译呢?毕竟上面在编译时已经知道新的程序集依赖了哪些程序集。
   不过早就有大佬已经实现了:Unity动态编译
   效果:

GIF

   而且偷完代码发现可以直接用~不过还是踩到了几个坑:

   在获取依赖的程序集时:AppDomain.CurrentDomain.GetAssemblies(),这里获取到的程序集包含了其他的动态程序集,而动态程序集是无法依赖其他动态程序集的,否则会报错:The invoked member is not supported in a dynamic module.
   所以在源代码中用Linq清理了一下动态程序集~
   此外源代码中将程序集生成到了temp文件夹下,并没有直接加载到内存中。所以还要后面loaddll,但是我嫌麻烦直接加到内存了~_compileParams.GenerateInMemory = true;
   完整代码:
DynamicCodeHelper:

DynamicCodeWindow:

3.动态编译+Unity Hooker

   其实动态编译到这已经差不多可以结束了,但是我们之前还研究过一下下UnityHooker。那么我们能不能把这两个东西结合一下呢?
   我们调试的时候经常会有逻辑十分复杂,但是又不知道是什么逻辑导致的一些比如GameObject不知道被谁给激活/隐藏了。那么我们的动态编译与Hooker就可以大显身手了!

GIF

   就像上图一样,有一段莫名其妙的逻辑一直在开关某个预制体(当然是我随便写的!),但是我们又一时之间找不到对应逻辑在哪,或者说想在SetActive的时候插入一段逻辑。那么只要把我们在一边写好的Hook逻辑(当然是编辑器里写好的复制过来的)逻辑动态编译进来,就可以动态的添加代码,调试代码了!

   Hook的逻辑也很简单,之前也有介绍过了,就是使用两个函数替换原有函数地址。只不过前面的动态编译代码中限定了类名和执行的函数名,这里把对应的函数替换了一下就可以用了~

文章大纲