Addressable热更新资源类型的疑问
1)Addressable热更新资源类型的疑问
2)本地删除FBX的DefaultMaterial在Unity重启后失效
3)如何实现MeshRenderer的效果
4)UGUI动态加载Item的DrawCall问题
5)Loading.CheckConsistency [Editor Only]编辑器上的优化问题
这是第250篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。
UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)
Addressable
Q:Addressable可以热更新Text/Xml等原始资源,不走AssetBundle吗?正在考虑是否使用Addressable作为热更新方案,有一些资源不想走AssetBundle,想直接读取,不知道Addressable是否支持?
A:Addressable目前没有直接支持Raw资源,基本都是走AssetBundle的。
一个思路就是把这些资源单独打AssetBundle,以AssetBundle为载体,逻辑上还是当成单独的Text,Binary去读取;另外一个思路就是做扩展改造。
可以看到有TextDataProvider这个Provider类,内部用“File.ReadAllText(path);”获取资源,因此其实可以去实现一下AddressableAssetGroupSchema和BuildScriptBase,做一个RawAssetSchema和BuildScriptRawAsset的扩展来打包。
感谢黄程@UWA问答社区提供了回答
Editor
Q:用了Addressables系统,打包AssetBundle有Default Material有冗余,在打包机上运行脚本把FBX的默认材质删除后变紫,但是重新打开工程后默认材质又回来了,导致冗余还在。(Unity版本2018.4.34)
删除材质的脚本:
using System; using UnityEngine; using System.Collections; using System.IO; using UnityEditor; using Object = UnityEngine.Object; class DisableMaterialImport : AssetPostprocessor { [MenuItem("Tools/Reimport All Model")] public static void ReimportAllModel() { var assetPaths = AssetDatabase.GetAllAssetPaths(); Array.Sort(assetPaths); Debug.LogWarning(string.Format("Total assets count: {0}", assetPaths.Length)); int processedCount = 0; foreach (string assetPath in assetPaths) { string normalizedAssetPath = assetPath.ToLower(); if (!normalizedAssetPath.EndsWith(".fbx") && !normalizedAssetPath.EndsWith(".obj") && !normalizedAssetPath.EndsWith(".3ds")) { continue; } var modelImporter = AssetImporter.GetAtPath(assetPath) as ModelImporter; if (modelImporter == null || modelImporter.importMaterials) { continue; } AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ImportRecursive | ImportAssetOptions.ForceUpdate); Debug.Log(assetPath, AssetDatabase.LoadMainAssetAtPath(assetPath)); processedCount++; } Debug.LogWarning(string.Format("Total processed model count: {0}", processedCount)); AssetDatabase.SaveAssets(); } private static void FixedModelImport(ModelImporter modelImporter, GameObject model) { //Debug.Log("FixedModelImport "+model); var renderers = model.GetComponentsInChildren<Renderer>(true); if (renderers == null) { return; } modelImporter.importMaterials = false; modelImporter.importBlendShapes = false; modelImporter.importAnimation = false; modelImporter.isReadable = false; modelImporter.optimizeMesh = true; foreach (var renderer in renderers) { if (renderer == null) { continue; } renderer.sharedMaterials = new Material[renderer.sharedMaterials.Length]; } /* var animator = model.GetComponent<Animator>(); if(animator!=null) Object.DestroyImmediate(animator);*/ }
A1:我这边试验过,不会变回来啊,会不会是把工程回滚了。另外这个脚本的ReimportAllModel只对本身就没有勾选Import Materials的模型有效。
感谢Prin@UWA问答社区提供了回答
A2:尝试多次,发现关掉Cache Server就好了。
感谢题主唐@UWA问答社区提供了回答
Editor
Q:我有个需求就是要将关卡里的对象通过脚本间接创建,而不是直接将Prefab拖到场景中。因此我需要能象MeshRenderer那样直接在场景中显示并方便我选中与编辑的。
代码类似这样:
public class SpawnPrefab : MonoBehaviour { public GameObject prefabEntity; void Start() { Instantiate(prefabEntity); } }
MeshRenderer有下面两个特性:
1. 在Editor模式下SceneView与GameView下面同时显示;
2. 在运行模式下绘制出的对象可以在SceneView下选中。
目前我是在OnDrawGizmos函数中调用Graphics.DrawMesh,但SceneView中却无法选中,在Update中绘制却在Editor模式下无法显示。我感觉好像尝试的方向错了。
A:OnDrawGizmos里面绘制显然是不行的,这个函数在打包出来Runtime都不会调用。
要在SceneView中选中编辑,一定得是场景中的某个GameObject。(用GameObject挂MeshRenderer肯定能满足“SceneView编辑”和“运行时脚本创建”两个需求的。)
如果题主的需求是一定要用Graphics的立即渲染接口来画Mesh,并且要在Editor下显示,那么给类加一个ExecuteAlways属性就可以了,只是还不能在SceneView中选中编辑。
如果还想实现Scene窗口下编辑功能,只能给要绘制的Mesh绑定一个场景中挂了MeshRenderer的GameObject。Graphics.DrawMesh绘制物体的Transform与这个GameObject保持一致就行了。如下图,Game窗口为OnPostRender函数中用Graphics.DrawMeshNow函数绘制的效果,Scene窗口中为绑定的用来编辑调整的物体,两者的Transform是一致的:
关键代码如下:
感谢Prin@UWA问答社区提供了回答
UGUI
Q:关于动态加载Item,这样是不是算是穿插了,如果有大量的Item那得多少DrawCall,我感觉我走入了误区,求解如何学习这方面,还是说实际工作中,都不去管这种?
我这里只是举了个例子,正常项目中元素不可能这么少,如果多,动态加载的如何合批?如果要单独写脚本来处理动态加载的Item中的元素,那可就太不合理了。例如NGUI使用过Depth来控制面板下元素的合批,我只了解UGUI是用摆放顺序来控制,如果只是这样,对动态加载的太不友好了。
A1:共享相同材质的网格,满足其它合批条件,以相邻顺序渲染即可合批,减少DrawCall。所以合批的关键,在于对使用相同材质的物体,控制渲染顺序。
合批条件和优化方案有以下资料:
https://blog.uwa4d.com/archives/optimzation_cpu.html
https://docs.unity3d.com/Manual/DrawCallBatching.htmlUnity中影响渲染顺序的因素:
https://zhuanlan.zhihu.com/p/55762351不同的资源控制渲染顺序的方式不同,如MeshRenderer,可以设置材质的RenderQueue;ParticleSystem可以设置Order In Layer和Sorting Layer等。
UGUI元素的渲染顺序是UGUI这个插件本身决定的,我们要做的是理解UGUI对元素渲染的排序方式,尽可能使相同材质的UI元素以相邻顺序渲染。具体的关键点譬如:避免同一层的UI元素互相重叠,UGUI是一层一层绘制的,保证相同材质的UI作为同一层来绘制。如题主项目中的Img都放在同一层,Txt都放在第二层。如果两个Img发生叠层,就会有一个Img是作为第二层来绘制的,导致Txt的绘制与Img的绘制互相穿插,就会增加DrawCall。
只要不重叠,使用相同图集的同层元素就会自动合批的。如果是复杂的界面就只能尽可能合并图集,叠层多的情况下,只要保证材质相同的元素使用相同图集,并以相邻顺序绘制就可以合批。UGUI里的图集一多,DrawCall就没什么好办法来控制了。
感谢Prin@UWA问答社区提供了回答
A2:只要Item的面积和另外一个Item的面积不重叠,那么所有的文本都算第二层,Image都算第一层,就2个DrawCall而已。如果怕被别的UI元素影响,可以让这些Item单独放在一个Canvas里面,这样就不会受到其它UI元素的影响了。
感谢Xuan@UWA问答社区提供了回答
Editor
Q:Loading.CheckConsistency [Editor Only]在编辑器上比较耗时,请问是做什么的,如何优化?
A:ReadObject实际上是在加载之后,对Object进行反序列化。一个Prefab反序列化后,会有大量的Object。IntegrateAllThreadedObjects会遍历这些Object,而Loading.CheckConsistency就是在遍历这些Object时,对数据进行一致性检验。
所谓一致性检验,就是比如,对下图Prefab的序列化文件,会检查两个红框中的fileID是否一致。
图片来源:
https://www.cnblogs.com/luguoshuai/p/12323186.html如这篇博文所讲,如果两个fileID不一致,会有CheckConsistency的报错。
为什么只在Editor下进行一致性检验,而打包后Runtime不需要检验呢?笔者推测是在打包的时候已经对所有对象都检验过了,Runtime就不需要检验,也避免了检验带来的高耗时。
证据如下图,某次打包的报错堆栈里面,包含了CheckConsistency的步骤:
图片来源:
https://networm.me/2019/06/23/unity-has-stopped-working/
感谢Prin@UWA问答社区提供了回答
封面图来源于:Pixel Sorting
https://lab.uwa4d.com/lab/5dd4587f8bab6aaf02db4018
今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。
官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)