Unity注入模块,可以运行时改变被注入函数实现。
Verified Verisons:
Unity 2021.3.x | Unity 2022.3.x |
---|---|
:heavy_check_mark: | :heavy_check_mark: |
开发此模块的最初动机是修改UnityEngine的源码。对这样一个具体问题一般化分析和设计后,最终实现顺便支持了用户自定义的装饰器和AOP。
Unity的注入模块已经有一些其他大神的实现了,为什么还要造一个轮子呢?原因如下:
直接修改DLL文件是可行的,但是存在以下问题:
step 1. 安装依赖库:DirectRetrieveAttribute
step 2. 通过git url安装
execute command line:
openupm add com.bbbirder.injection
一个修改Debug.Log
的例子
using com.bbbirder.injection;
using UnityEngine;
// this illustration shows how to hook `Debug.Log`
public class FirstPatch
{
// the field to be overwrited to original method
static Action<object> RawLog;
static void Log(object msg){
return RawLog.Invoke("[msg] "+msg);
}
internal class MethodReplacer : IInjection
{
// implement method: ProvideInjections
public IEnumerable<InjectionInfo> ProvideInjections()
{
yield return InjectionInfo.Create<Action<object>>(
Debug.Log, // replace Debug.Log
FirstPatch.Log, // with FirstPatch.Log
f => FirstPatch.RawLog = f // save origin method to FirstPatch.RawLog
);
}
}
}
自定类继承自IInjection
,并实现接口
ProvideInjections
中返回一个或多个InjectionInfo
初始化的时候调用:
FixHelper.InstallAll();// 查找所有注入标记,并使生效
测试成果:
Debug.Log("hello"); //output: [msg] hello
更多使用方法参考附带的Sample工程
Problem | Reason | Solution |
---|---|---|
文档示例中的异步方法无法打印完整 | WebGL平台不支持多线程 | 文档中使用的是Task,改成UniTask或其他方式即可 |
注入时未搜索到标记的方法 | Managed Stripping Level 过高,Attribute被移除 |
降低Stripping Level或 保留代码 |
注入时报UnauthorizedAccessException 或cannot access file |
文件访问权限不够 | 管理员运行 或 修改目标文件夹的安全设置(属性-安全-编辑,添加当前用户的完全控制) |
打包时报the same key has already been added. Key: mscorlib |
不支持的Unity版本,程序集依赖树包含多个不同版本的mscorlib | 暂不提供解决方案,可自行修改类型引入部分逻辑,如果这个问题影响的人多则解决之 |
UnityInjection在编译时织入,不用担心运行时兼容性
织入时机:
当前的方法在Editor下也会执行织入,这将导致domain reload一定程度变慢,这对于超大代码库来说是难以忍受的。 事实上Editor下的织入不是必要的。作者忙完本职工作后,会来完善此处。
泛型的支持有一些特殊性,这种特殊性需要额外的工作: | Inject Phase | Fix Phase | |
---|---|---|---|
AOT approach | 与非泛型方法相同 | - | |
RT approach | 不同的GenericInstance有时共享同一个方法体,有时又独享之。如:Full Generic Sharing方法存在一个共享的方法体;普通Generic Sharing则按照泛型类型内存大小共享方法体 | 泛型实例是运行时创建的。UnityInjection需要实现一种GenericInstantiation(classInst&methodInst)遍历的技术。这种技术对于IL2CPP backend来说,可以轻易实现;但是对于Mono backend,则需要对cctor的额外注入。 |