官方文档
Unity 版本(国际版): Unity 2020.3.26.f1
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"
}
}
运行 HybridCLR/Installer… 再点击安装 (有的版本比较特殊)
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中编写一个泛型实例化))
详细介绍
我之前是 2019.4.33f1 但是官方最低只支持 2019.4.40,如果要使用 2019.4.33f1 需要自己手动修改 il2cpp.dll。2019 与 2021 都需要额外处理一些东西,所以我选择了进行Unity升级。2020 版本不需要额外改动,是最简单接入的版本。
我做这个热更新只是为了打包方便,如果出现bug不想再用Unity Build项目。不做网络下载部分。
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
{
///
/// 这里填写需要补充元数据的 AOT dll
///
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}";
}
///
/// 下载好的资源
///
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();
}
///
/// 启动游戏
///
void StartGame()
{
LoadMetadataForAOTAssemblies();
LoadHotFixDll();
SceneManager.LoadScene("GameScene");
//执行热更新入口代码 换成自己的
var method = MainAssembly.GetType("LightVelocityFrame.GameEntry").GetMethod("Init");
method.Invoke(null, null);
}
///
/// 为aot assembly加载原始metadata, 这个代码放aot或者热更新都行。
/// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行
///
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
}
}
}
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/CompileDll/ActiveBuildTarget
编译生成热更新dll。如果是AOT dll找不到,请先运行HybridCLR/Generate/LinkXml
,接着在Build Settings
中打包或者导出工程来生成AOT dll由于我的是老项目,不能直接使用 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 添加到游戏物体上
遇到 MissingMethodException xxx 错误
错误原因:热更新中使用了其他程序集的泛型类,而没有把这个程序集作为AOT dll 添加注册元数据。
解决方法:把这个Dll 配置到Entry.AOTMetaAssemblyNames 字段中
调优,把这个泛型类 在 AOT dll中定义一次