我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Unity 接入HybridCLR的点点滴滴,亲测三平台(PC、Android、WebGL)妥妥的。-问题分享

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Unity 接入HybridCLR的点点滴滴,亲测三平台(PC、Android、WebGL)妥妥的。-问题分享

        

一入热更深似海啊,没有热更是真恼火啊,干啥啥不方便,动不动就得重新发包;说实在的,也是工作之余研究这个,在原有框架基础上接入这个热更,既要保持原有功能,还要支持热更,实实在在、断断续续搞了这么久,终于是接入并测通了,这一路是坎坎坷坷,下面把走过的弯弯绕绕记录下,希望对后来想接入的小伙伴有帮助吧。

        其实之前早就有动热更新的心,无奈现在没做游戏了,项目这块基本都是定制开发,所以这块一直搁置,不过之前有考虑过lua和xlua也看过ILRuntime,但是一直耿耿于怀,要用另外一种语言去搞,而且咱又是一直干C#的,而且这几个实现机制都是需要一个独立的vm,编译完在解释一套,而且有的还不能直接使用,需要特殊处理,看着看着就没心思搞了;这时看到了HybridCLR,说实话,一开始看到的是huatuo,这俩其实到后来了解到是一个东西,看了几天,发现这个的实现机制从根本上解决了独立vm的问题,具体的HybridCLR官网说的更清楚,使用了AOT + Interpreter混合运行方式,HybridCLR使得il2cpp变成一个全功能的runtime,原生(即通过System.Reflection.Assembly.Load)支持动态加载dll,这个就是亮点,在实现热更的路上,它占了绝对的分量。

        看到这里的,默认你已经在接入HybridCLR的路上了,而且已经接入,遇到问题了,如果是还没接入的,可以查看Unity 热更新 之 huatuo(HybridCLR) 和 Unity 热更新 HybridCLR 对接到项目中 了解和接入。

        这里使用的Unity版本是 2021.3.6f1 , HybridCLR是 v2.3.0 , il2cpp_plus版本是v2021-2.2.0一切准备就绪后,开始坎坷之路吧,Lz这里主要测试的平台有PC、Android、WebGL,感觉最明显的就是WebGL限制是真多,所以在这里爬坑也是绞尽脑汁了。

        所有常见的问题HybridCLR常见错误(这里是链接,可以跳过去看)里其实也都有,只是有的指出了方向,但具体怎么解决人家也没细细说明,必经错误千奇百怪,遇到了至少人家有个指引方向,剩下的就得靠自己慢慢爬了。

        别的不多说了,进入主题。

        问题:项目已经进入热更新,可以使用,但是想进一步使用热更新,热更项目中原有的dll。

        正常接入后,打包出来没问题,然后有要求,比如项目之前就有已经引用的dll或则框架要求,把已经统一的功能封装出去,最终形成dll在项目中引用使用,不管什么原因,你想实现已经引用的外部dll的热更新。

        在HybridCLR中,是有这块的功能的,

 这里需要配置外部搜索dll路径和需要热更dll的名称,注意:这里的设置一定是同时设置,这样热更新才能找到需要热更的dll在哪里,而且外部搜索dll路径是以Assets为父级(根目录)去找的,这里Lz直接放在了项目内,如果你放在项目外,就要填写项目外地址了。

 确定配置完成后,真正的问题就从此开始了。

1.报错:Building Library/Bee/artifacts/xxxx failed with output: Fatalerror in Unitiy CIL Linker Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly:'xxx'

这里Lz经历完爬坑后,总结先说下,他这里的配置,是将你选择的dll,在发布时,做了过滤,也就是不会打包到包中(AOT),也就意味着,你的AOT代码中,如果有对他们的引用,将会报错(Building Library/Bee/artifacts/xxxx failed with output: Fatalerror in Unitiy CIL Linker Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly:'xxx'),或者你的AOT中没有引用,但是你外部热更的dll中有引用,同样会报这个错,根本原因是,热更(HybridCLR)把你要外部热更的dll过滤掉了(相当于删除),所以在打包时找不到引用,你说能不报错吗?这里爬坑了好久,希望对你有帮助,一定要冷静分析你要热更的dll中有没有其他地方在引用(如果是AOT那就必须分离掉那块功能,如果是外部dll,要么一起加入热更,要么把那块功能分离掉)。这个错误部分平台,都适用。

2.报错:WebGL平台打包时遇到 undefine symbol: send file 之类的错误

这个就纯属WebGL平台限制问题,一般主要是本地读取的一些问题,比如File操作,这里LZ建议都是用加载(UntyWebRequest)的方式(那个平台都适用,不会有问题),只是在这要注意的是,不同平台在使用网络加载的时候,要加入平台前缀,不然会加载失败 提示错误:Cannotconnect to destination host等(比如Android要加jar:file://   PC要加file://)

3.注意一点,WebGL平台必须使用il2cpp的全局安装,这块官网说的比较明确,而且处理方法也很详细可以按照流程操作.如果还不懂的可以直接看官网这个错误:打包WebGL平台出现 build.js: undefined symbol: RuntimeApi_LoadMetadataForAOTAssembly (referenced by top-level compiled C/C++ code)的解决办法。

4.运行时,报错资源挂在的脚本丢失了ScriptMissing

排除了你版本的问题,最大的可能就是你的热更新程序集没有加载,或者你在加载使用时,程序集还没有加载,要看下逻辑是不是有问题。

5.运行时,遇到Unity: TypeLoadException: Could not load type 'XxxType' from assembly 'yyyAssembly'

一般正常操作,.net必须使用.net4.x,然后最有可能的就是你程序集加载的顺序的问题呢,比如,如果A依赖于B,那你应该先加载B,再加载A,你加载顺序反了也会报这个错,毕竟代码的执行顺序都懂的,有依赖关系,一定是先加载被依赖的。

6.运行时,报错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. 

是因为在加载场景时,没有找到对应的场景文件,第一种时把场景文件打包为ab包加载,第二种则是把场景加入到BuildSetting中,两者看起来都能明白,但是在实际操作时,遇到问题时,肯定首先查过了,这两种都是不在问题发生范围内,那究竟时什么原因呢。其实第二种情况比较好理解,如果你不想热更你的场景,那就把场景直接拖入的的BuildSetting中,打包出来,正常切换场景是没问题的,但是,既然做了热更,你肯定不想这么做,那唯一的办法就是把场景打成ab包,但是打成ab包后,在Editor模式下加载没问题,发布出来了,就找不到场景了,(仅个人推测啊,在Editor模式下,加载可以不区分大小写,但是发布出来以后,是区分大小写的,这点注意下,就是你ab包的名称和场景名称要统一),有个加载方式是加载ab包后,有个ab.GetAllScenePaths()[0];可以读到场景文件的整个路径,在加载场景时,传入这个整个路径是不会出现问题的,而且可以加载成功,(这里lz遇到了,名称一摸一样,但是就是加载不出来,提示not been load,因为一般的做法要么根据id加载,要么跟据场景名称加载,特殊情况,开始要读完整路径加载),这个问题到这里就完结了,希望你这么操作也能加载成功。

7. 找不到引用库 resolve AOT dll:{assemblyName} 失败! 请确保主工程已经引用了该dll并且正确生成了裁剪后的AOT dll。更多请参阅常见错误文档。

如果你是hbyridclr v3.3.0,那么你在执行HybridCLR-》Generat下载Aot或者Bridge时,就是生成aot和建立桥接的时候,汇报这个错误,时因为hybridclr找不到你配置的外部热更新dll,按照官网指示,lz这里主工程里有引用,正常build也是可以的,但就是报这个,一番跟踪源码,才知道他是在aot时,通过dnlib的刷新项目内的程序集(assembly),找到了你的外部引用dll,但是在处理

asseblyPathResolver时, 并没有把我们的dll在aot时,放到他能处理的aot目录(HybridCLRData\AssembliesPostIl2CppStrip\StandaloneWindows64),StandaloneWindows64是对应平台的目录,会有Android、WebGL等,这里需要把报错的dll手动给让复制到这个目录下,就ok了 。

修改源码,实现自动拷贝

需要修改HybridCLR.Editor.Commands下的StripAOTDllCommand.cs

第一步,添加方法CopyIDEDllAotAssemblies()

///         /// 拷贝自定义 热更新下的dll到 hybridclr可识别的aot目录下        ///         private static void CopyIDEDllAotAssemblies(BuildTarget target)        {            var externalDirs = HybridCLRSettings.Instance.externalHotUpdateAssembliyDirs;            var dstPath = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);            List 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),一直遇到这个问题,就是在hybridclr配置过的程序集,他会处理,但是如果你是想更新原本项目中已有的dll,并不是项目开发中创建的程序集,就会遇到这个问题,hybridclr是没有记录这些外部热更新dll逻辑的,虽然他们在设置界面里留了配置外部更新dll的位置,但是在拷贝到aot目录或者hotfix目录时,没有相应逻辑的.

如果你是hybridclr v2.3.0,而且你想热更项目原有的应用dll,在配置了外部热更新dll后,hybridclr并没有把你配置的热更新dll放到他们能识别的热更新dll目录下,导致你在过滤热更新后,找不到你外部的dll,这里lz翻源码,添加了逻辑,拷贝到他们能识别的目录下

修改源码,拷贝我们外部引用的dll到他们可识别的目录下:

第一步:修改HybridCLR.Editor.Link下的Analyzer.cs

添加方法

public void CopyHotAssemblyToHotDllDir(List 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 class="lazy" data-srcPath = dnAssPath;    string tarPath = $"{SettingsUtil.GetHotUpdateDllsOutputDirByTarget(EditorUserBuildSettings.activeBuildTarget)}/{hotupaName}.dll";    File.Copy(class="lazy" data-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 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就可被识别到了,执行一键拷贝时就不会报错了。

9.WebGL平台报错:Could not produce class With ID 81.

在PlayerSetting中,要取消勾选 Strip Engine Code 

 

来源地址:https://blog.csdn.net/u013774978/article/details/131565741

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Unity 接入HybridCLR的点点滴滴,亲测三平台(PC、Android、WebGL)妥妥的。-问题分享

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录