Home

Awesome

UnityInjection

Unity注入模块,可以运行时改变被注入函数实现。

mono support il2cpp support GitHub last commit GitHub package.json version

Verified Verisons:

Unity 2021.3.xUnity 2022.3.x
:heavy_check_mark::heavy_check_mark:

Purpose

开发此模块的最初动机是修改UnityEngine的源码。对这样一个具体问题一般化分析和设计后,最终实现顺便支持了用户自定义的装饰器和AOP。

是个轮子?

Unity的注入模块已经有一些其他大神的实现了,为什么还要造一个轮子呢?原因如下:

  1. 基于UnityEditor。一些注入模块,需要依赖外部工具;而此实现在完全在UnityEditor下注入。
  2. 支持IL2CPP。一些注入模块只支持Editor和Mono;而此实现支持所有平台和选项。
  3. 支持修改引擎源码。一些注入模块只能修改用户代码,无法注入引擎代码;而此实现可以注入引擎和用户代码。

直接修改DLL文件?

直接修改DLL文件是可行的,但是存在以下问题:

  1. 无法记录所做的修改
  2. 不方便团队共享和版本控制
  3. 不能改变Unity版本
  4. 修改步骤繁琐,容易误操作

Quick Start

Installation

install via git url

step 1. 安装依赖库:DirectRetrieveAttribute

step 2. 通过git url安装

install via openupm (recommend)

execute command line:

openupm add com.bbbirder.injection

Basic Usage

一个修改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工程

Possible Problems

ProblemReasonSolution
文档示例中的异步方法无法打印完整WebGL平台不支持多线程文档中使用的是Task,改成UniTask或其他方式即可
注入时未搜索到标记的方法Managed Stripping Level过高,Attribute被移除降低Stripping Level或 保留代码
注入时报UnauthorizedAccessExceptioncannot access file文件访问权限不够管理员运行 或 修改目标文件夹的安全设置(属性-安全-编辑,添加当前用户的完全控制)
打包时报the same key has already been added. Key: mscorlib不支持的Unity版本,程序集依赖树包含多个不同版本的mscorlib暂不提供解决方案,可自行修改类型引入部分逻辑,如果这个问题影响的人多则解决之

How it works

UnityInjection在编译时织入,不用担心运行时兼容性

织入时机:

Todo List

  1. 更多Unity版本测试,有问题提ISSUE附Unity版本,或者PR。
  2. Editor加速注入

当前的方法在Editor下也会执行织入,这将导致domain reload一定程度变慢,这对于超大代码库来说是难以忍受的。 事实上Editor下的织入不是必要的。作者忙完本职工作后,会来完善此处。

  1. 支持泛型

泛型的支持有一些特殊性,这种特殊性需要额外的工作:

Inject PhaseFix Phase
AOT approach与非泛型方法相同-
RT approach不同的GenericInstance有时共享同一个方法体,有时又独享之。如:Full Generic Sharing方法存在一个共享的方法体;普通Generic Sharing则按照泛型类型内存大小共享方法体泛型实例是运行时创建的。UnityInjection需要实现一种GenericInstantiation(classInst&methodInst)遍历的技术。这种技术对于IL2CPP backend来说,可以轻易实现;但是对于Mono backend,则需要对cctor的额外注入。