侑虎科技

侑虎科技 查看完整档案

上海编辑  |  填写毕业院校  |  填写所在公司/组织 www.uwa4d.com 编辑
编辑

简单优化,优化简单~

个人动态

侑虎科技 发布了文章 · 11月20日

关于Camera.activeTexture和Camera.targetTexture的疑问

1)关于Camera.activeTexture和Camera.targetTexture的疑问
​2)如何降低动画文件的浮点数精度
3)使用EasyMovieTexture在Android播放视频失败
4)模型顶点色(Color)如何去除
5)多层3D场景寻路方案探讨


这是第227篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

Rendering

Q:一直有个疑惑,Camera.activeTexture和Camera.targetTexture在Get的时候,Get到的是一个RenderTexture吗?

我知道这两个的区别是一个只能获得当前所正在渲染的RenderTexture,一个是既可以获得也可以设置渲染目标,但这样activeTexture有什么必要性呢?

是我对这两个的理解有错误吗?还有大家平时说的Backbuffer,在我渲染目标一直是屏幕的时候,这两个方法获得的都是Backbuffer吗?

A:第一个问题:Camera.targetTexture必须在相机指定Render Target才会非null,Camera.activeTexture获取到的值和调用的时机有关系。官方文档中写着可以在OnPostRender中获取到,比如使用了后处理,在OnPostRender中可以获取到后处理申请的RenderTexture,在Update中获取就是null。如果给相机设置了RenderTexture,那么targetTexture就是设置的RenderTexture。如果没有开启HDR,在OnPostRender中activeTexture获取到的也是这个RenderTexture。如果开启了HDR,则是一个另外申请的RenderTexture。

第二个问题:推荐使用RenderDoc查看渲染状态,分为两种情况,在安卓平台上开启了Always Blit和没开启会不一样。开启了Always Blit,会在最后多一个Present的过程,在Present之前,会将所有的渲染渲染到一个叫tex的RenderTexture上(这里不考虑后处理以及HDR导致生成了额外的RenderTexture),在Present的时候将tex Copy到Backbuffer里面。如果没有开启AlwaysBlit,比如Blit Type是Never,那么所有的渲染都是直接渲染到Backbuffer的(这里同样不考虑RenderTexture的情况),Blit Type为Auto的时候没有遇到出现Present的情况,在线性空间也没出现Present 。Backbuffer和那个用来Present的tex这两个对象都可以理解为系统提供的,从C#脚本中是没法获取到的,而且从Profiler里面也是看不到这两个对象的大小的。

总结起来就是:大部分情况下Camera.targetTexture都是为null的,除非相机设置了RenderTarget。

对于Camera.activeTexture,这个需要看调用时机,在OnPostRender中调用,如果有使用后处理或者HDR,则可以获取到申请到的RenderTexture,在Update里面获取是null的,其他的函数阶段没有详细测试过。

对于Backbuffer,这个系统是提供的,C#脚本是获取不到的。而且不一定是直接绘制到Backbuffer,就算没有使用后处理或者HDR,如果开启了Always Blit,也是会多一个中间层,在最后阶段中间层会Copy到Backbuffer。

感谢Xuan@UWA问答社区提供了回答


Animation

Q:动画文件后处理可以做两件事,精度压缩,Scale曲线剔除。比起用工具修改原始FBX文件,这样比较灵活。

实际测试,在开启Optimal压缩的情况下,加上这个后处理,能再节省40%左右。

示例代码和测试结果见知乎专栏:
https://zhuanlan.zhihu.com/p/27378492

void OnPostprocessModel(GameObject g) 
{
    // for skeleton animations.

        List<AnimationClip> animationClipList = new List<AnimationClip>(AnimationUtility.GetAnimationClips(g));
        if (animationClipList.Count == 0) {
            AnimationClip[] objectList = UnityEngine.Object.FindObjectsOfType (typeof(AnimationClip)) as AnimationClip[];
            animationClipList.AddRange(objectList);
        }

        foreach (AnimationClip theAnimation in animationClipList)
        {

            try 
            {
                //去除scale曲线
                foreach (EditorCurveBinding theCurveBinding in AnimationUtility.GetCurveBindings(theAnimation))
                {
                    string name = theCurveBinding.propertyName.ToLower();
                    if (name.Contains("scale"))
                    {
                        AnimationUtility.SetEditorCurve(theAnimation, theCurveBinding, null);
                    }
                } 

                //浮点数精度压缩到f3
                AnimationClipCurveData[] curves = null;
                curves = AnimationUtility.GetAllCurves(theAnimation);
                Keyframe key;
                Keyframe[] keyFrames;
                for (int ii = 0; ii < curves.Length; ++ii)
                {
                    AnimationClipCurveData curveDate = curves[ii];
                    if (curveDate.curve == null || curveDate.curve.keys == null)
                    {
                        //Debug.LogWarning(string.Format("AnimationClipCurveData {0} don't have curve; Animation name {1} ", curveDate, animationPath));
                        continue;
                    }
                    keyFrames = curveDate.curve.keys;
                    for (int i = 0; i < keyFrames.Length; i++)
                    {
                        key = keyFrames[i];
                        key.value = float.Parse(key.value.ToString("f3"));
                        key.inTangent = float.Parse(key.inTangent.ToString("f3"));
                        key.outTangent = float.Parse(key.outTangent.ToString("f3"));
                        keyFrames[i] = key;
                    }
                    curveDate.curve.keys = keyFrames;
                    theAnimation.SetCurve(curveDate.path, curveDate.type, curveDate.propertyName, curveDate.curve);
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError(string.Format("CompressAnimationClip Failed !!! animationPath : {0} error: {1}", assetPath, e));
            }
        }

} 
A1:压的是文本文件的大小,只是编辑器下更快,不论怎么处理,内存还是一个Float。

感谢海的味道@UWA问答社区提供了回答

A2:Mecanim的动画系统的压缩确实不是靠改变Float类型来达到的,而是通过降低数值位数后,可以产生更多的Const曲线,从而让引擎达到更高效存储的效果,进而达到所谓的“压缩”结果。

该问答由UWA提供

A3:裁剪精度产生更多Const Curve,达到缩小体积的目的,这个可以理解。但除非动画曲线是动捕出来的,有大量抖动的细节,否则这样降低的内存很有限。而且统一暴力降精度在很多时候并不可取。

Unity官方的减帧算法是一套参数针对所有骨骼的,比较低效。推荐细化处理:针对不同关节,设置不同的误差阈值,手动做Linear Key Reduction。越靠近根骨骼,所需精度越高,允许的误差阈值越小。越靠近末端骨骼,误差阈值越大。对Position和Rotation Curve,使用不同的误差度量方式,面部、手指等关键骨骼再加额外配置。

这样可以有效大幅减小Clip内存和包体大小,尤其对于动捕产生的动画。脚本处理方法仅适用于.anim文件,且由于会产生额外的EditorCurve,在处理完后资产大小可能会不减反增。但最终EditorCurve会合并入Curve,AssetBundle和运行时内存是会减小的。

CompressAnimationClips.cs

感谢zhangji@UWA问答社区提供了回答


Video

Q:使用EasyMovieTexture在Android播放视频失败,报错信息如下:

AndroidJavaException: java.lang.IllegalStateException: Unable to update texture contents (see logcat for details)
java.lang.IllegalStateException: Unable to update texture contents (see logcat for details)
android.graphics.SurfaceTexture.nativeUpdateTexImage(Native Method)
android.graphics.SurfaceTexture.updateTexImage(SurfaceTexture.java:248)
com.EasyMovieTexture.EasyMovieTexture.UpdateVideoTexture(EasyMovieTexture.java:331)
com.unity3d.player.UnityPlayer.nativeRender(Native Method)
com.unity3d.player.UnityPlayer.access600(Unknown Source:0)
com.unity3d.player.UnityPlayer600(UnknownSource:0)com.unity3d.player.UnityPlayerf1.handleMessage(Unknown Source:150) android.os.Handler.dispatchMessage(Handler.java:106)android.os.Looper.loop(Looper.java:219)
com.unity3d.player.UnityPlayer1.handleMessage(UnknownSource:150)android.os.Handler.dispatchMessage(Handler.java:106)android.os.Looper.loop(Looper.java:219)com.unity3d.player.UnityPlayerf.run(Unknown Source:20)
UnityEngine.AndroidJNISafe.CheckException () (at <3c22a197ab60454cb70124c69f2248be>:0)
UnityEngine.AndroidJNISafe.CallVoidMethod (System.IntPtr obj, System.IntPtr methodID, UnityEngine.jvalue[] args) (at <3c22a197ab60454cb70124c69f2248be>:0)
UnityEngine.AndroidJavaObject._Call (System.String methodName, System.Object[] args) (at <3c22a197ab60454cb70124c69f2248be>:0)
UnityEngine.AndroidJavaObject.Call (System.String methodName, System.Object[] args) (at <3c22a197ab60454cb70124c69f2248be>:0)
MediaPlayerCtrl.Call_UpdateVideoTexture () (at :0)
MediaPlayerCtrl.Update () (at :0)

A1:视频播放建议使用AssetStore中的AVProVideo,功能更强且兼容性更好一些。

感谢范君@UWA问答社区提供了回答

A2:建议使用自带的视频播放,兼容性更好。

感谢Robot.Huang@UWA问答社区提供了回答

A3:我之前遇到过一个EasyMovieTexture的问题,而且只出现在某个小米手机上,我们最后把视频格式改为mp4,码率我记得也改的跟EasyMovieTexture示例的视频一样然后就好了。

ps:你可以先用示例的视频测试一下,如果同样播放不了,那就是其他原因了。

感谢lanyt@UWA问答社区提供了回答

A4:直接用官方自己的VideoPlayer,如下图:

感谢小辉辉-177865@UWA问答社区提供了回答


Mesh

Q:在UWA中的模型分析页面中有关于模型顶点色的信息,得知如果不需要此数据,最好在Max中不要导出此属性,那么如果清除此属性呢?

我尝试过新建一个新的模型,这时此模型是不带顶点色属性的,一旦修改顶点色后就无法再清除此属性了,求解。

A1:感谢kent的回答,但是他提供的Demo内的exe我这里打开会报错,自己尝试从FBXSDK导出了一个exe。

FbxSDK官网下载,根据VS的使用版本来下载就可以,不影响下面的教程。

FbxSDK与VisualStudio环境搭建

这个教程会教你打开一个Samples,随便打开一个Sample,然后把Kent提供的main.cxx内容替换到同名文件内,编译出exe就好。

这里提供一个自己编译的,需要配合Kent提供的Demo使用:

ClearUVClr.exe

感谢高松@UWA问答社区提供了回答

A2:我在使用的时候发现有的Fbx文件替换无效,经查是main文件中

只获取了第一个节点,所以我改成遍历获取

这里提供main文件和exe文件:
main.cxx

ClearUVClr.exe

感谢Lim@UWA问答社区提供了回答


Script


Q:目前面临一个寻路方案的需求,期望有相关经验的朋友探讨一下各种实现方案的优劣和可行性,开阔下思路。

需求抽象之后大致如下:
1. 3D多层场景,但是场景不会很大,几百米乘以几百米以内吧;
2. 场景分层,高度上可能有多层重叠,层之间有地面阻挡,挖空的地方有各种穿梭方式;
3. 场景内有箱子、小巷之类的阻挡与道路;
4. 玩家可能会有类似轻功的飞行移动方式。

3D方面可选的方案不多,比如Navmesh、体素化、纯物理,目前比较倾向于体素化的方案,纯物理的目前测试下来感觉性能消耗有点不能接受。当然需求方面也有一些可以取舍的,想看看各位有经验的朋友有什么好的想法和建议。

A:最近看到一些3D寻路的解决方案想到这个问题,挂靠一下:

  1. SVON:基于八叉树场景划分+A星寻路的算法实现,有开源库(Native源码+Unity工程)见链接。

  2. Mercuna 3D Navigation:看官方介绍类似SVON,对A星算法进行了优化,有UE和Unity的插件,但没找到相关插件下载,需要联系官方。

感谢羽飞@UWA问答社区提供了回答

封面图来源于网络


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

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

查看原文

赞 0 收藏 0 评论 0

侑虎科技 发布了文章 · 11月19日

Unity版本使用情况统计(更新至2020年10月)

引擎版本的选择对于项目的开发至关重要!自2017年11月UWA第一次发布Unity版本的使用统计报告以来,受到开发者们的广泛关注和积极反馈,便于行业人员参考并做出合理的选择。

今天我们将分享UWA统计的第七期Unity版本使用情况,于2020年5月至2020年10月之间,在UWA网站www.uwa4d.com上提测Unity项目的版本使用统计。

2020年5月—2020年10月版本分布

图1
图1

以近半年的数据统计来看,如图1所示,2018.4的版本在开发团队中的使用率较高,达到60.83%;其次分别为2017.4、2019.4、2019.3和2019.2,占比分别为14.62%、6.68%、4.15%和3.25%;相比于2019年11月至2020年4月的数据统计,5.6的版本使用率已经大幅度降低;使用2019版本进行开发的项目占比有所提升。


近半年Unity版本使用趋势


图2

从近半年的使用趋势来看,如图2所示,2018.4和2017.4是开发者较为青睐的版本,其中2018.4是近半年来开发者使用率最高的版本;2018.4版本自今年5月份以来,一直继续呈较高的使用趋势;2019.4的版本在今年7月份开始有显著提升。

下面,我们再对每个系列的具体版本号做详细的分析。


近半年2019.4版本使用分布


图3

2019.4系列中,版本普及率依次为2019.4.5(18.92%)、2019.4.4(16.22%)、2019.4.0(13.51%)、2019.4.2(10.81%)、2019.4.8(8.11%)、2019.4.9(8.11%)和2019.4.1(5.41%)。


近半年2018.4版本使用分布


图4

2018.4系列中,版本普及率相对靠前的依次为2018.4.0(12.47%)、2018.4.23(10.99%)、2018.4.21(7.72%)、2018.4.18(7.42%)、2018.4.13(6.82%)、2018.4.15(6.53%)和2018.4.19(5.34%)。


近半年2017.4版本使用分布


图5

2017.4系列中,版本普及率相对靠前的依次为2017.4.30(18.52%)、2017.4.34(13.58%)、2017.4.18(12.35%)、2017.4.40(9.88%)、2017.4.35(7.41%)、2017.4.32(6.17%)和2017.4.39(6.17%)。


特别说明

以下为大家挑选了一些与版本相关的问题,供大家参考。

更多问题可登陆UWA问答查看。

1、如何获取已打包好的APP的Unity开发版本号?

2、2018.4版本的Shadowmap相比2018.3的内存占用大了3倍。

3、Unity 5.6版本怎么出64位的包?

4、Unity 2020版本,ARBackgroundRenderer和ARRenderMode类没了,请问有相应替代类吗?

5、Unity 2019 图片压缩格式的选择。

6、关于Unity2019 AssetPipline v2和Accelerator的一些疑问

7、Unity2019的打包中的Gradle设置怎么不见了?

8、Unity2019.4打包android,自定义Manifest和GradleTemplate的规则和Unity2018.4不兼容

欢迎大家定期关注UWA的问答社区,我们会将最新的研究发现与大家共享。

查看原文

赞 0 收藏 0 评论 0

侑虎科技 发布了文章 · 11月13日

UE4 垃圾回收

UE4引擎为我们搭建了一套UObject对象系统,并且加入了垃圾回收机制,使我们用C++进行游戏开发时更加方便,而且游戏本身也可以极大程度地避免内存泄漏问题。

UE4引擎采用了标记-清扫垃圾回收方式,是一种经典的垃圾回收方式。一次垃圾回收分为两个阶段:第一阶段从一个根集合出发,遍历所有可达对象,遍历完成后就能标记出可达对象和不可达对象了,这个阶段会在一帧内完成;第二阶段会渐进式地清理这些不可达对象,因为不可达的对象将永远不能被访问到,所以可以分帧清理它们,避免一下子清理很多UObject。比如,Map卸载时就会发生明显的卡顿。

GC发生在游戏线程上,对UObject进行清理,支持多线程GC。

对GC可以设置若干参数,比如MaxObjectsInGame,规定了游戏中最大存在的UObject对象(对编辑器不生效),移动平台上默认设置了131072。当UObject数量超过这个阈值时,游戏会崩溃,其他详细参数可见UGarbageCollectionSettings、GarbageCollection.cpp和UnrealEngine.cpp中相关的属性。

下图为标记-清扫的工作原理:
image


1.1 GC何时进行

UE4引擎中GC可以分为主动引发自动引发两种方式。

1.1.1 主动引发

可以在执行一些操作时手动调用GC。比如,卸载一个资源后,立即调用一次GC进行清理。

游戏中可以调用ForceGarbageCollection来让World下次Tick时进行垃圾回收,也可以直接调用CollectGarbage进行垃圾回收(引擎中大部分情况都用这种方式主动引发)。

1.1.2 自动引发

游戏中,大部分的垃圾回收操作都是由UE4引擎自动引发的,普通情况下不需要手动调用GC,这也是理想的GC使用方式。

当World进行Tick时,会调用UEngine::ConditionalCollectGarbage()函数,函数中进行了一些判断,当满足GC条件时,才会执行GC。下面分析一下ConditionalCollectGarbage的执行逻辑。
image

UE4 GC流程 和 清理流程 可前往UWA学堂免费查看。

查看原文

赞 0 收藏 0 评论 0

侑虎科技 发布了文章 · 11月10日

关于_CameraDepthTexture的疑惑

1)关于_CameraDepthTexture的疑惑
​2)贴图Alpha通道对图片大小的影响
3)URP要怎么实现GrabPass的效果
4)如何获取AssetDatabase加载失败的Asset的Instance ID
5)如何判断Bundle文件加载进内存的时机


这是第226篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

Rendering

Q:一个关于_CameraDepthTexture的疑问。

如果开启_CameraDepthTexture,Camera就需要渲染一遍场景内所有带有ShadowCaster的可见物体的Pass来实现深度图。

但是场景中的物体在开启ZWrite的时候就把深度写进了Depth Buffer中了,直接获得这个Depth Buffer是不是比近乎DrawCall翻倍的方式更有效率呢?还是Unity在这方面有什么考虑?

另外,问一个更实际的问题:
我们的项目需要渲染场景的中湖水的深度效果,所有不透明的场景物体的材质都是关联同样一个Shader,这个Shader是带有ShadowCaster的。但是只有个别插入水中的物体需要去渲染ShadowCaster的Pass,有没有方法在不增加Shader的情况下,让没有插入水里的物体不渲染Shadow Caster Pass呢?我们用的是Built-in的渲染管线。

A:第一个问题,可以参考这个问题中Unity官方人员的回复。
里面讲了两个原因,第一是对于非全屏渲染的情况,本来是想拿对应相机渲染的深度,但是Depth Buffer是全屏的。第二个原因是因为很多平台不支持直接拿Depth Buffer的数据。

参考网页:
https://forum.unity.com/threads/poor-performance-of-updatedepthtexture-why-is-it-even-needed.197455/

另外查FrameBufferFetch相关问题的时候看到Unity论坛上另外一个贴子里面的回答。里面说到Unity支持了FrameBufferFetch,但是不支持DepthBuffer的获取。

参考网页:
https://forum.unity.com/threads/pixel-local-storage-and-frame-buffer-fetch-on-mobile-devices.604186/

第二个问题,如果不增加Shader,目前没想到其他好的方法。
如果可以增加Shader,可以将原来的Shader复制一份,只在ShadowCaster的部分加一个“NeedDepth”这样的Tag,将水下的物体的材质球换成这个Shader,另外做一个只有ShadowCaster并带有“NeedDepth”这个Tag的Shader,这个Shader用来做Replace操作。

额外增加一个Camera,这个Camera跟随主相机,或者作为主相机的子节点,创建一个RT,让这个Camera渲染到这个RT,在Update里面使用ReplaceShader去画一下,那么只有有那个Tag的ShadowCaster会进行深度渲染,后续可以对这个RT进行编码等操作,这个RT记录的就是水下物体的深度。整个过程看上去没有特别多的额外工作,觉得可以一试(我没有做过测试,但理论上是可行的)。

感谢Xuan@UWA问答社区提供了回答


Texture

Q:在UWA的《纹理优化:不仅仅是一张图片那么简单》这篇文章中,描述了图片含有Alpha通道会对内存有影响。

通过以下的测试资源配置:

  • Tga_Alpha - 含有Alpha通道
  • Tga_NoAlpha - 不含有Alpha通道
  • Png_Trans - 含有透明的图片
  • 进入到Unity中的Format,全部代码设置为TextureImporterFormat.ASTC_6x6

测试结果:

  • 三张图片Unity全部Format为下图格式
  • 三张图片显示的内存大小全部一样
  • Texture Importer的Alpha Source设置为None,对测试结果无影响

    问题:图片是否含有Alpha通道,对于同一个Format格式,内存大小都是一样的吗?
    Tex.7z

A:原文中的优化建议是去除无意义的Alpha通道(原文中的定义为Alpha值全部为1的贴图),这个确实是对内存优化有帮助的。

题主的测试用例中,无论是png格式还是tga格式,进入引擎后,都会被引擎转为内部的格式(RGBA、ETC、ASTC等)。设想一下,以ETC2为例,如果没有Alpha通道,在压缩质量可以接受的情况下,就可以选用RGB_Compressed_ETC2_4bits,而如果添加了这个无意义的Alpha通道,那么我们在批量导入设置时,都会自动选择RGBA_Compressed_ETC2_8bits。这样内存就相差了一倍。

而问题中关于ASTC的情况,在Unity编辑器源码中的Texture导入格式的定义有这样一句注释:// ASTC uses 128bit block of varying sizes (we use only square blocks). It does not distinguish RGB/RGBA,即与是否包含Alpha通道无关。关于ASTC格式的介绍,这里推荐你阅读一下Github上的ASTC Format Overview

而关于你楼上回复中提到的Unity新版本的问题,可以看下Unity论坛上的这个问题

可能有人觉得对于项目的贴图格式都是ASTC来说,去除Alpha的意义不是很大,其实不能一概而论,虽然大小相同,但根据(https://zhuanlan.zhihu.com/p/158740249)中的测试,是否有Alpha通道会一定程度上影响压缩质量。所以还是要在项目中合理使用。

感谢范君@UWA问答社区提供了回答

A2:游戏运行时Texture的Alpha通道要看导入后的情况,不能看源文件的情况。UWA本地资源检测对于Texture的Alpha通道检测的就是导入后的结果。

图片源文件的格式,图形硬件是不支持的,Unity也不直接接管。导入图片后,会按照Import Settings中的设置对图片进行处理,将图片导入成硬件支持的格式(在引擎中的格式),而在运行中使用的资源也是导入后的。

题主将Alpha Source设置为None,那么导入时就不对源文件的Alpha通道进行导入,而压缩格式为ASTC_6x6,这个格式是包含Alpha通道的。这样导入后三个资源都会默认生成一个全为1的Alpha通道。占用内存大小自然一样。

感谢Prin@UWA问答社区提供了回答


Rendering

Q:URP如何实现GrabPass的效果?URP管线已经去掉了原先GrabPass的功能,现在有一个扭曲特效的功能,类似热扰动那种。虽然URP能够直接获取到实体图做扭曲效果,但是像是半透明物体(比如:水、其他特效等)也需要被扭曲就做不到。

A:题主想要完全实现GrabPass估计不行,但是可以有替代方案:

GrabPass的意思就是先绘制a物件,然后绘制b物件的时候影响a,绘制c物件的时候影响ab。注意,这最重要的是b物件的扰动影响不了c物件,因为b物件是比c物件先绘制的。

但是如果你能够接受b物件和c物件的扰动同时影响abc,那么就简单了,先用一个RT将扭曲的信息存下来(其实就是UV偏移),然后在Uber Shader中将偏移应用在画面上。这样扭曲信息就扰动了所有物件(不管是不透明还是半透明)。

虽然不知道URP怎么实现的,但是我们自己改的SRP,就是这么实现的。

感谢王烁@UWA问答社区提供了回答


Editor

Q:现在有一个继承于ScriptableObject的Asset,无论出于什么原因,引用脚本的GUID错了。那么通过一定的编辑器脚本能找到这个Asset的Path,但是如何在ProjectView内选中它?

经测试,使用Selection.instanceID是可以选中的,而Instance ID一般需要通过Object来获取,但是因为脚本引用错误了,所以AssetDatabase是无法载入这个Object的。有什么办法或者API能获取到这个Instance ID呢?

A:通过查看公开源码,找到了一个可用的方法:

//assetPath : 某个找不到对应脚本的Asset实例路径
HierarchyProperty property = new HierarchyProperty(assetPath);
Selection.activeInstanceID = property.GetInstanceIDIfImported(); 

这样可以在ProjectView内正确选中了。

感谢题主黄程@UWA问答社区提供了回答


Addressable

Q:当下载好所有的资源后,在一个测试脚本中调用LoadAssetAsync函数,如下图:

在AssetBundleProvider这里是第一个疑惑:
既然Addressables全部采用缓存机制来存放AssetBundle包,那此处的File.Exists不是总会为False吗?

第二个疑惑在于:
Addressables在WebRequest完成下载后,如何把握把Bundle加载进内存的时机?也可能是我的思路有问题,但是在后面的堆栈追踪里面没有发现有类似于LoadFromFile这样的调用。

A:这儿的File.Exists并不是检查缓存,而是检查文件在不在StreamingAssets里面。而所有下载的资源则是通过缓存来获取的,若缓存里没有则下载。而Load这部分已经被封装在了UnityWebRequestAssetBundle内了。

感谢黄程@UWA问答社区提供了回:

封面图来源于网络


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

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

查看原文

赞 0 收藏 0 评论 0

侑虎科技 发布了文章 · 11月5日

使用SBP后,如何查询Bundle的依赖关系

1)使用SBP后,如何查询Bundle的依赖关系
​2)大量同样的怪物的骨骼动画如何优化
3)DynamicBone动态骨骼插件是否在项目里使用
4)音频换Wwise的成本
5)运行时如何保存Prefab


这是第225篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

AssetBundle

Q:使用ContentPipeline.BuildAssetBundles接口打出Bundle文件后,没有产生AssetBundleManifest文件,请问该如何查询某个Bundle所依赖的其他Bundle呢?

A:可以从ComatibilityBuildPipeline.cs的源码里面看到下面的代码,

用上图中的方法来生成Manifest对象,然后参考《使用SBP打Bundle如何读取AssetBundleManifest》生成资源文件和打包AssetBundle。

感谢Xuan@UWA问答社区提供了回答


Animation

Q:大量同样的怪物的骨骼动画应该怎么优化?

1.如果有N个怪物,是否同样的骨骼动画会计算N遍来得到每个Mesh的新的顶点位置。
2.如果1是对的,能否计算完一个怪物在动画这一帧的顶点位置后,其他怪物都用这个计算好的结果进行渲染。

第二个问题的这个方法我认为是和GPU Instancing的贴图动画是有区别的。不知道第二个方法有没有实现的可能,或者已经有人实现了吗?

就是这一群模型表现都一样,变换矩阵每一帧都一样。请问Shared SkinnedMesh方案是什么思路,或者有没有参考链接?

A:请参考以下答案:

1.不同模型的动画序列帧不同,骨骼的变换矩阵必然有差异,除非这一群模型表现都一样,可以自己写一套Shared SkinnedMesh的方案。如果需要在CPU端蒙皮把Shader中的蒙皮计算拿出来就好。

Shared SkinnedMesh的方案参考:https://lab.uwa4d.com/lab/5bc6f85504617c5805d4eb0a

2.GPU Instancing的方案也是通过将序列帧骨骼动画烘培到贴图中,然后在顶点着色器中实现蒙皮计算的过程。

具体细节可参考:《移动游戏开发核心技术讲解:3D UI、GPU Skinning和DOTS》中GPUSkinning相关内容。

感谢羽飞@UWA问答社区提供了回答


Animation

Q:DynamicBone动态骨骼插件有人测试过性能吗?是否建议在项目里使用?

A1:这插件的性能真费,项目里主角30根骨骼用上这个插件后,高端机每帧耗时1ms。2个人就是2ms了。

感谢剑影蒙残@UWA问答社区提供了回答

A2:可以用,但是不能大面积用,最好用在重点对象上,适合搞一些骚操作。

感谢与我相关@UWA问答社区提供了回答

A3:完全可以使用。

我们上一个项目中有大量的舞蹈动作,最多支持16人的同台表演。在服装制作上使用了和DynamicBone类似的Unity日本开源的UnityChan中的SpringBone。同时进行了魔改,加入了横向约束防止在抬腿等大幅度动作时的穿模。魔改后的版本计算量比原版更大。

这类弹簧骨骼系统的计算量主要在两方面,一方面是计算弹簧间的力传导,另一方面是计算碰撞,都是重CPU的计算,是非常适合放入子线程去做的。刚好可以应用Unity的ECS来进行优化。

目前网上开源的实现中,可以参考下https://github.com/EsProgram/uSpringBone的实现。但由于ECS目前还未正式Release,所以稳定性不是特别好。

前段时间在逛AssetStore时,发现一个新的插件,使用JobSystem+BurstCompiler,推荐你们去看看。

MagicaCloth

感谢范君@UWA问答社区提供了回答


Audio

Q:我们现在Unity原生的音频,使用的是一个Resonance-Audio插件,可能我们自己也没用好,但能凑合着用。

听说Wwise不错,打算都换上。但昨天下载了一天没安装上,而且和现有音频不能一起用,不知大家是否也遇到过这样的问题,有没有什么好的思路?

大家在项目中途转Wwise的成本大吗?

A1:我们也转了Wwise,与原生确实不能共用。需要把现有的音频放到Wwise工程之后,再分平台统一导出,Wwise导入导出以及管理还是蛮方便的。安装也是装了两三遍才装完。

Wwise Launcher软件和Unity Intergration似乎是分两次下的,集成入Unity工程需要等那个页面反应一下,否则一不小心又会点到下载。

问题确实在安装,设置,打包,打热更都会有一点。

链接里是我碰到过的几个问题,虽然不是很全,可以参考看看:https://kmageek.com/2020/06/23/wwiseUnity/

感谢Maggie@UWA问答社区提供了回答

A2:做好音频文件和项目工程的解耦(音频播放配表,不是通过Prefab关联),转过去会很方便,Wwise通过Bank加载、ID播放,做好ID和音频名称的映射。一般会进行二次开发,在Editor做个Bank解析、播放、关联的小标签会方便策划配置。

感谢郑骁@UWA问答社区提供了回答


Script

Q:运行时代码克隆了一个Prefab,然后我改了坐标或者角度,如何再保存回Prefab?

我这个保存的数据只是举了简单的例子,实际是运行时美术会改特效特效Particle system里面一堆参数的。

A1:建议使用ScriptableObject来实现相关功能。

不同对象的参数调整要分开考虑。如果是粒子系统,编辑模式下基本都可以调了。大部分参数在编辑模式下调好,非常少量的可以在运行模式下调。调好后拷贝一下,然后退回编辑模式粘贴一下应该也可以了。

当然也可以把一些参数提出到ScriptableObject,调这个序列化,每次运行启动的时候赋值给目标对象。如果有必要也可以做个按钮,运行时按后直接去修改Prefab内的值,只是这样处理起来比较麻烦。不是很大很复杂的需求还是尽量避免。

其他的一些Prefab内编辑器模式下不方便调的,还是建议提出到ScriptableObject来处理更好。

感谢黄程@UWA问答社区提供了回答

A2:PrefabUtility.SaveAsPrefabAssetAndConnect

感谢与我相关@UWA问答社区提供了回答

封面图来源于网络


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

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

查看原文

赞 0 收藏 0 评论 0

侑虎科技 发布了文章 · 10月28日

纹理优化:不仅仅是一张图片那么简单

在上期《网格优化:溃堤之穴,一个也不能放过》中,我们对网格相关的优化知识点做了讲解。无论是大家在开发时的疏忽,还是对相关知识点的理解不足,这些问题的积累最终都会反映到项目的性能表现上。为此,我们将这些规则列出,并且以一个个知识点的形式逐一解读。

本期我们聚焦纹理相关的优化,以UWA本地资源检测“包含无效透明通道的纹理”“过滤模式为Trilinear的纹理”“开启Read/Write选项的纹理”这三条规则,来为大家简单说明相关的优化要点。我们将力图以浅显易懂的表达,让职场萌新或优化萌新深入理解。


1、包含无效透明通道的纹理

首先,Alpha通道是个计算机图形学术语,常常被理解为图像的“透明度”。 其实Alpha本身没有透明度的意思,Alpha通道指的是除R(Red)、G(Green)、B(Blue)以外的一个通道,取值为0到1。一个像素的Alpha值常用来表示像素是否对图片有“贡献”,经过运算之后,其外在表现是“透明度”。我们在纹理格式中见到的“RGBA”指的就是纹理的四个通道R、G、B和Alpha。

简单来讲,像素的(R,G,B)值与Alpha值通常是相乘的关系,当某个像素的Alpha值为1时,则表现为完全不透明状态。如果一张纹理上所有像素的Alpha值都为1,那么在渲染的计算过程中,关于该纹理Alpha值的计算就是无意义的,这就会导致了内存上和计算上不必要的开销。

所以本条检测规则筛选出Alpha值全为1的纹理后,建议开发团队去除相应纹理的Alpha通道。


2、过滤模式为Trilinear的纹理

对于纹理,大部分情况下,我们都是要把它作为“一层皮肤”去贴到图形表面。在渲染管线的逐片元着色阶段,会对利用屏幕上像素对应的UV坐标对纹理进行采样,采样过程可以笼统地理解为“贴皮肤”的过程。在这个过程中,纹理常常会被“放大”、“缩小”、“变形”。纹理在图形表面就会出现模糊或者锯齿化的现象,从而降低整体的表现效果。为了降低放大或缩小对纹理表现带来的“失真”影响,过滤模式应运而生。

过滤模式会提供多种方案来使得纹理投影到物体表面的过程变得更为顺滑自然。一般来讲,Unity提供了三种模式:

Point(no filter)——Point模式为不过滤的采样方式,使用最近点采样的方法,当UV坐标没有刚好对应Texture上的一个采样点时,它会选择最近的一个采样点作为该坐标的采样值。当纹理没有拉伸变形时,这样速度是最快的,且效果理想。但如果拉伸变形了,会出现马赛克现象。

Bilinear——双线性过滤模式。简单来讲,它会对相邻的像素进行模糊化处理,使得像素之间的变化更为圆润平滑,不会有明显的锯齿感或者马赛克化。但这只涉及单个“平面”的操作,所以一旦涉及mipmap层级间的处理,双线性过滤就会有些“力不从心”,某些表现效果会大打折扣。

Trilinear——三线性过滤模式。和Bilinear模式类似,但是额外优化了mipmap层级间的转换效果,它会在mip层级间进行模糊处理,弥补了Bilinear模式的不足。

这里我们延伸讲一下关于“mipmap”的概念。简单来讲为了更好地应对纹理贴图在不同距离和大小情况下的表现效果,以提升渲染的速度和降低图像锯齿化的影响,Unity会以纹理原尺寸为基础,预设几个等比例缩放的“复制品”,在实际使用中会根据情况加载对应的mipmap贴图,从而提升渲染性能,放大缩小的过程也因为mipmap层级的选择而更为快捷。

回到规则本身,Trilinear模式对表现效果的提升,是以GPU的额外开销为代价的。同等条件下,三线性过滤的GPU占用是最高的。所以如果对纹理的细致程度不那么敏感(比如像素类游戏),或者不涉及mipmapping的应用(比如2D游戏),那么就没有必要去选择Trilinear模式。

所以建议开发团队对本条规则检测下的纹理资源,依据项目实际需求进行相关的过滤模式的修改。


3、开启Read/Write选项的纹理

有时项目的实际运用中,会对纹理进行读写操作。

开启纹理的Read/Write选项后,就可以从脚本来访问纹理数据。内存中会产生一份纹理数据的副本,以便于对纹理进行实施的编辑和修改,以此来达成某些动态的纹理效果。所以如果相关纹理不涉及读写的操作,Read/Write选项的开启就会导致内存上额外的占用。

大家可以参看上期《网格优化:溃堤之穴,一个也不能放过》中关于网格的读写权限,两者在Read/Write选项的开启关闭上很类似。所以开发团队可以对照着去理解纹理读写权限开关的实际运用环境,从而对本规则下选出的纹理进行对应的权限修改,以减少不必要的内存开销。

需要说明的是,每一项检测规则的阈值都可以由开发团队依据自身项目的实际需求去设置合适的阈值范围,这也是本地资源检测的一大特点。同时,也欢迎大家来使用UWA推出的本地资源检测服务,可帮助大家尽早对项目建立科学的美术规范。

我们已支持Luacheck功能啦~
我们已支持粒子特效检测~
这么多实用贴心的功能
你们还没用起来吗?

万行代码屹立不倒,全靠基础掌握得好!

性能黑榜相关阅读

《那些年给性能埋过的坑,你跳了吗?》
《那些年给性能埋过的坑,你跳了吗?(第二弹)》
《掌握了这些规则,你已经战胜了80%的对手!》

查看原文

赞 0 收藏 0 评论 0

侑虎科技 发布了文章 · 10月28日

URP关于多个摄相机的性能优化

1)URP关于多个摄相机的性能优化
​2)Unity Addressables打包的时候如何设置BuildAssetBundleOptions.DisableWriteTypeTree
3)Unreal可以用于商业化游戏的热更新方案
4)UGUI SpriteAtlas在使用中回调实例化,AtlasRequested和Start的顺序颠倒
5)从AssetBundle中动态加载渲染管线,后期渲染异常问题


这是第224篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

Rendering

Q1:URP7.4.3,除开主相机外,还有一个子相机,用于将照到的模型渲到游戏主界面UI上。在Profiler中看到以下情况:

可以看到,在子相机中也进行了包括对LOD的计算,但子相机的CullingMask只开了一个名为RTModel的Layer,在这一层里只有一个3D对象。按说子相机CullScriptable这块开销不应该有才对。

目前怀疑可能的原因是URP会对每个Base Camera都进行这部分的计算,但如果用Overlay相机,又无法用原来的方式将相机的targetTexture渲到一张RawImage上了。

有人遇到过么?

A:题主的疑惑是:子相机的CullScriptable这块的开销不应该有那么大对吧(毕竟我只有一个物件)?

这里有两个问题:
1.Culling到底做了啥,只有一个物件为啥要Culling那么久(难道只有一个物件也要做很多的准备工作)?
2.你在Profiler里面看到的数据真的是真实数据吗?也就是说,子相机的Culling真的做了1.68ms吗?

抛开这两个问题,也可以有更好的做法:

我们一共两个相机,主相机和UI相机,那么UI上显示的3D物件怎么办呢?
我们有个虚拟相机,所谓相机,其实就是做一个VP矩阵,做一个RT,绘制可见的物件就可以了。那么使用Unity的SRP,随便在什么地方,设置一下VP矩阵,设置RT,接着绘制指定的物件(UI中所有的3D物件都会挂在这个物件下面),然后这个RT就可以随意使用了。

假如一个UI上有两个3D物件,尽量都放在一个RT上,如果不行,就放在两个或更多的RT上。只是会多几个绘制命令,几个RT(还不需要是全屏的),而且会多几个Swap RT的操作。由于我们项目没需求需要若干RT,所以假设一下,在这种需要若干RT的情况下,也可以用一个RT加多个Viewport来解决的。这个代码都是现成的,参考一下Cascade Shadow Map的做法,这样Swap RT也就省了。

综上所述,既然你都知道自己要绘制什么,就不要给Unity Culling的机会了。

感谢王烁@UWA问答社区提供了回答

Q2:数据是在Development Build中连真机看到的性能数据, 目前在使用类似于HLOD的方式来减少掉这个LOD的巨大开销。楼上说的“设置一下VP矩阵,设置RT”,请问这个VP矩阵的操作具体是什么?可否详解下或者有相关资料吗?

A:一切皆有可能!但不过这个不重要。然后你提的HLOD和LOD和上面的Culling没关系。VP矩阵就是view矩阵和projection矩阵。相机的作用就是提供这俩矩阵的。

如果你在管线里面设置了相应的矩阵,然后绘制指定的物件,就可以完全不用多一个相机,毕竟多一个相机就多一个Culling。

如果你对VP矩阵不熟悉,不清楚怎么实现,也简单。依然用一个额外相机,关上这个相机的Culling,然后在渲染pass中,不要绘制cullingresult.visibleobject,而直接用Graphics.DrawMesh或者CommandBuffer.DrawMesh绘制你要显示的那个3D Object的物件就好了。

感谢王烁@UWA问答社区提供了回答


Addressable

Q:原来的AssetBunlde打包的时候可以设置BuildAssetBundleOptions.DisableWriteTypeTree,使得包体小好多。今天项目尝试升级使用Addressables,请问该如何设置BuildAssetBundleOptions.DisableWriteTypeTree?

A1:首先Addressable打Bundle包主要的脚本是BuildScriptPackedMode,在DoBuild函数内可以看到用了AddressableAssetsBundleBuildParameters这个类,这个类继承于BuildParameters,而BuildParameters有一个成员变量是public ContentBuildFlags ContentBuildFlags { get; set; },这个ContentBuildFlags就是用于设置DisableWriteTypeTree的,BuildParameters是属于ScriptableBuildPipline的。

参考SBP的CompatibilityBuildPipline有以下操作:

if ((options & BuildAssetBundleOptions.DisableWriteTypeTree) != 0)
   parameters.ContentBuildFlags |= ContentBuildFlags.DisableWriteTypeTree;
IBundleBuildResults results; 
ReturnCode exitCode = ContentPipeline.BuildAssetBundles(parameters, content, out results); 

我们在BuildScriptPackedMode.cs里也可以加上:

var buildParams = new AddressableAssetsBundleBuildParameters(
                    aaContext.Settings,
                    aaContext.bundleToAssetGroup,
                    buildTarget, 
                    buildTargetGroup,
                    aaContext.Settings.buildSettings.bundleBuildPath); 
                buildParams.ContentBuildFlags = UnityEditor.Build.Content.ContentBuildFlags.DisableWriteTypeTree; 

但是我在这儿测试了一下,并没有什么用,不知道是最底层没有支持还是我的测试用例有问题。建议用同样的方法,测试一下。

另外说一下,我看了一圈代码,在打包和压缩部分SBP并没有用到这个参数。而在处理场景依赖的地方,这个参数会被合到BuildSettings结构体提供给C++部分调用。所以这个参数应该最终传到C++层起作用。

感谢黄程@UWA问答社区提供了回答

A2:Addressable似乎没有给外部的接口直接来修改这个设置项,应该是需要自己写代码来修改打包方式了。需要自己写一个BuildMode,可以参考以下这个帖子中第五个问题的回答:https://answer.uwa4d.com/ques...

核心的代码是要DoBuild中修改buildParams的设置项,buildParams.ContentBuildFlags |= UnityEditor.Build.Content.ContentBuildFlags.DisableWriteTypeTree;如下图:

做了一下测试,发现是有效果的:

上面是默认的打包,修改后重新打包,变成下面的结果:

整体都变小了一点点,而且使用解开AssetBundle后,可以看到里面的内容变得很简洁。

感谢Xuan@UWA问答社区提供了回答


Unreal

Q:Unreal方面,目前有什么可以用于商业化游戏的热更方案吗?

A1:因为我们公司是自己研发的一套框架,所以我也没有太在意是否有开源的Unreal热更新方案,印象中好像是没有的。

但基于Unity引擎的开源方案倒是蛮多,你们可以按照Unity的开源方案的思路实现一套,基本思路就是使用反射预先生成引擎的导出Lua接口。

如何利用反射呢?简单来说就是根据反射得到引擎接口的函数名,返回值类型、参数名和参数类型,由工具生成解析参数类型以及它的个数和顺序,返回正确结果的Lua导出函数int xxx(lua_State *L) 函数。对于类成员函数需要导出到Table里,根据C++类层级关系利用Metatable可以找到父类的Lua导出成员函数。

Unity开源方案如此,Unreal也可以如此,区别只是Unreal使用UClass UProperty这套,而Unity使用C#而已。你们可以尝试着实现一下。

感谢王远明@UWA问答社区提供了回答

A2:可以参考以下这个链接:
https://github.com/Tencent/puerts

感谢lanyt@UWA问答社区提供了回答

A3:腾讯有两款Lua热更新的框架可供使用:

  1. sLuaUnreal
    据说是《和平精英》手游采用的框架;
  2. UnLua
    较sLua后推出的一个框架。

感谢Vest@UWA问答社区提供了回答


Script

Q:请问UGUI SpriteAtlas使用中,AtlasRequested和Start的顺序颠倒,如下图:

测试工程可戳原问答获取,测试环境Unity 2019.4。

A:可以使用Timeline看到具体的执行时机,在Main.Update里面进行实例化的时候(我测试的时候把Main.Update里面的interval去掉了),Atlas的回调跑到第二帧了,而TestAtlasSprite的Awake、OnEnable和Start都在第一帧。在Main.Start里面实例化的时候,这些打印都在第一帧,因为Atlas的回调是在EarlyUpdate.SpriteAtlasManagerUpdate里面,而实例化出来的TestSpriteAtlas.Start是在FixedUpdate.ScriptRunDelayedFixedFrameRate下面的,所以就跑到了Atlas回调的后面。


这个图是在Update里面执行的情况。


这个是在Start里面执行的情况,可以看到都是在第一帧打印的。


这是在Main.Start里面执行实例化的Timeline的图,可以看到TestSpriteAtlas.Start是在Atlas的回调后面执行的。


这是在Main.Update里面执行实例化的Timeline。

EarlyUpdate,FixedUpdate这些回调的执行顺序可以参考:
https://medium.com/@thebeardphantom/unity-2018-and-playerloop-5c46a12a677

感谢Xuan@UWA问答社区提供了回答


Rendering

Q:有人遇到动态切换管线,显示错误的问题吗?
现有UBP项目需求在进入新的场景之后,切换到指定的渲染管线。在打包场景的时候将渲染管线一并打包到场景中,并通过以下代码来切换渲染管线。

 GraphicsSettings.renderPipelineAsset = targetAsset;
 QualitySettings.renderPipeline = targetAsset; 

但是通过AssetBundle加载出来的渲染流程:在后处理中,UberPost 缺少两个Keyword。

造成如下的图像。

在Project Setting -》 Quality里面手动设置成项目的hero_show管线则正常显示。


通过断点调试发现:在Render过程中,Keywords其实都是有赋值的。二者的流程均一致。

在PostProcessPass.cs文件中:

void Render(CommandBuffer cmd, ref RenderingData renderingData) 


但就是在Frame Profiler中抓帧显示没有Keywords。附上简单的项目测试:

默认是Windows平台。直接运行defaultScene即可。
如不是,按下面流程处理:
1. AssetBundle->build
2. 运行defaultScene.

链接:https://pan.baidu.com/s/1q3s6mAUwE723wTJ3VmS81g
提取码:8kwn

A1:虽然没遇到过,但是能猜到原因。
先总结一下楼主的问题:
Project Setting -》 Quality里面手动设置渲染管线,显示正常;
以AssetBundle的形式加载渲染管线,显示不正常,原因是Keyword丢失。

那么答案就很简单了。
因为这两种模式Unity对渲染管线这个资源的处理方式是不同的:
以第一种方式,Unity会认为渲染管线是个默认资源,将其全部打包进包体,并进行使用。
以第二种方式,Unity会将渲染管线,特别是渲染管线关联的Shader,判断其Keyword是否被使用,如果没使用,就优化掉了。

项目还没有看,我猜测楼主是在代码中开启的这两个Keyword,这种代码开启Keyword的方式,Unity检测不到,所以就丢掉了。

解决方法也简单,对付Keyword常用的方案,创建一个没用的材质球,使用相应的Shader,在材质球中开启对这些Keyword的使用打进AssetBundle,这样Unity就知道,这个Shader的这些Keyword是有用的,就不会Skip掉了(其实这个方法常被用于以防instance_on这个Keyword丢失)。

感谢王烁@UWA问答社区提供了回答

A2:想尝试楼上的方法,发现材质球并不能直接引用到UberPost这个Shader,因为这个Shader是放在Package目录下,于是参考了Packages目录下Shader打包这个帖子中的方法进行打包。使用编辑器的SVC采集方法采集了一下SVC,SVC里面的UberPost是有那两个Keyword的。将UberPost和这个SVC一起打包,拆包后可以看到ubqScene场景是引用了刚刚这个SVC中的Shader的,切场景后在Awake里面加载SVC并Warmup,然而也还是没有解决问题。最后我把题主的原始工程打了一个exe,运行发现并没有Keyword丢失的问题,渲染一切正常。另外打包成APK测试,在小米9上测试也是正常渲染的。

可能还是Unity编辑器的问题吧,类似于这个帖子:
UnityEditor下加载AssetBundle,材质球Keywords正常,但是某些属性不存在)。

感谢Xuan@UWA问答社区提供了回答

封面图来源于网络


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

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

查看原文

赞 0 收藏 0 评论 0

侑虎科技 发布了文章 · 10月21日

开启Allow unsafe code的影响

1)开启Allow unsafe code的影响
​2)Unity线性空间下,PhotoShop制作半透明图片,制作带外发光、内发光的图层如何正确导出
3)Unity中实现完善的暂停机制
4)RenderPipelineManager.DoRenderLoop_Internal的堆内存优化
5)Unity 2020版本中ARBackgroundRenderer和ARRenderMode的相应替代类


这是第223篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

Script

Q:最近项目引进几个插件,以及学习别人的插件,代码中用到了unsafe关键字,需要再project settings中勾选Allow unsafe code,之前知道一点是不勾选最好,但是勾选以后到底哪里不好也忘了,也没有勾选之后上线来验证,所以问问大家,是否应该勾选,以及勾选之后有何影响?

A:勾了没什么实际上的坏处,只是由于.net当初设计的时候是不鼓励使用unsafe代码的,所以在编译器默认设置这个门槛,鼓励大家尽可能使用安全的代码(也就是不要直接操作原生内存地址)。

但是,很多高性能的C#代码为了最大限度地高效访问内存,不可避免要用unsafe,只要保证unsafe代码被限制在较小的范围并严格测试保证不会出内存访问错误,对整个工程来说就不是大问题。

感谢招文勇@UWA问答社区提供了回答


Texture

Q:大家好,因为项目是PBR项目,所以Unity使用线性空间,现在在制作UI的时候,带透明通道的图片,混合时候就会出错。

我们前后试了几种方法:
1. 改PhotoShop的工作空间为线性的,定义Gamma为1.0:

2. 单独改用灰度系数混合RGB颜色:1.00,这样大部分情况下,Unity中效果PhotoShop中效果就是一致的:

3. 改Unity渲染管线。

以上方法都有一些局限和问题。

1. 普通不透明图层有些渐变效果无法绘制,因为线性空间下的暗部颜色精度不够;
2. PhotoShop制作带外发光、内发光的图层合并之后会错误:


3. 因为用的还是内置Forward管线,影响太大,低端机效率低下,暂不考虑。
我们最终采用的是第二种,美术还是在Gamma工作空间作图,只是图层混合采样。

最后想问下大家,针对线性空间下的半透明图层如何导出?
或者有没有什么办法能处理这种外发光、内发光图层问题?

A1:目前公司项目都采用Linear方式,美术直接修改PhotoShop设置,出图过后,在Unity中测试,能与原图是一个色彩,之前也尝试过其他办法,如Shader修正,但是都有一定的问题,目前采用的这种方案,暂未发现问题。

感谢范超@UWA问答社区提供了回答

A2:用下图的配置,在PhotoShop CC 2019里试了下,只要不合并图层,直接导出png,再放到Unity中就没有问题。另外如问题中的例子,只要从下向上合并图层(带着背景),最终结果就没问题。可能是PhotoShop的Bug。

就我和美术了解的情况,他们很少合并图层。一般导出就是把不需要的图层隐藏了。所以这种情况应该是可以避免的。

感谢张首峰@UWA问答社区提供了回答


Script

Q:目前使用TimeScale=0来暂停游戏,但是在UI上不想让特效和动画暂停,而在某些战斗场景,只希望暂停一部分逻辑。如果把TimeScale=0,就要处理ParticleSystem、TrailRenderer、Spine动画等组件,特别是TrailRenderer和Spine都需要修改源码。如果不用TimeScale=0, 是不是就要在框架层就处理好各模块的更新逻辑?

A1:常规的做法确实就如你说的,不用TimeScale=0,各个地方去实现暂停,然后统一管理。

感谢邓永健@UWA问答社区提供了回答

A2:可参考这篇文章:《Unity Chronos - Time Control 时间控制器》

感谢黄程@UWA问答社区提供了回答


Mono

Q:项目采用了URP,用UWA进行了初步测试,发现RenderPipelineManager.DoRenderLoop_Internal分配了很多堆内存,这个函数是URP中的内置函数吗?应该如何定位问题进行优化?

A:这个函数是内置用于SRP的总函数,不过分配堆内存的很有可能是题主自己写的C#函数。从图中来看,目前堆内存分配的太多了,建议通过GOT(Online)中的Mono模式来进行测试,就可以看到其具体的堆内存分配情况了。通过“倒序”查看方式,可以直接查看分配的最低级子函数,如下图所示。

以上回答由UWA提供


Editor

Q:请问下,以下这两个类现在有替代类吗?是怎么规划处理的?
UnityEngine.XR.ARBackgroundRenderer
UnityEngine.XR.ARRenderMode

以上这两个类经测试:
Unity版本:2020.1.4f1c1,找不到;
Unity版本:2019.4.7f1,找得到。

我又试了下当前最新的Untiy 2020.2.0b6版本这两个类也没有。
看了些资料,Unity 2020版本后,这两个类应该就被删除了。

A:Unity新版本将很多功能都是以Package的方式导入,关于AR从PackageManager中导入AR Foundation包,ChangeLog中有提到ARBackgroundRenderer已经在某个版本重命名为ARCameraBackground,AR/VR开发可能需要熟悉一下相关的Package,如下图:

感谢羽飞@UWA问答社区提供了回答

封面图来源于网络


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

官方技术QQ群:793972859(原群已满员)

查看原文

赞 0 收藏 0 评论 0

侑虎科技 发布了文章 · 10月16日

Unity Batches与glDrawElements的关系

1)Unity Batches与glDrawElements的关系
​2)渲染大面积草地时,如何降低消耗
3)HUD随着摄像机偏移
4)Unity中如何在竖屏模式的UI之上显示强制横屏的UI
5)iOS能耗问题


这是第222篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

Rendering

Q:Unity里用FrameDebug显示DrawCall是422个,Batches是435个,在SnapDragonProfiler里抓取出来glDrawElements数目是1449个,怎么理解这种差异?

A1:可参考以下几点:

  1. 题主的数据有一点问题,DrawCall数目应该大于等于Batches。根据特定的规则,多个DrawCall可以视为一个Batch,一个Batch中包含多个DrawCall,节省了一部分CPU提交数据的操作。
  2. 调用glDrawElements函数进行绘制就是DrawCall的一种形式。

感谢Vest@UWA问答社区提供了回答

A2:这部分可以参考UWA Day 2020里张强老师的分享《Unity移动游戏项目优化案例分析(上)》中的第二小节。

感谢范君UWA问答社区提供了回答


Rendering

Q1:渲染大面积草地时,如何降低消耗?

A1:有以下几点:

  1. 使用DrawMeshInstance。
  2. 上面这个API是不会进行视距剔除,视锥体剔除和遮挡剔除的。

下面有两种方案:
a. 将草地按区域分组,用每组的中心点计算视距,依据距离切换网格LOD或剔除。
还能用向量点乘简单剔除在相机后方的草地。(注意临界问题)
b. 借助CullingGroup。
CullingGroup.onStateChanged事件绑定,通过事件触发调整传入DrawMeshInstanced的Matrix顺序和渲染数量。(比较难的是,DrawMeshInstanced只能指定渲染前几个Matrix)

通过cullingGroup.SetBoundingSpheres实现视锥体剔除和遮挡剔除;
通过cullingGroup.SetBoundingDistances实现视距剔除和LOD;
这个方案也最好进行区域分组,不然CullingGroup的事件监听占用会比较高。中端机上4000个监听会占约2ms。

以后如果有对比两种方案的性能,我再进行补充。
附:
CullingGroup API的使用说明

Unity 3D研究院之Lightmap支持GPU Instancing

如何高效使用GPU Instancing技术来进行草丛渲染

升级Unity 2018过程中,遇到的DrawMeshInstanced不生效的问题

感谢题主李先生@UWA问答社区提供了回答

A2:可以使用Indirect模式的Instancing,配合ComputerShader实现视锥剔除和遮挡剔除。

感谢邹春毅@UWA问答社区提供了回答

Q2:手机上能用吗?

A:推荐一个使用URP制作的草海效果,亲测可在Mobile端使用。
Unity URP Mobile Draw Mesh Instanced Indirect Example

性能测试:

  • can handle 10 million instances on Samsung Galaxy A70 (GPU = adreno612, not a strong GPU), 50~60fps, performance mainly affected by visible grass count on screen(draw distance = 125)
  • can handle 10 million instances on Lenovo S5 (GPU = adreno506, a weak GPU), 30fps, performance mainly affected by visible grass count on screen(draw distance = 75)

感谢Vest@UWA问答社区提供了回答


Rendering

Q:HUD为什么会随着摄像机偏移?

代码如下:

    Vector3 midVe1   = obj.transform.position;
    Vector2 viewPose = worldCamera.WorldToViewportPoint(midVe1 + new Vector3(0, 2, 0));
    viewPose.x *= Screen.width;
    viewPose.y *= Screen.height;
    hud.anchoredPosition = viewPose; 

图片如下:

A:题主说的这个现象是合理的。

Vector2 viewPose = worldCamera.WorldToViewportPoint(midVe1);
Vector2 viewPose = worldCamera.WorldToViewportPoint(midVe1 + new Vector3(0, 2, 0)); 

这两个算出来的viewPos.x是不一样的。所以可以改成先算x,再偏移y。大概是这样:

Vector3 midVe1 = obj.transform.position;
Vector2 viewPose = worldCamera.WorldToViewportPoint(midVe1);        
viewPose.x *= Screen.width;
viewPose.y *= Screen.height;
viewPose.y += 130;
hud.anchoredPosition = viewPose; 

这里面y方向上的偏移值(130)就要自己调整了。

感谢Xuan@UWA问答社区提供了回答


UGUI

Q1:针对Unity中如何在竖屏模式的UI之上显示强制横屏的UI,我们现在有两种思路:

1. 改Screen.orientation之后再显示。
- 需要考虑外层RectTransform是否已经“完成”了转屏导致的变化,然后才能去适配内层。尚未找到可靠的事件。
- 需要处理背景,半透明背景可能透出后面的UI排版混乱。

2. 将新UI旋转依z轴转-90度显示。
- 对刘海屏的适配需要做相应的修改。
- 无法转屏(因为还是竖屏,且不支持Upside Down)。但是这个应该可以克服。

请问,两者哪种更好呢?

A1:一个简单的方式:
Canvas的RenderMode改成World Space,管它摄像机怎么裁剪,UI画布也不会发生变化。

如果自适应不了,在Canvas上根据你自己的需求做好自适应就好。

感谢郑骁@UWA问答社区提供了回答

A2:我们之前项目里刚好有个类似的需求,在横屏游戏中,个别玩法切换到竖屏。

当时采用的解决方案是修改Screen.orientation,同时修改UI全局根节点上的CanvasScaler的referenceResolution(竖屏时设置为1080,1920,横屏设置为1920,1080),matchWidthOrHeight(和项目的适配策略相关,竖屏时设置为0,横屏时为1),切换时会有一个全屏遮罩的fade效果,来避免切换时显示错误。

感谢范君@UWA问答社区提供了回答

Q2:请问为什么不考虑转90度的做法?

A:我们当时没有测过旋转的方案。现在大概想想有两个问题,不知道想的对不对。

  1. 单纯旋转角度应该只能在横屏的中间部分显示一个竖屏界面吧?感觉还需要其他处理。
  2. 对工作流是否有影响,如果要一个竖屏界面是否是要在制作时先做成一个横屏界面?内部控件是否要处理?例如最终呈现出竖屏效果的剧情文字从左到右出现,旋转之前应该是横屏的从下到上出现,同时每个单一文字也是旋转的。

感谢范君@UWA问答社区提供了回答

Q3:RectTransform的尺寸会被延迟修改这件事, 没有造成什么问题么?

A:记不太清了,印象中没有遇到什么大问题。不过我们当时这类需求切换时都是全屏UI,加了遮罩Fade后没有什么明显的穿帮。

感谢范君@UWA问答社区提供了回答


iOS

Q:我在Xcode真机调试中发现能耗始终处于Very High,而我项目的基本情况是(每帧GPU渲染3.5ms,每秒60帧,渲染分辨率为750*1624,设备iPhone XS,场景中只有UGUI没有其它3D或2D物体),麻烦大家帮忙看看这个电量消耗属于正常吗?还有没有优化的空间(帧数和分辨率估计是没有办法下降了,会影响项目效果),谢谢。

A1:测试过空场景Average Energy Impact也会显示Very Hight(如下图),题主的GPU能耗占比较大说明CPU相对而言还是压力比较小的:

这个帖子也是类似的问题,看上去是因为Unity引擎本身相对于普通APP体量较大,所以能耗表现较高,降低目标帧率会有所改善。

Energy Impact表征的是全局(非单个APP)的能耗,而且测试的时候USB连接充电是如何在统计中避免的不太清楚机制,建议使用Xcode Instruments工具的Energy Log模版测一下单个APP的能耗情况。
Energy Log文档

关于能耗的优化先关注整体的是GPU还是CPU(或其他方面),GPU的用FrameCapture定位一些比较高耗时的Shader进行优化,CPU结合Time Profiler模版定位一些高能耗的CPU Bound。

感谢羽飞@UWA问答社区提供了回答

A2:GPU的用FrameCapture定位一些比较高耗时的Shader进行优化。
关键是Capture哪一帧,这个会很盲目。

感谢山中千年@UWA问答社区提供了回答

封面图来源于网络


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

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

查看原文

赞 0 收藏 0 评论 0

侑虎科技 发布了文章 · 9月30日

关于Addressables做启动热更资源的路径问题

1)关于Addressables做启动热更资源的路径问题
​2)Unity 2018 Android平台Blit Type设置为Never时画面会变暗
3)视频压缩方案
4)关于AssetBundle中的资源冗余的问题
5)Addressable如何让加载的时候不对比服务器


这是第221篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

Addressable

Q:关于Addressables做启动热更资源的路径问题,将部分资源在可寻址系统里打入了StreamAssets,并将资源设置Cannot Release。打出程序后修改资源,做热更流程,然后运行程序也正常检测热更并下载,但是下载的更新AssetBundle包并不在PersistentDataPath路径下。如果是在同一机器下,热更包会在Unity编辑器的Temp路径下找到,可是我将程序拿到什么都没装的新电脑下,程序还是能检测热更下载,但PersistentDataPath仍然看不到热更AssetBundle,求解。

A1:Addressable下载更新用的是缓存的方式,所以不会在PersistentDataPath里面,会在应用的默认缓存目录里面,如下图:

如果需要修改缓存的路径,这个可以试下:

最后拉进去AddressableAssetSettings的Initialization Object列表里就行。

感谢Toushirou@UWA问答社区提供了回答

A2:感谢您的帮助,该方式确实可以重写缓存路径。值得注意的是Cache Directory Override并不能完全像Addressables Profiles里路径一样配置可变路径,只支持了带“{}”的方式,如{UnityEngine.Application.persistentDataPath},带“[]”则不支持配置。再次感谢层主的帮助!

另外,上层Cache Directory Override最终调用的源码,可根据自己的需求拓展,如下图:

感谢题主兮风@UWA问答社区提供了回答

A3:在安卓手机上安装一个apk,并且运行之后,假设这个apk的包名叫com.x.y,会在安卓手机上多3个目录:
目录1:sdcard/Android/data/com.x.y,这个目录下有一个cache目录。
目录2:data/app/com.x.y-1,这个目录下有目录lib和目录oat,还有一个base.apk。
目录3:data/data/com.x.y,这个目录下有有5个目录,files,shared_prefs,cache,code_cache,lib。
上面的目录1的sdcard和目录2的data是同级的。目录2和目录3需要root才能看到,没有root的手机应该是只能看到sdcard下面的文件。

当我运行Addressable进行远程加载资源后,在目录1里面多了一个与cache同级的files目录,这个files目录应该就是Application.persistentDataPath。在这个files目录里面会多两个目录,分别是com.unity.addressables,这里面放的是catalog.hash和catalog.json,另外一个是UnityCache目录,里面有两个目录,Shared和Temp,Temp是空的,Shared里面放的是下载的AssetBundle。

Addressable使用的是Caching机制,所以是可以通过类似于Caching.currentCacheForWriting = Caching.AddCache(“D:/Shalou/UnityCaching/”);这样的操作在PC上更改缓存目录的,在Android平台上应该也是可以的,这个就和安卓的权限有关系了,没有做进一步的测试。猜测是,如果没有开启External的Write Permission,只能在目录1下面的files目录里面进行目录替换了,也就是只能在Application.persistentDataPath后面添加子目录来更改缓存目录。

在PC上,题主说的默认的缓存和persistentDataPath确实是两个路径,如下图:

感谢Xuan@UWA问答社区提供了回答


Rendering

Q:有看到说Blit Type设置为Always会多出一次Blit操作,在2018下在真机测试了下出现类似对比度变大,画面变暗的效果。改回Always就正常。
色彩空间为线性空间。
这项设置有什么讲究?

A:AndroidBlitType.Never不提供sRGB后备缓冲区。线性渲染需要一个执行sRGB读/写转换的帧缓冲区(请参阅RenderTexture.sRGB),否则生成的图像通常显得太暗。因此,在使用线性渲染时,不建议使用AndroidBlitType.Never。如果想使用AndroidBlitType.Never进行线性渲染,尽管有这些信息,您仍然必须设置自己的sRGB渲染目标并处理针对后备缓冲区的Blit操作。

可以参考一下网址:

https://docs.unity.cn/cn/current/ScriptReference/AndroidBlitType.html
https://docs.unity3d.com/ScriptReference/AndroidBlitType.html

感谢小埃拉@UWA问答社区提供了回答


Video

Q:请教一下,对于视频来讲有什么比较合适的压缩方案?目前我们原始视频有近60MB,准备压缩后放入StreamingAssets下,读取解压后播放。除了对VideoClip本身做处理外,还有其他方案么?希望Android和iOS平台都通用。

A1:可以试试压缩成H.264或者H.265编码的MP4格式视频。

感谢马三小伙儿@UWA问答社区提供了回答

A2:H.264兼容各平台在Unity 2018上。

感谢Eureka@UWA问答社区提供了回答


Assetbundle

Q:用UWA检测AssetBundle资源冗余时,发现有17个资源冗余,但是通过对比查询Bundle的Manifest文件,发现这些冗余的资源都会单独打包成一个AssetBundle,并且没有存在于自己预设资源中,但是UWA给的结论是这份冗余的资源存在于他所在的各个预设当中,请问是怎么解?

A:我也是当初遇到的问题,Manifest没有这个冗余,AssetBundle加载进来,内存里你也没有发现这个冗余,调用GetAllAssetNames也看不到这个冗余的名字,但是如果调用LoadAllAssets,就出现了这个冗余,非常神奇,藏的很隐秘。

原因:Rawimage挂的Texture,ParticleSystem引用UI资源。
冗余的危害我就不说了。

解决方案:如果说尽量保证Rawimage的图和Prefab等资源打到一个AssetBundle,这个有点难,也很累,排查起来比较困难。

我的解决方案:

  1. 除了Background,禁止Rawimage挂Texture,而说实话,我的Background都做了RGBA分离,所以Rawimage挂的是Material,而Rawiamge不挂Texture是因为我们项目都是异步加载了的,尽量保证Prefab先出来,后面不重要的异步出来。
  2. 禁止粒子特效挂在到UI的Prefab上,并且写工具,禁止粒子特效的Prefab引用UI资源,由于粒子特效ParticleSystem描述文件缺失大,如果挂在UI上,还会造成Prefab可能上M数量级,影响加载速度,同时粒子特效和UIPrefab分离,也不会造成冗余。

感谢夏霖锐@UWA问答社区提供了回答


Addressable

Q:想做传统的那种,在进入游戏的时候统一加载更新,而不是目前这种,读取某个界面,就去服务器判断。
现在的问题是怎么让每一次加载的时候不去判断服务器,而在进入的时候判断全部更新。

A:可以在游戏启动的时候在一个专门的更新界面做更新,具体做法是先初始化,然后调用GetDownloadSizeAsync和Addressables.DownloadDependenciesAsync进行AssetBundle更新(Addressable是以AssetBundle为最小更新单元的),这样就会将所有需要更新的AssetBundle缓存到本地了,下次用到这些资源就不用远程下载了。大致代码如下:
    IEnumerator Start() 
    {       

        //只要打包的时候不要将Disable  Catalog Update on Startup勾选上就行,这样初始化的时候会自动更新Catalog到最新
        yield return Addressables.InitializeAsync();

        IEnumerable<IResourceLocator> locators = Addressables.ResourceLocators;        
        List<object> keys = new List<object>();
        //暴力遍历所有的key
        foreach (var locator in locators)
        {            
            foreach(var key in locator.Keys)
            {
                keys.Add(key);
            }
        }

        var handle = Addressables.GetDownloadSizeAsync(keys);
        yield return handle;
        long downloadSize = handle.Result;

        if (downloadSize > 0)
        {
            yield return Addressables.DownloadDependenciesAsync(keys, Addressables.MergeMode.Union, true);                
        }        
    } 
在游戏过程中,Addressable初始化以后,只要不调用Addressables.CheckForCatalogUpdates和Addressables.UpdateCatalogs,内存中常驻的Catalog生成的映射表对象是不会发生改变的,就算把服务器的Catalog和资源更新了也不会影响客户端的运行。

感谢Xuan@UWA问答社区提供了回答

封面图来源于网络


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

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

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 4 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 3月17日
个人主页被 833 人浏览