对比 lua: 是创建一个lua
虚拟机来跑热更代码,会有跨域行为。Ilruntime: 创建一个c#虚拟机来跑热更代码,对mono等支持不好,而且还会有跨域行为hybridclr: 在IL2CPP VM中内置一个.net 字节码解释器,同时还会把.net里面的数据对象映射到native的数据对象(内存对其),不会有跨域行为,对unity代码完全支持,而且不需要生成任何中间代码。
例子 git下载入门项目 https://github.com/focus-creative-games/hybridclr_trial
操作
打开项目
打开安装菜单,HybirdCLR->installer
,点击安装按钮
生成必要文件,HybridCLR->Generate->All
构建一次项目,File->Build Settings
构建一个自己需要测试的平台的项目。
修改代码
修改LoadDll.cs,改成加载服务器上的代码,并且修改热更启动代码为HotCodeLaunch
修改BuildAssetsCommand.cs,热更代码编译生成路径修改,改到Editor下。
LoadDll.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 using HybridCLR;using System;using System.Collections;using System.Collections.Generic;using System.IO;using System.Linq;using UnityEngine;using UnityEngine.Networking;public class LoadDll : MonoBehaviour { public static List<string > AOTMetaAssemblyNames { get ; } = new List<string >() { "mscorlib.dll" , "System.dll" , "System.Core.dll" , }; void Start ( ) { StartCoroutine(DownLoadAssets(this .StartGame)); } private static Dictionary<string , byte []> s_assetDatas = new Dictionary<string , byte []>(); public static byte [] GetAssetData (string dllName ) { return s_assetDatas[dllName]; } private string GetWebRequestPath (string asset ) { var path = $"http://nginx1.gameorse.com/test1/" +asset; if (path.EndsWith(".dll" )) { path += ".bytes" ; } return path; } IEnumerator DownLoadAssets (Action onDownloadComplete ) { var assets = new List<string > { "prefabs" , "Assembly-CSharp.dll" , }.Concat(AOTMetaAssemblyNames); foreach (var asset in assets) { string dllPath = GetWebRequestPath(asset); Debug.Log($"start download asset:{dllPath} " ); 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 { byte [] assetData = www.downloadHandler.data; Debug.Log($"dll:{asset} size:{assetData.Length} " ); s_assetDatas[asset] = assetData; } } onDownloadComplete(); } void StartGame ( ) { LoadMetadataForAOTAssemblies(); #if !UNITY_EDITOR var gameAss = System.Reflection.Assembly.Load(GetAssetData("Assembly-CSharp.dll" )); #else var gameAss = AppDomain.CurrentDomain.GetAssemblies().First(assembly => assembly.GetName().Name == "Assembly-CSharp" ); #endif const string LAUNCH_MONO_NAME = "HotCodeLaunch" ; Type type = gameAss.GetType(LAUNCH_MONO_NAME); GameObject go = new GameObject(LAUNCH_MONO_NAME, type); } private static void LoadMetadataForAOTAssemblies ( ) { HomologousImageMode mode = HomologousImageMode.SuperSet; foreach (var aotDllName in AOTMetaAssemblyNames) { byte [] dllBytes = GetAssetData(aotDllName); LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode); Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName} . mode:{mode} ret:{err} " ); } } }
BuildAssetsCommand.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 using HybridCLR.Editor.Commands;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Text;using System.Threading.Tasks;using UnityEditor;using UnityEngine;namespace HybridCLR.Editor { public static class BuildAssetsCommand { public static string HybridCLRBuildCacheDir => Application.dataPath + "/HybridCLRBuildCache" ; public static string AssetBundleOutputDir => $"{HybridCLRBuildCacheDir} /AssetBundleOutput" ; public static string AssetBundleSourceDataTempDir => $"{HybridCLRBuildCacheDir} /AssetBundleSourceData" ; public static string GetAssetBundleOutputDirByTarget (BuildTarget target ) { return $"{AssetBundleOutputDir} /{target} " ; } public static string GetAssetBundleTempDirByTarget (BuildTarget target ) { return $"{AssetBundleSourceDataTempDir} /{target} " ; } public static string ToRelativeAssetPath (string s ) { return s.Substring(s.IndexOf("Assets/" )); } private static void BuildAssetBundles (string tempDir, string outputDir, BuildTarget target ) { Directory.CreateDirectory(tempDir); Directory.CreateDirectory(outputDir); List<AssetBundleBuild> abs = new List<AssetBundleBuild>(); { var prefabAssets = new List<string >(); string testPrefab = $"{Application.dataPath} /Prefabs/HotUpdatePrefab.prefab" ; prefabAssets.Add(testPrefab); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); abs.Add(new AssetBundleBuild { assetBundleName = "prefabs" , assetNames = prefabAssets.Select(s => ToRelativeAssetPath(s)).ToArray(), }); } BuildPipeline.BuildAssetBundles(outputDir, abs.ToArray(), BuildAssetBundleOptions.None, target); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); } public static void BuildAssetBundleByTarget (BuildTarget target ) { BuildAssetBundles(GetAssetBundleTempDirByTarget(target), GetAssetBundleOutputDirByTarget(target), target); } [MenuItem("HybridCLR/Build/BuildAssetsAndCopyToStreamingAssets" ) ] public static void BuildAndCopyABAOTHotUpdateDlls ( ) { CompileDllCommand.CompileDllActiveBuildTarget(); CopyAOTAssembliesToStreamingAssets(); CopyHotUpdateAssembliesToStreamingAssets(); AssetDatabase.Refresh(); } public static void CopyABAOTHotUpdateDlls (BuildTarget target ) { CopyAssetBundlesToStreamingAssets(target); CopyAOTAssembliesToStreamingAssets(); CopyHotUpdateAssembliesToStreamingAssets(); } public static void BuildSceneAssetBundleActiveBuildTargetExcludeAOT ( ) { BuildAssetBundleByTarget(EditorUserBuildSettings.activeBuildTarget); } public static void CopyAOTAssembliesToStreamingAssets ( ) { var target = EditorUserBuildSettings.activeBuildTarget; string aotAssembliesSrcDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target); string aotAssembliesDstDir = Application.dataPath + "/Editor/HybridCLR/HotDlls" ; foreach (var dll in LoadDll.AOTMetaAssemblyNames) { string srcDllPath = $"{aotAssembliesSrcDir} /{dll} " ; if (!File.Exists(srcDllPath)) { Debug.LogError($"ab中添加AOT补充元数据dll:{srcDllPath} 时发生错误,文件不存在。裁剪后的AOT dll在BuildPlayer时才能生成,因此需要你先构建一次游戏App后再打包。" ); continue ; } string dllBytesPath = $"{aotAssembliesDstDir} /{dll} .bytes" ; File.Copy(srcDllPath, dllBytesPath, true ); Debug.Log($"[CopyAOTAssembliesToStreamingAssets] copy AOT dll {srcDllPath} -> {dllBytesPath} " ); } } public static void CopyHotUpdateAssembliesToStreamingAssets ( ) { var target = EditorUserBuildSettings.activeBuildTarget; string hotfixDllSrcDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); string hotfixAssembliesDstDir = Application.dataPath + "/Editor/HybridCLR/HotDlls" ; foreach (var dll in SettingsUtil.HotUpdateAssemblyFiles) { string dllPath = $"{hotfixDllSrcDir} /{dll} " ; string dllBytesPath = $"{hotfixAssembliesDstDir} /{dll} .bytes" ; File.Copy(dllPath, dllBytesPath, true ); Debug.Log($"[CopyHotUpdateAssembliesToStreamingAssets] copy hotfix dll {dllPath} -> {dllBytesPath} " ); } } public static void CopyAssetBundlesToStreamingAssets (BuildTarget target ) { string streamingAssetPathDst = Application.streamingAssetsPath; Directory.CreateDirectory(streamingAssetPathDst); string outputDir = GetAssetBundleOutputDirByTarget(target); var abs = new string [] { "prefabs" }; foreach (var ab in abs) { string srcAb = ToRelativeAssetPath($"{outputDir} /{ab} " ); string dstAb = ToRelativeAssetPath($"{streamingAssetPathDst} /{ab} " ); Debug.Log($"[CopyAssetBundlesToStreamingAssets] copy assetbundle {srcAb} -> {dstAb} " ); AssetDatabase.CopyAsset( srcAb, dstAb); } } } }
增加热更测试代码
增加HotCodeLaunch.cs 作为代码热更启动入口
增加TestClass.cs 测试普通类的热更
增加TestMono.cs 测试mono类的热更
HotCodeLaunch.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using UnityEngine;public class HotCodeLaunch : MonoBehaviour { void Start ( ) { Debug.Log("======= HotCodeLaunch Start =======" ); TestClass testClass = new TestClass(); testClass.Test1(); GameObject testMono = new GameObject("TestMono" ); testMono.AddComponent<TestMono>(); } }
TestClass.cs 1 2 3 4 5 6 7 8 9 using UnityEngine;public class TestClass { public void Test1 ( ) { Debug.Log("======= TestClass Test1 =======" ); } }
TestMono.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 using System.Collections;using System.Collections.Generic;using UnityEngine;public class TestMono : MonoBehaviour { private int num = 5 ; void Start ( ) { Debug.Log("======= TestMono Start Hot =======" ); } void Update ( ) { if (num > 0 ) { Debug.LogFormat("======= TestMono Update Hot {0}=======" , num); num--; } } }
发布测试 android
切换到android平台。
生成热更代码,HybridCLD/Build/BuildAssetsAndCopyToStreamingAssets
,将生成的代码字节文件上传到服务器。
安装apk测试,
修改热更代码,HotCodeLaunch.cs、TestClass.cs、TestMono.cs 或新加代码等。
重新生成热更代码,将生成的代码字节文件上传到服务器。
重新运行apk测试,看是否是热更后的日志等。
ios
切换到ios平台
进入工程目录xxx/HybridCLRData/iOSBuild/
命令行执行bash ./build_libil2cpp.sh
,看build
目录下是否有libil2cpp.a
且大于60m,有就是成功,否则出错。
发布xcode项目
用新生成的libil2cpp.a
替换xcode项目Libraries
的libil2cpp.a
新工程测试 环境
Unity 2021.3.19f1
package的manifest.json 添加com.focus-creative-games.hybridclr_unity": "https://gitee.com/focus-creative-games/hybridclr_unity.git
,打开packagemanager查阅版本号Version 2.1.0
修改ProjectSetting
的Scripting Backend
改成IL2CPP
、Api Compatiblity Level
改为.NET Framework
安装hybridclr+il2cpp_plus
代码到本地目录,HybirdCLR->installer
,点击安装按钮
生成必要文件,HybridCLR->Generate->All
修改设置,如果打开设置界面失败,去例子工程复制HybridCLRSettings.asset
放入到ProjectSetting目录
构建一次项目,File->Build Settings
构建一个自己需要测试的平台的项目。
补充 安装HybridCLR package
打开unity的packageMgr
点击+号,Add package from git URL,将git或gitee的地址里面的git地址拷贝进去,会自行下载安装。
或者在:package目录的manifest.json
目录添加"com.focus-creative-games.hybridclr_unity": "https://gitee.com/focus-creative-games/hybridclr_unity.git",
错误 Editor代码引用了游戏代码会失败 将游戏代码复制一份到Editor,或先将Editor删除了
Mono.Linker.LinkerFatalErrorException: /Users/popolin/files/git/hhf/unity-go-mhsg/client/Assets/Scripts/BaseCode/GameLauncher.cs(22,9): error IL1005: .GameLauncher.Awake(): Error processing method ‘.GameLauncher.Awake()’ in assembly ‘Assembly-CSharp.dll’
问题出现在非热更代码使用到了热更代码。
方法1:删掉对应的xxx热更模块的Assembly。 方法2:删掉对应引用了热更模块的代码。 方法3:将引用了热更模块代码的代码都放到热更代码模块里面。
Exception: OBSOLETE - Providing Android resources in Assets/Plugins/Android/assets was removed, please move your resources to an AAR or an Android Library. See “AAR plug-ins and Android Libraries” section of the Manual for more details.
方法1:先删除对应的文件夹,等生成完成在加回来。
挂载载资源上的脚本出现Scrip Missing
方法1:资源需要打AssetBundle方式加载,不能直接放到Resource下加载。
相关地址
huatuo入门github hybridclr国内 hybridclr国外 hybridclr热更框架地址 hybridclr官网文档 HybridCLR入门代码改 https://pan.baidu.com/s/1uZz0ezccXskH3X3xQy6GVQ 提取吗prbs