????????
一入熱更深似海啊,沒有熱更是真惱火啊,干啥啥不方便,動(dòng)不動(dòng)就得重新發(fā)包;說實(shí)在的,也是工作之余研究這個(gè),在原有框架基礎(chǔ)上接入這個(gè)熱更,既要保持原有功能,還要支持熱更,實(shí)實(shí)在在、斷斷續(xù)續(xù)搞了這么久,終于是接入并測(cè)通了,這一路是坎坎坷坷,下面把走過的彎彎繞繞記錄下,希望對(duì)后來想接入的小伙伴有幫助吧。
? ? ? ? 其實(shí)之前早就有動(dòng)熱更新的心,無奈現(xiàn)在沒做游戲了,項(xiàng)目這塊基本都是定制開發(fā),所以這塊一直擱置,不過之前有考慮過lua和xlua也看過ILRuntime,但是一直耿耿于懷,要用另外一種語言去搞,而且咱又是一直干C#的,而且這幾個(gè)實(shí)現(xiàn)機(jī)制都是需要一個(gè)獨(dú)立的vm,編譯完在解釋一套,而且有的還不能直接使用,需要特殊處理,看著看著就沒心思搞了;這時(shí)看到了HybridCLR,說實(shí)話,一開始看到的是huatuo,這倆其實(shí)到后來了解到是一個(gè)東西,看了幾天,發(fā)現(xiàn)這個(gè)的實(shí)現(xiàn)機(jī)制從根本上解決了獨(dú)立vm的問題,具體的HybridCLR官網(wǎng)說的更清楚,使用了AOT + Interpreter
混合運(yùn)行方式,HybridCLR使得il2cpp變成一個(gè)全功能的runtime,原生(即通過System.Reflection.Assembly.Load)支持動(dòng)態(tài)加載dll,這個(gè)就是亮點(diǎn),在實(shí)現(xiàn)熱更的路上,它占了絕對(duì)的分量。
? ? ? ? 看到這里的,默認(rèn)你已經(jīng)在接入HybridCLR的路上了,而且已經(jīng)接入,遇到問題了,如果是還沒接入的,可以查看Unity 熱更新 之 huatuo(HybridCLR) 和?Unity 熱更新 HybridCLR 對(duì)接到項(xiàng)目中?了解和接入。
? ? ? ? 這里使用的Unity版本是 2021.3.6f1 , HybridCLR是 v2.3.0 , il2cpp_plus版本是v2021-2.2.0一切準(zhǔn)備就緒后,開始坎坷之路吧,Lz這里主要測(cè)試的平臺(tái)有PC、Android、WebGL,感覺最明顯的就是WebGL限制是真多,所以在這里爬坑也是絞盡腦汁了。
? ? ? ? 所有常見的問題HybridCLR常見錯(cuò)誤(這里是鏈接,可以跳過去看)里其實(shí)也都有,只是有的指出了方向,但具體怎么解決人家也沒細(xì)細(xì)說明,必經(jīng)錯(cuò)誤千奇百怪,遇到了至少人家有個(gè)指引方向,剩下的就得靠自己慢慢爬了。
????????別的不多說了,進(jìn)入主題。
? ? ? ? 問題:項(xiàng)目已經(jīng)進(jìn)入熱更新,可以使用,但是想進(jìn)一步使用熱更新,熱更項(xiàng)目中原有的dll。
? ? ? ? 正常接入后,打包出來沒問題,然后有要求,比如項(xiàng)目之前就有已經(jīng)引用的dll或則框架要求,把已經(jīng)統(tǒng)一的功能封裝出去,最終形成dll在項(xiàng)目中引用使用,不管什么原因,你想實(shí)現(xiàn)已經(jīng)引用的外部dll的熱更新。
? ? ? ? 在HybridCLR中,是有這塊的功能的,
?這里需要配置外部搜索dll路徑和需要熱更dll的名稱,注意:這里的設(shè)置一定是同時(shí)設(shè)置,這樣熱更新才能找到需要熱更的dll在哪里,而且外部搜索dll路徑是以Assets為父級(jí)(根目錄)去找的,這里L(fēng)z直接放在了項(xiàng)目內(nèi),如果你放在項(xiàng)目外,就要填寫項(xiàng)目外地址了。
?確定配置完成后,真正的問題就從此開始了。
1.報(bào)錯(cuò):Building Library/Bee/artifacts/xxxx failed with output: Fatalerror in Unitiy CIL Linker Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly:'xxx'
這里L(fēng)z經(jīng)歷完爬坑后,總結(jié)先說下,他這里的配置,是將你選擇的dll,在發(fā)布時(shí),做了過濾,也就是不會(huì)打包到包中(AOT),也就意味著,你的AOT代碼中,如果有對(duì)他們的引用,將會(huì)報(bào)錯(cuò)(Building Library/Bee/artifacts/xxxx failed with output: Fatalerror in Unitiy CIL Linker Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly:'xxx'),或者你的AOT中沒有引用,但是你外部熱更的dll中有引用,同樣會(huì)報(bào)這個(gè)錯(cuò),根本原因是,熱更(HybridCLR)把你要外部熱更的dll過濾掉了(相當(dāng)于刪除),所以在打包時(shí)找不到引用,你說能不報(bào)錯(cuò)嗎?這里爬坑了好久,希望對(duì)你有幫助,一定要冷靜分析你要熱更的dll中有沒有其他地方在引用(如果是AOT那就必須分離掉那塊功能,如果是外部dll,要么一起加入熱更,要么把那塊功能分離掉)。這個(gè)錯(cuò)誤部分平臺(tái),都適用。
2.報(bào)錯(cuò):WebGL平臺(tái)打包時(shí)遇到 undefine symbol: send file 之類的錯(cuò)誤
這個(gè)就純屬WebGL平臺(tái)限制問題,一般主要是本地讀取的一些問題,比如File操作,這里L(fēng)Z建議都是用加載(UntyWebRequest)的方式(那個(gè)平臺(tái)都適用,不會(huì)有問題),只是在這要注意的是,不同平臺(tái)在使用網(wǎng)絡(luò)加載的時(shí)候,要加入平臺(tái)前綴,不然會(huì)加載失敗 提示錯(cuò)誤:Cannotconnect to destination host等(比如Android要加jar:file://? ?PC要加file://)
3.注意一點(diǎn),WebGL平臺(tái)必須使用il2cpp的全局安裝,這塊官網(wǎng)說的比較明確,而且處理方法也很詳細(xì)可以按照流程操作.如果還不懂的可以直接看官網(wǎng)這個(gè)錯(cuò)誤:打包WebGL平臺(tái)出現(xiàn)?build.js: undefined symbol: RuntimeApi_LoadMetadataForAOTAssembly (referenced by top-level compiled C/C++ code)的解決辦法。
4.運(yùn)行時(shí),報(bào)錯(cuò)資源掛在的腳本丟失了ScriptMissing
排除了你版本的問題,最大的可能就是你的熱更新程序集沒有加載,或者你在加載使用時(shí),程序集還沒有加載,要看下邏輯是不是有問題。
5.運(yùn)行時(shí),遇到Unity: TypeLoadException: Could not load type 'XxxType' from assembly 'yyyAssembly'
一般正常操作,.net必須使用.net4.x,然后最有可能的就是你程序集加載的順序的問題呢,比如,如果A依賴于B,那你應(yīng)該先加載B,再加載A,你加載順序反了也會(huì)報(bào)這個(gè)錯(cuò),畢竟代碼的執(zhí)行順序都懂的,有依賴關(guān)系,一定是先加載被依賴的。
6.運(yùn)行時(shí),報(bào)錯(cuò)couldn't be loaded because it has not been added to the build settings or the AssetBundle has not been loaded. 或者?couldn’t be loaded because it has not been added to the build settings.?
是因?yàn)樵诩虞d場景時(shí),沒有找到對(duì)應(yīng)的場景文件,第一種時(shí)把場景文件打包為ab包加載,第二種則是把場景加入到BuildSetting中,兩者看起來都能明白,但是在實(shí)際操作時(shí),遇到問題時(shí),肯定首先查過了,這兩種都是不在問題發(fā)生范圍內(nèi),那究竟時(shí)什么原因呢。其實(shí)第二種情況比較好理解,如果你不想熱更你的場景,那就把場景直接拖入的的BuildSetting中,打包出來,正常切換場景是沒問題的,但是,既然做了熱更,你肯定不想這么做,那唯一的辦法就是把場景打成ab包,但是打成ab包后,在Editor模式下加載沒問題,發(fā)布出來了,就找不到場景了,(僅個(gè)人推測(cè)啊,在Editor模式下,加載可以不區(qū)分大小寫,但是發(fā)布出來以后,是區(qū)分大小寫的,這點(diǎn)注意下,就是你ab包的名稱和場景名稱要統(tǒng)一),有個(gè)加載方式是加載ab包后,有個(gè)ab.GetAllScenePaths()[0];可以讀到場景文件的整個(gè)路徑,在加載場景時(shí),傳入這個(gè)整個(gè)路徑是不會(huì)出現(xiàn)問題的,而且可以加載成功,(這里lz遇到了,名稱一摸一樣,但是就是加載不出來,提示not been load,因?yàn)橐话愕淖龇ㄒ锤鶕?jù)id加載,要么跟據(jù)場景名稱加載,特殊情況,開始要讀完整路徑加載),這個(gè)問題到這里就完結(jié)了,希望你這么操作也能加載成功。
7. 找不到引用庫 resolve AOT dll:{assemblyName} 失敗! 請(qǐng)確保主工程已經(jīng)引用了該dll并且正確生成了裁剪后的AOT dll。更多請(qǐng)參閱常見錯(cuò)誤文檔。
如果你是hbyridclr v3.3.0,那么你在執(zhí)行HybridCLR-》Generat下載Aot或者Bridge時(shí),就是生成aot和建立橋接的時(shí)候,匯報(bào)這個(gè)錯(cuò)誤,時(shí)因?yàn)閔ybridclr找不到你配置的外部熱更新dll,按照官網(wǎng)指示,lz這里主工程里有引用,正常build也是可以的,但就是報(bào)這個(gè),一番跟蹤源碼,才知道他是在aot時(shí),通過dnlib的刷新項(xiàng)目內(nèi)的程序集(assembly),找到了你的外部引用dll,但是在處理
asseblyPathResolver時(shí), 并沒有把我們的dll在aot時(shí),放到他能處理的aot目錄(HybridCLRData\AssembliesPostIl2CppStrip\StandaloneWindows64),StandaloneWindows64是對(duì)應(yīng)平臺(tái)的目錄,會(huì)有Android、WebGL等,這里需要把報(bào)錯(cuò)的dll手動(dòng)給讓復(fù)制到這個(gè)目錄下,就ok了 。
修改源碼,實(shí)現(xiàn)自動(dòng)拷貝
需要修改HybridCLR.Editor.Commands下的StripAOTDllCommand.cs
第一步,添加方法CopyIDEDllAotAssemblies()
/// <summary>
/// 拷貝自定義 熱更新下的dll到 hybridclr可識(shí)別的aot目錄下
/// </summary>
private static void CopyIDEDllAotAssemblies(BuildTarget target)
{
var externalDirs = HybridCLRSettings.Instance.externalHotUpdateAssembliyDirs;
var dstPath = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
List<string> allHotUpdateDllNames = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved;
foreach (var dir in externalDirs)
{
DirectoryInfo root = new DirectoryInfo(dir);
FileInfo[] files = root.GetFiles("*.dll");
foreach (var fp in files)
{
string fileouExt = Path.GetFileNameWithoutExtension(fp.FullName);
string file = fp.Name;
if (allHotUpdateDllNames.Contains(fileouExt))
{
Debug.Log($"[CopyIDEDllAotAssemblies] 過濾熱更新assembly:{file}");
continue;
}
Debug.Log($"[CopyIDEDllAotAssemblies] copy strip dll :{fp.FullName} ===>>> :{dstPath}/{file}");
File.Copy($"{fp.FullName}", $"{dstPath}/{file}", true);
}
}
}
第二步,在?GenerateStripedAOTDlls()方法中,添加該方法的引用
public static void GenerateStripedAOTDlls()
{
GenerateStripedAOTDlls(EditorUserBuildSettings.activeBuildTarget, EditorUserBuildSettings.selectedBuildTargetGroup);
CopyIDEDllAotAssemblies(EditorUserBuildSettings.activeBuildTarget);
}
8. FileNotFoundException: Could not find file 'xxx/HybridCLRData\HotUpdateDlls\StandaloneWindows64\xxx.dll'.
這塊是hybridclr處理aot代碼庫邏輯問題,目前(lz從v2.3.0 到 3.3.0),一直遇到這個(gè)問題,就是在hybridclr配置過的程序集,他會(huì)處理,但是如果你是想更新原本項(xiàng)目中已有的dll,并不是項(xiàng)目開發(fā)中創(chuàng)建的程序集,就會(huì)遇到這個(gè)問題,hybridclr是沒有記錄這些外部熱更新dll邏輯的,雖然他們?cè)谠O(shè)置界面里留了配置外部更新dll的位置,但是在拷貝到aot目錄或者h(yuǎn)otfix目錄時(shí),沒有相應(yīng)邏輯的.
如果你是hybridclr v2.3.0,而且你想熱更項(xiàng)目原有的應(yīng)用dll,在配置了外部熱更新dll后,hybridclr并沒有把你配置的熱更新dll放到他們能識(shí)別的熱更新dll目錄下,導(dǎo)致你在過濾熱更新后,找不到你外部的dll,這里lz翻源碼,添加了邏輯,拷貝到他們能識(shí)別的目錄下
修改源碼,拷貝我們外部引用的dll到他們可識(shí)別的目錄下:
第一步:修改HybridCLR.Editor.Link下的Analyzer.cs
添加方法
public void CopyHotAssemblyToHotDllDir(List<string> rootAssemblies)
{
using (var assCollector = new AssemblyCache(_resolver))
{
var gs = HybridCLRSettings.Instance;
foreach (string rootAss in rootAssemblies)
{
string dnAssPath = assCollector.GetAssemblyPath(rootAss);
if (gs.hotUpdateAssemblies != null && gs.hotUpdateAssemblies.Length> 0)
{
foreach (string hotupaName in gs.hotUpdateAssemblies)
{
if(hotupaName == rootAss)
{
Debug.Log($"熱更新中找到了外部dll:{hotupaName} path:{dnAssPath}");
string srcPath = dnAssPath;
string tarPath = $"{SettingsUtil.GetHotUpdateDllsOutputDirByTarget(EditorUserBuildSettings.activeBuildTarget)}/{hotupaName}.dll";
File.Copy(srcPath, tarPath, true);
}
}
}
}
assCollector.Dispose();
}
?第二步,修改HybridCLR.Editor.Meta下的AssemblyCache.cs
添加方法
public string GetAssemblyPath(string assembName)
{
return _assemblyPathResolver.ResolveAssembly(assembName, true);
}
第三步,修改HybridCLR.Editor.Commands下的LinkGeneratorCommand.cs
在 GenerateLinkXml方法中 添加一句代碼analyzer.CopyHotAssemblyToHotDllDir(hotfixAssemblies);
public static void GenerateLinkXml(BuildTarget target)
{
var ls = SettingsUtil.HybridCLRSettings;
List<string> hotfixAssemblies = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved;
var analyzer = new Analyzer(MetaUtil.CreateHotUpdateAndAOTAssemblyResolver(target, hotfixAssemblies));
var refTypes = analyzer.CollectRefs(hotfixAssemblies);
analyzer.CopyHotAssemblyToHotDllDir(hotfixAssemblies);
Debug.Log($"[LinkGeneratorCommand] hotfix assembly count:{hotfixAssemblies.Count}, ref type count:{refTypes.Count} output:{Application.dataPath}/{ls.outputLinkFile}");
var linkXmlWriter = new LinkXmlWriter();
linkXmlWriter.Write($"{Application.dataPath}/{ls.outputLinkFile}", refTypes);
AssetDatabase.Refresh();
}
到此ok了,我們外部的dll就可被識(shí)別到了,執(zhí)行一鍵拷貝時(shí)就不會(huì)報(bào)錯(cuò)了。
9.WebGL平臺(tái)報(bào)錯(cuò):Could not produce class With ID 81.
在PlayerSetting中,要取消勾選 Strip Engine Code?文章來源:http://www.zghlxwxcb.cn/news/detail-658788.html
?文章來源地址http://www.zghlxwxcb.cn/news/detail-658788.html
到了這里,關(guān)于Unity 接入HybridCLR的點(diǎn)點(diǎn)滴滴,親測(cè)三平臺(tái)(PC、Android、WebGL)妥妥的。-問題分享的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!