官方文档
实验环境
Unity 版本(国际版): Unity 2020.3.26.f1
hybridclr_unity : https://gitee.com/focus-creative-games/hybridclr_unity.git#v2.0.0-rc
安装HybridCLR (有gitee账号才可使用)
官方安装教程
添加 hybridclr_unity package
- 方法一:
在 Unity项目/Packages/manifest.json 文件中添加一行(注意逗号, 末尾行不需要在后面加逗号)
“com.focus-creative-games.hybridclr_unity”: “https://gitee.com/focus-creative-games/hybridclr_unity.git#v2.0.0-rc”,
如:
{
"dependencies": {
"com.focus-creative-games.hybridclr_unity": "https://gitee.com/focus-creative-games/hybridclr_unity.git#v2.0.0-rc",
"com.unity.2d.sprite": "1.0.0",
"com.unity.2d.tilemap": "1.0.0",
"com.unity.ide.rider": "3.0.7",
"com.unity.ide.visualstudio": "2.0.12",
"com.unity.render-pipelines.universal": "10.8.1",
"com.unity.textmeshpro": "3.0.6",
"com.unity.timeline": "1.6.2",
"com.unity.ugui": "1.0.0",
"com.unity.modules.ai": "1.0.0",
"com.unity.modules.androidjni": "1.0.0",
"com.unity.modules.animation": "1.0.0",
"com.unity.modules.assetbundle": "1.0.0",
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.cloth": "1.0.0",
"com.unity.modules.director": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0",
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.particlesystem": "1.0.0",
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.physics2d": "1.0.0",
"com.unity.modules.screencapture": "1.0.0",
"com.unity.modules.terrain": "1.0.0",
"com.unity.modules.terrainphysics": "1.0.0",
"com.unity.modules.tilemap": "1.0.0",
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.uielements": "1.0.0",
"com.unity.modules.umbra": "1.0.0",
"com.unity.modules.unityanalytics": "1.0.0",
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.unitywebrequestassetbundle": "1.0.0",
"com.unity.modules.unitywebrequestaudio": "1.0.0",
"com.unity.modules.unitywebrequesttexture": "1.0.0",
"com.unity.modules.unitywebrequestwww": "1.0.0",
"com.unity.modules.vehicles": "1.0.0",
"com.unity.modules.video": "1.0.0",
"com.unity.modules.wind": "1.0.0"
}
}
- 方法二:
使用 git 拉取 https://gitee.com/focus-creative-games/hybridclr_unity.git 项目到本地
创建文件夹 com.focus-creative-games.hybridclr_unity
把拉取下来的文件拷贝到 com.focus-creative-games.hybridclr_unity 文件夹(不要拷贝 .git 文件夹)
把 com.focus-creative-games.hybridclr_unity 文件夹 拷贝到 Unity项目/Packages/ 文件夹下
安装
运行 HybridCLR/Installer… 再点击安装 (有的版本比较特殊)
HybridCLR Unity项目设置
Build Setting 设置
- 使用 Faster runtime。(有的平台有,有的平台无此选项)
Player 设置
- Scripting Backend 必须是 IL2CPP
- Api Compatibility Level* 没特殊情况 设置为 .Net 4.x
- Use incremental GC 取消勾选,暂时不支持增量GC
HybridCLR Settings
- 热更新 dll(Hot Update Assemblies) 与 热更新 Assembly Definitions(Hot Update Assembly Definitions)
不能把同一个dll 名字配置到 两个配置中。 两个都表示可以热更新的 dll 名称(不带扩展名)
Hot Update Assembiles 表示普通dll
Hot Update Assembly Definitions 表示Unity项目中的(assembly definition)程序集 - externalHotUpdateAssemblyDirs 外部热更新dll搜索路径
如果热更新 dll 项目 是Unity外部的,则需要配置一个外部dll所在的路径。
泛型
1.HybridCLR的补充元数据技术。如果热更新 dll 中使用到 aot dll 中的泛型类,那么这个aot dll 就需要调用RuntimeApi.LoadMetadataForAOTAssembly 方法来补充元数据。
补充元数据有两个模式
HomologousImageMode.Consistent 只能使用裁减后的AOT dll
HomologousImageMode.SuperSet 既可以使用裁减后的AOT dll 也可以使用原始 AOT dll
补充元数据的泛型函数以解释方式运行,执行效率慢,最好提前在AOT中泛型实例化,(HybridCLR/Generate/AOTGenericReference)工具可以自动收集泛型实例
2.il2cpp 的泛型共享技术 (值类型无法使用,最好还是使用(第一种方法+AOT中编写一个泛型实例化))
详细介绍
我的理解
- AOT 表示表示静态编译。上下文中的 AOT dll 表示静态编译的 dll(可以理解为除了热更新dll,其他的就是 AOT dll)。静态编译一般是不能被更新的,HybridCLR 中也可以被热更新(需要使用DHE)
- 热更新 可以 依赖 热更新dll 以及 AOT dll, 但是 AOT 不可以依赖 热更新 dll。所以热更新dll一般采用 Assembly-CSharp 或者 外部 dll作为热更新 dll。
- 热更新多个 dll 时,需要先加载被依赖的 dll
- link.xml 中可以配置一些预留的 保留类。方便以后热更新dll 使用
接入实战
一个基于GameFrarmeWork(GF框架)的游戏框架
环境 Unity 2020.3.26f1
我之前是 2019.4.33f1 但是官方最低只支持 2019.4.40,如果要使用 2019.4.33f1 需要自己手动修改 il2cpp.dll。2019 与 2021 都需要额外处理一些东西,所以我选择了进行Unity升级。2020 版本不需要额外改动,是最简单接入的版本。
我做这个热更新只是为了打包方便,如果出现bug不想再用Unity Build项目。不做网络下载部分。
接入步骤
- 在Unity 项目的manifest中新增一行
“com.focus-creative-games.hybridclr_unity”: “https://gitee.com/focus-creative-games/hybridclr_unity.git#v2.0.0-rc”, - 点击 HybridCLR Installer -> 安装
- 修改相关配置
关闭增量GC 取消 Use incremental GC 勾选
配置热更新dlls 我选择热更新 Unity 主项目 Assembly-CSharp - 新增游戏入口程序集,创建入口场景,编写入口场景
Entry 脚本
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using HybridCLR;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
public class Entry : MonoBehaviour
{
/// <summary>
/// 这里填写需要补充元数据的 AOT dll
/// </summary>
public static List<string> AOTMetaAssemblyNames { get; } = new List<string>()
{
"mscorlib.dll",
"System.dll",
"System.Core.dll",
//这里填写自己的ATO dll
"FGUI.dll",
"LightVelocityFrame.dll",
"UnityGameFramework.Runtime.dll",
"UnityEngine.dll",
"GameFramework.dll",
"Newtonsoft.Json.dll"
};
public const string MainHotFixDll = "Assembly-CSharp.dll";
public static Assembly MainAssembly
{
get;
private set;
}
//这里填写热更新 dll
public static List<string> HotFixAssemblyNames { get; } = new List<string>()
{
};
public static string DllDir => $"{Application.streamingAssetsPath}/HotFixDll";
public static string GetDllPath(string asset)
{
return $"{DllDir}/{asset}";
}
/// <summary>
/// 下载好的资源
/// </summary>
private static Dictionary<string, byte[]> s_assetDatas = new Dictionary<string, byte[]>();
public static byte[] GetAssetData(string dllName)
{
return s_assetDatas[dllName];
}
// Start is called before the first frame update
void Start()
{
StartCoroutine(DownLoadAssets(this.StartGame));
}
private string GetWebRequestPath(string asset)
{
var path = GetDllPath(asset);
if (!path.Contains("://"))
{
path = "file://" + path;
}
if (path.EndsWith(".dll"))
{
path += ".bytes";
}
return path;
}
public IEnumerator DownLoadAssets(Action onDownloadComplete)
{
var assets = new List<string>(){MainHotFixDll}.Concat(AOTMetaAssemblyNames).Concat(HotFixAssemblyNames);
foreach (var asset in assets)
{
string dllPath = GetWebRequestPath(asset);
UnityWebRequest www = UnityWebRequest.Get(dllPath);
yield return www.SendWebRequest();
#if UNITY_2020_1_OR_NEWER
if (www.result != UnityWebRequest.Result.Success)
{
Debug.Log(www.error);
}
#else
if (www.isHttpError || www.isNetworkError)
{
Debug.Log(www.error);
}
#endif
else
{
// Or retrieve results as binary data
byte[] assetData = www.downloadHandler.data;
Debug.Log($"dll:{asset} size:{assetData.Length}");
s_assetDatas[asset] = assetData;
}
}
onDownloadComplete();
}
/// <summary>
/// 启动游戏
/// </summary>
void StartGame()
{
LoadMetadataForAOTAssemblies();
LoadHotFixDll();
SceneManager.LoadScene("GameScene");
//执行热更新入口代码 换成自己的
var method = MainAssembly.GetType("LightVelocityFrame.GameEntry").GetMethod("Init");
method.Invoke(null, null);
}
/// <summary>
/// 为aot assembly加载原始metadata, 这个代码放aot或者热更新都行。
/// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行
/// </summary>
private static void LoadMetadataForAOTAssemblies()
{
/// 注意,补充元数据是给AOT dll补充元数据,而不是给热更新dll补充元数据。
/// 热更新dll不缺元数据,不需要补充,如果调用LoadMetadataForAOTAssembly会返回错误
///
HomologousImageMode mode = HomologousImageMode.SuperSet;
foreach (var aotDllName in AOTMetaAssemblyNames)
{
byte[] dllBytes = GetAssetData(aotDllName);
// 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码
LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
}
}
private static void LoadHotFixDll()
{
MainAssembly = System.Reflection.Assembly.Load(GetAssetData(MainHotFixDll));
foreach (var hotFixDllName in HotFixAssemblyNames)
{
#if !UNITY_EDITOR
System.Reflection.Assembly.Load(GetAssetData(hotFixDllName));
#endif
}
}
}
- 编写编辑器脚本 实现以下功能
在 HybridCLRData\AssembliesPostIl2CppStrip\Android 文件夹中找出,热更新dll 中可能会继承的类所在的AOT程序集。放入到项目 StreamingAssets/HotFixDll目录中,并把后缀名改为 bytes
在 HybridCLRData\HotUpdateDlls\Android 文件夹中找出,热更新 dll。放到项目StreamingAssets/HotFixDll目录中,并把后缀名改为 bytes
using System.Collections.Generic;
using System.IO;
using System.Linq;
using HybridCLR.Editor;
using HybridCLR.Editor.Commands;
using UnityEditor;
using UnityEngine;
namespace LightVelocityFrame.Editor
{
public static class HybridCLRTool
{
private const string TitleGenerateAll = "【LVFrame.Builder】/HotUpdate_GenerateAll";
private const string TitleCompileDll = "【LVFrame.Builder】/HotUpdate_Compile";
[MenuItem(TitleGenerateAll)]
public static void GenerateAndCopyDll()
{
PrebuildCommand.GenerateAll();
CopyDll2StreamingAssets();
}
[MenuItem(TitleCompileDll)]
public static void CompileDll()
{
CompileDllCommand.CompileDll(EditorUserBuildSettings.activeBuildTarget);
CopyHotFixAssembilesToStreamingAssets();
}
private static void CopyDll2StreamingAssets()
{
CopyAOTAssembliesToStreamingAssets();
CopyHotFixAssembilesToStreamingAssets();
}
private static void CopyAOTAssembliesToStreamingAssets()
{
var target = EditorUserBuildSettings.activeBuildTarget;
string aotAssembliesSrcDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
string aotAssembliesDstDir = Entry.DllDir;
foreach (var dll in Entry.AOTMetaAssemblyNames)
{
string srcDllPath = $"{aotAssembliesSrcDir}/{dll}";
if (!File.Exists(srcDllPath))
{
Debug.LogError($"没有找到 裁减后的 aot-dll {dll}, 请使用 Generate/all 命令生成 aot dll");
continue;
}
string dllBytesPath = $"{aotAssembliesDstDir}/{dll}.bytes";
File.Copy(srcDllPath, dllBytesPath, true);
Debug.Log($"[CopyAOTAssembliesToStreamingAssets] copy AOT dll {srcDllPath} -> {dllBytesPath}");
}
}
private static void CopyHotFixAssembilesToStreamingAssets()
{
var target = EditorUserBuildSettings.activeBuildTarget;
string hotAssembliesSrcDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target);
string hotAssembliesDstDir = Entry.DllDir;
var hotFixAssemblyNames = new List<string>(){Entry.MainHotFixDll}.Concat(Entry.HotFixAssemblyNames);
foreach (var dll in hotFixAssemblyNames)
{
string srcDllPath = $"{hotAssembliesSrcDir}/{dll}";
if (!File.Exists(srcDllPath))
{
Debug.LogError($"没有找到热更新dll {dll}, 请使用 Generate/all 命令生成热更新dll");
continue;
}
string dllBytesPath = $"{hotAssembliesDstDir}/{dll}.bytes";
File.Copy(srcDllPath, dllBytesPath, true);
Debug.Log($"[CopyAOTAssembliesToStreamingAssets] copy AOT dll {srcDllPath} -> {dllBytesPath}");
}
}
}
}
- 使用 HybridCLR/GenerateAndCopyDll 命令生成 并 拷贝 aot dll 以及 热更新 dll
- 可能遇到的问题 Exception: resolve assembly: xxxx 失败! 如果是热更新dll找不到,请先运行
HybridCLR/CompileDll/ActiveBuildTarget
编译生成热更新dll。如果是AOT dll找不到,请先运行HybridCLR/Generate/LinkXml
,接着在Build Settings
中打包或者导出工程来生成AOT dll
解决方案
如果 xxxx 是 netstandard,那么是 Api Compatibility Level* 设置问题,需要改成 4.x
如果 xxxx 是其他dll,这个问题很奇怪,解决方案在AOT dll 中找个地方使用 这个报错dll(定义字段,或者调用方案)。或者找到热更新代码中调用到这个dll的地方,尝试删除,不能删除就用前面这个方法。如果不行,重启再试。(我是这么解决的)。
- 可能遇到的问题 Exception: resolve assembly: xxxx 失败! 如果是热更新dll找不到,请先运行
- 构建 Android 项目
- 直接使用Android 构建 apk
遇到的问题
由于我的是老项目,不能直接使用 Andorid 构建好的项目。需要把Unity Build的项目与老项目进行融合,导致了一些错误。
-
JNI DETECTED ERROR IN APPLICATION: JNI GetStaticMethodID called with pending exception java.lang.ClassNotFoundException: Didn’t find class “com.unity3d.player.PlayAssetDeliveryUnityWrapper”
出现这个错误是 unity-classes.jar 这个文件没有同步,升级了Unity版本的原因。 -
JNI FatalError called: Unable to load library:xxxxx [dlopen failed: library “libil2cpp.so” not found]
build.gradle 中需要加入 BuildIL2Cpp 任务,参考Unity Build 的安卓项目 -
安卓 Build 报错 NDK is not installed
在local.properties 中配置 ndk 目录 -
Script Missing GF 使用HybridCLR 很可能会遇上这个错误
错误原因:热更MonoBehaviour,如果是挂载在Prefab上,那么这个Prefab 需要是AssetBundle
解决方法:参考
使用AddComponent 添加 或者 把Prefab打包成AssetBundle
GF 解决思路:
GF 的基础组件作为 AOT 程序集,
GF 的自定义组件使用 AddComponent 添加到游戏物体上文章来源:https://www.toymoban.com/news/detail-413115.html -
遇到 MissingMethodException xxx 错误
错误原因:热更新中使用了其他程序集的泛型类,而没有把这个程序集作为AOT dll 添加注册元数据。
解决方法:把这个Dll 配置到Entry.AOTMetaAssemblyNames 字段中
调优,把这个泛型类 在 AOT dll中定义一次文章来源地址https://www.toymoban.com/news/detail-413115.html
到了这里,关于HybridCLR 热更新笔记 GF接入的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!