Addressable热更新资源类型的疑问

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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/607cef2a6bb31032f9791381


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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/609906386bb31032f97915ae


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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/6096bc126bb31032f979157b


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.html

Unity中影响渲染顺序的因素:
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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/609935306bb31032f97915b2


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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/609cf2106bb31032f97916f2

封面图来源于:Pixel Sorting
https://lab.uwa4d.com/lab/5dd4587f8bab6aaf02db4018


今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)