Zusammenfassung der Vorforschung zum Hybird Hot Update (0.8.0)

(Codename Wolong) ist eine nahezu perfekte native C#-Hot-Update-Lösung für die vollständige Unity-Plattform mit vollständigen Funktionen, keinen Kosten, hoher Leistung und wenig Speicher .

HybridCLR erweitert den Code von il2cpp, wodurch er von der reinen AOT-Laufzeit (öffnet neues Fenster) zur Hybrid-Laufzeit „AOT+Interpreter“ wechselt und dann nativ das dynamische Laden von Assemblys unterstützt, sodass Spiele, die auf dem il2cpp-Backend basieren, nicht nur darauf ausgeführt werden können die Android-Plattform, aber auch effiziente Ausführung im AOT+Interpreter- Hybridmodus auf IOS, Konsolen und anderen Plattformen, die JIT einschränken. Unterstützt umfassende Hot-Updates von der untersten Ebene.

Anmerkungen:

AOT, Ahead Of Time, bezieht sich auf das Kompilieren vor der Ausführung. JIT, also Just-in-Time, dynamische (Just-in-Time) Kompilierung, wird während der Ausführung kompiliert.

IL2cpp: Unity hat IL2cpp selbst entwickelt, das den Zwischencode, der auf der virtuellen Mono-Maschine hätte ausgeführt werden sollen, in CPP-Code umwandelt und dann die plattformübergreifende Funktion von C++ verwendet, um den generierten CPP-Code auf jeder Plattform zu übergeben. Plattformen sind kompiliert mit gut optimierten nativen C++-Compilern für höhere Effizienz und bessere Kompatibilität.

charakteristisch.

1. Die Funktionen sind vollständig und die ECMA-335-Spezifikation ist fast vollständig implementiert . Funktionen mit Ausnahme der unten aufgeführten „Einschränkungen und Vorbehalte“ werden unterstützt.

2. Keine Lern- und Nutzungskosten. HybridCLR erweitert die reine AOT-Laufzeit zu einer vollständigen Laufzeit, sodass Hot-Update-Code nahtlos mit AOT-Code funktioniert. Die Skriptklasse und die AOT-Klasse befinden sich in derselben Laufzeit, und Sie können Codes wie Vererbung, Reflexion und Multithreading (flüchtig, ThreadStatic, Task, asynchron) frei schreiben. Es ist nicht erforderlich, speziellen Code zu schreiben, keine Codegenerierung und keine besonderen Einschränkungen.

3.执行高效。实现了一个极其高效的寄存器解释器,所有指标都大幅优于其他热更新方案

4.内存高效。 热更新脚本中定义的类跟普通c#类占用一样的内存空间,远优于其他热更新方案。

5.原生支持hotfix修复AOT部分代码。几乎不增加任何开发和运行开销。

限制和注意事项:(后面补充)

与其他流行的c#热更新方案的区别

HybridCLR是原生的c#热更新方案。通俗地说,il2cpp相当于mono的aot模块,HybridCLR相当于mono的interpreter模块,两者合一成为完整mono。HybridCLR使得il2cpp变成一个全功能的runtime,原生(即通过System.Reflection.Assembly.Load)支持动态加载dll,从而支持ios平台的热更新。

其他热更新方案则是独立vm,与il2cpp的关系本质上相当于mono中嵌入lua的关系。因此类型系统不统一,为了让热更新类型能够继承AOT部分类型,需要写适配器,并且解释器中的类型不能为主工程的类型系统所识别。特性不完整、开发麻烦、运行效率低下。

稳定性状况

目前PC(x86及x64)、macOS(x86、x64、Arm64)、Android(arm v7及v8)、iOS(64bit) 可稳定使用。已经有数百个大中小型商业游戏项目完成接入,其中一些在紧锣密鼓作上线前测试。

Hybrid导入

  1. 打开PackageManager 通过 Add package from git URL 添加 https://github.com/focus-creative-games/hybridclr_unity.git或者下载到本地, 通过 Add package from disk 导入插件。

  1. HybridCLR ---- Installer 导入安装 (当前项目2020.3.29f1可直接点击安装,更低版本需要手动操作)

3.PlayerSetting配置

  • 关闭增量式GC(Use Incremental GC) 选项。因为目前不支持增量式GC

  • Scripting Backend 切换为 il2cpp

  • Api Compatability Level 切换为 .Net 4 or .Net Framework (打主包时可以使用.net standard,但使用脚本Compile热更新dll时必须切换到.Net 4.x or .Net Framework

4.热更新模块拆分

很显然,项目必须拆分为AOT(即编译到游戏主包内)和热更新 assembly,才能进行热更新。HybridCLR对于 怎么拆分程序集并无任何限制,甚至你将AOT或者热更新程序集放到第三方工程中也是可以的。

常见的拆分方式有几种:

  • 使用Unity支持的Assembly Definition将整个项目拆分为多个程序集,Assembly-CSharp作为AOT程序集,不引用任何热更新程序集的代码

  • 将AOT部分拆分为一个或多个程序集,Assembly-CSharp作为热更新程序集,同时还有其他0-N个热更新程序集。

无论哪种拆分方式,正确设置好程序集之间的引用关系即可。

点击菜单 HybridCLR/Settings 打开配置界面。

对于项目中的热更新程序集,如果是assembly definition(asmdef)定义的程序集,加入 hotUpdateAssemblyDefinitions列表,如果是普通dll,则将程序集名字(不包含'.dll'后缀,如Main、Assembly-CSharp)加入hotUpdateAssemblies即可。这两个列表是等价的,不要重复添加,否则会报错。

Hybrid - PC平台测试

1.安装Addressables Window - PackageManager - Package:Unity Registry - 找到Addressables

2.安装Python2.7

3.VS安装C++开发支持 (VS - 工具 - 获取工具和功能)

4.Gradle5.6.4 升级到Gradle 6.1.1

2020.3.29版本测试(新建测试工程Hybrid_Kane,仿照示例工程进行设置)

HybridCLR - Build - Win64 打包成功后,运行成功!

新增热更代码Debug.Log("我热更了一句代码")!

HybridCLR - BuildBundles - BuildAll_ActiveBuildTarget

HybridCLR - Build - BuildAssetsAndCopyToStreamingAssets

手动拷贝编辑器工程StreamingAssets文件到打包后工程StreamingAssets

可见"我热更了一句代码" 至此PC版本测试成功!

Hybrid - Android测试

出包测试:

  1. 新建

Main.dll 以及包含的LoadDll.cs负责加载热更dll以及aot-dll.

LoadDll 逻辑 加载dll - aot加载原始metadata - 加载热更dll。(基础模块,不能热更)

  1. 新建Editor - HybridCLRBuilder 负责打包 热更dll以及aot-dll流程处理。

HybridCLRBuilder 逻辑 - 同步热更dll - 打包生成裁剪dll - 清空临时文件夹 - Hybrid 代码Generate生成 - 编写对应平台dll - 打包hybrid特殊ab - 拷贝aot和hotupdate的dll.

  1. Unity出包Build , 生成apk ,运行成功!

热更测试(接入Dev出包流程):

  1. LoadDll后执行项目第一个启动文件 (通过加载 hybrid的ab XXX.prefab)

(1)加载逻辑,先读外部目录(persistentDataPath),不存在的话,读内部目录(streamingAssetsPath)

  1. 打包 在“构建资源AssetBundle”后

  1. 执行 HybridCLRBuilder的流程,资源根据项目放到basic文件夹下

  1. 内置资源处理

  1. 将hybrid特殊ab 和 aot 以及hotupdate的dll 添加到内置资源中。

检查引用丢失(注意事项):

Hybrid与XLua交互:

1.在打包时,先生成wrapper文件,再将文件生成到热更新模块中。

2.因为xlua生成的代码全在全局的Assembly-CSharp里,甚至做成partial类与Runtime代码关联,因此需要做代码 调整.配合热更新操作。(将xLua原生代码拆分成两部分,编辑器脚本放在主工程,其他部分放在Hotfix工程)

经测试打包,新增方法,Xlua获取新增C#方法均实现。

性能测试:

测试用例:

local function test0()
    CS.HybridHotUpdateTool.StartTime("lua_test" .. 0)
    local cnt = CS.HybridHotUpdateTool.times * 1000

    local go = CS.UnityEngine.GameObject("_")
    local transform = go.transform

    for i = 1, cnt do
        transform.position = transform.position
    end

    CS.UnityEngine.GameObject.Destroy(go)
    CS.HybridHotUpdateTool.EndTime()
end

local function test1()
    CS.HybridHotUpdateTool.StartTime("lua_test" .. 1)
    local cnt = CS.HybridHotUpdateTool.times * 100

    local go = CS.UnityEngine.GameObject("_")
    local transform = go.transform

    for i = 1, cnt do
        transform:Rotate(CS.UnityEngine.Vector3.up, 1)
    end

    CS.UnityEngine.GameObject.Destroy(go)
    CS.HybridHotUpdateTool.EndTime()
end

local function test2()
    CS.HybridHotUpdateTool.StartTime("lua_test" .. 2)
    local cnt = CS.HybridHotUpdateTool.times * 1000

    local go = CS.UnityEngine.GameObject("_")
    local transform = go.transform

    for i = 1, cnt do
        local tmp = CS.UnityEngine.Vector3(i, i, i)
        local x = tmp.x
        local y = tmp.y
        local z = tmp.z
        local r = x + y * z
    end
    CS.HybridHotUpdateTool.EndTime()
end

local function test3()
    CS.HybridHotUpdateTool.StartTime("lua_test" .. 3)
    local cnt = CS.HybridHotUpdateTool.times * 10
    for i = 1, cnt do
        local tmp = CS.UnityEngine.GameObject("___")
        CS.UnityEngine.GameObject.Destroy(tmp)
    end
    CS.HybridHotUpdateTool.EndTime()
end

local function test4()
    CS.HybridHotUpdateTool.StartTime("lua_test" .. 4)
    local cnt = CS.HybridHotUpdateTool.times * 10
    for i = 1, cnt do
        local tmp = CS.UnityEngine.GameObject("___")
        tmp:AddComponent(typeof(CS.UnityEngine.SkinnedMeshRenderer))
        local c = tmp:GetComponent(typeof(CS.UnityEngine.SkinnedMeshRenderer))
        c.receiveShadows = false
        CS.UnityEngine.GameObject.Destroy(tmp)
    end
    CS.HybridHotUpdateTool.EndTime()
end

local function test5()
    CS.HybridHotUpdateTool.StartTime("lua_test" .. 5)
    local cnt = CS.HybridHotUpdateTool.times * 1000
    for i = 1, cnt do
        local tmp = CS.UnityEngine.Input.mousePosition;
    end
    CS.HybridHotUpdateTool.EndTime()
end

local function test6()
    CS.HybridHotUpdateTool.StartTime("lua_test" .. 6)
    local cnt = CS.HybridHotUpdateTool.times * 1000
    for i = 1, cnt do
        local tmp = CS.UnityEngine.Vector3(i, i, i)
        CS.UnityEngine.Vector3.Normalize(tmp)
    end
    CS.HybridHotUpdateTool.EndTime()
end

local function test7()
    CS.HybridHotUpdateTool.StartTime("lua_test" .. 7)
    local cnt = CS.HybridHotUpdateTool.times * 100
    for i = 1, cnt do
        local t1 = CS.UnityEngine.Quaternion.Euler(i, i, i)
        local t2 = CS.UnityEngine.Quaternion.Euler(i * 2, i * 2, i * 2)
        CS.UnityEngine.Quaternion.Slerp(t1, t2, CS.UnityEngine.Random.Range(0.1, 0.9))
    end
    CS.HybridHotUpdateTool.EndTime()
end

local function test8()
    CS.HybridHotUpdateTool.StartTime("lua_test" .. 8)
    local cnt = CS.HybridHotUpdateTool.times * 10000
    local total = 0
    for i = 1, cnt do
        total = total + i - (i / 2) * (i + 3) / (i + 5)
    end
    CS.HybridHotUpdateTool.EndTime()
end

local function test9()
    CS.HybridHotUpdateTool.StartTime("lua_test" .. 9)
    local cnt = CS.HybridHotUpdateTool.times * 1000
    for i = 1, cnt do
        local tmp0 = CS.UnityEngine.Vector3(1, 2, 3)
        local tmp1 = CS.UnityEngine.Vector3(4, 5, 6)
        local tmp2 = tmp0 + tmp1
    end
    CS.HybridHotUpdateTool.EndTime()
end
private static void Test0()
    {
        StartTime("CS_Test" + 0);
        var go = new GameObject("t");
        var transform = go.transform;

        var cnt = times * 1000;
        for (var i = 0; i < cnt; i++)
        {
            transform.position = transform.position;
        }

        UnityEngine.Object.Destroy(go);
        EndTime();
    }

    private static void Test1()
    {
        StartTime("CS_Test" + 1);
        var go = new GameObject("t");
        var transform = go.transform;

        var cnt = times * 100;
        for (var i = 0; i < cnt; i++)
        {
            transform.Rotate(Vector3.up, 1);
        }

        UnityEngine.Object.Destroy(go);
        EndTime();
    }

    private static void Test2()
    {
        StartTime("CS_Test" + 2);
        var cnt = times * 1000;
        for (var i = 0; i < cnt; i++)
        {
            var v = new Vector3(i, i, i);
            var x = v.x;
            var y = v.y;
            var z = v.z;
            var r = x + y * z;
        }
        EndTime();
    }

    private static void Test3()
    {
        StartTime("CS_Test" + 3);
        var cnt = times * 10;
        for (var i = 0; i < cnt; i++)
        {
            var go = new GameObject("t");
            UnityEngine.Object.Destroy(go);
        }
        EndTime();
    }

    private static void Test4()
    {
        StartTime("CS_Test" +4);
        var cnt = times * 10;
        for (var i = 0; i < cnt; i++)
        {
            var go = new GameObject();
            go.AddComponent<SkinnedMeshRenderer>();
            var c = go.GetComponent<SkinnedMeshRenderer>();
            c.receiveShadows = false;
            UnityEngine.Object.Destroy(go);
        }
        EndTime();
    }

    private static void Test5()
    {
        StartTime("CS_Test" + 5);
        var cnt = times * 1000;
        for (var i = 0; i < cnt; i++)
        {
            var p = Input.mousePosition;
        }
        EndTime();
    }

    private static void Test6()
    {
        StartTime("CS_Test" + 6);
        var cnt = times * 1000;
        for (var i = 0; i < cnt; i++)
        {
            var v = new Vector3(i, i, i);
            Vector3.Normalize(v);
        }
        EndTime();
    }

    private static void Test7()
    {
        StartTime("CS_Test" + 7);
        var cnt = times * 100;
        for (var i = 0; i < cnt; i++)
        {
            var q1 = Quaternion.Euler(i, i, i);
            var q2 = Quaternion.Euler(i * 2, i * 2, i * 2);
            Quaternion.Slerp(Quaternion.identity, q1, 0.5f);
        }
        EndTime();
    }

    private static void Test8()
    {
        StartTime("CS_Test" + 8);
        long total = 0;
        var cnt = times * 10000;
        for (var i = 0; i < cnt; i++)
        {
            total = total + i - (i / 2) * (i + 3) / (i + 5);
        }
        EndTime();
    }

    private static void Test9()
    {
        StartTime("CS_Test" + 9);
        var cnt = times * 1000;
        for (var i = 0; i < cnt; i++)
        {
            var a = new Vector3(1, 2, 3);
            var b = new Vector3(4, 5, 6);
            var c = a + b;
        }
        EndTime();
    }

测试结果:

Hybrid - IOS测试:

libil2cpp.a 编译

确保你的macOS版本>=12以及xcode版本>=13

点击菜单 生成所有必要的文件

HybridCLRData/Generate/All

安装Camke

brew install cmake

坑点(需要优化,处理):

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

热更dll中,不能用 UNITY_EDITOR 宏, compile之后,已经没有所谓的宏了。

  1. dll裁剪生成导出是在打包时,所以需要二次打包(要整理出包流程,当前是用二次出包,需要优化)

  1. 暂不支持:

  1. GetComponent(string name)方式无法获得组件(PC,和真机不一致,打包后报错风险)

  1. IOS打包需要生成并替换Xcode工程中的libil2cpp.a文件。

  1. 某些Unity版本需要手动修改Unity编辑器相关dll.(目前2020.3.29不需要)

  1. Unity 安装目录必须包含版本号,否则无法识别版本。

Supongo que te gusta

Origin blog.csdn.net/qq_40833062/article/details/129736450
Recomendado
Clasificación