技术分享连载(七十五)

技术分享连载(七十五)

本期我们聚集了这些话题:资源加载方式对比、Unity内置Shader处理、Profiler中Assets和SceneMemory的区别...


我们将从日常技术交流中精选若干个开发相关的问题,建议阅读时间15分钟,认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。
UWA QQ群:793972859
UWA 问答社区:answer.uwa4d.com


资源管理

Q1:我们开发的是独立游戏,游戏本身资源不是很多,也不考虑会更新资源,就把资源都放到Resource下。基于上述背景,我想问下游戏启动时间会和这些资源的数量有关吗?这些资源会不会占用内存?还是说要Load出来并实例化之后才会算进内存里面?放在Resource下面的对象和在脚本里面直接引用有区别么?听说Unity 5.6之后的版本中Resources.load的速度慢了很多,我是否需要考虑把资源全部打包成AssetBundle来加载更好呢?相对资源打包成AssetBundle加载快也方便更新,那Resource加载有什么优势?

就题主的问题,回答如下:
(1)Resource文件下,如果资源过多,确实会影响游戏的启动时间;
(2)Resource文件夹下的资源本身不会在启动后加载到内存中,资源是在Resources.Load后被加载进内存的。但是需要指出的是,引擎启动时会创建一个Resource索引表,Resource文件夹下文件越多,其索引表也就越大,这个是会占用内存的;
(3)目前确实有不少团队反馈这个问题,具体内容可以查看这里:
https://answer.uwa4d.com/question/596dd374848e993475a664e5
(4)关于以上两种加载方式的选择,我们建议使用AssetBundle,这也是Unity Best Practice中的建议: Don’t Use Resource.

补充一点我个人的看法, 欢迎拍砖:
Resource和AssetBundle是两套不同倾向的资源管理策略,各自有适用的应用环境。Resource可以看成一个默认加载的巨大AssetBundle,优点在于使用方便,不需要应用层维护资源依赖关系,缺点在于对更新不友好(后文会详细解释),内存敏感的场景下不便于细粒度控制资源的内存占用生命周期,此外还会影响游戏初始化速度。

AssetBundle可以提供更细粒度的资源控制策略,而且对于资源更新友好,但是缺点是管理麻烦,应用层需要维护依赖关系以及管理生命周期,稍不注意就容易出现资源泄露等问题。

Resources和AssetBundle之间有一条天然的鸿沟,那就是AssetBundle无法依赖到Resource里的资源,反之亦然。那么就带来一个问题,如果Resources里一个资源a需要更新,除了更新这个资源a外,还需要更新依赖这个资源的资源b、c、d、e等,还要更新资源a依赖的资源x、y、z等,进而b、c、d、e、x、y和z所依赖的、以及被依赖的资源都需要更新,牵一发而动全身,简直是更新的噩梦。

对于资源更新频繁的网游,尽可能首选AssetBundle,Resource下可以放一些无需更新且启动时就需要的资源,譬如登陆场景的Loading图等,游戏启动的逻辑简洁不易出错;对于无资源更新的单机游戏,也需要尽可能控制Resources下的资源量,避免过长的游戏启动时间。

Unity 5.5及之后的版本,若发现Resources.Load慢了很多,不妨试试Resources.LoadAsync,说不定会颠覆大家的先入为主的“同步加载快于异步加载”的认识。类似地,AssetBundle.LoadAsset/LoadAssetAsync也可能存在同样的问题。

感谢UWA问答社区的胡阿毛提供了以上回答。

如您对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/598b1a22fb389f61434c5d4c


资源管理

Q2:如下图,不理解ScriptMapper具体是什么,为什么会引用StandardShader,有什么办法可以彻底把StandardShader从内存里移除呢?
请输入图片描述

这说明是有代码在索引这个Shader。建议题主尝试以下方法来定位Standard Shader的具体引用:

在游戏运行时遍历所有的GameObject或者Material,然后获取其所使用的Shader信息,查找跟“Standard”名称相对应的Shader,然后就可以定位它是出自哪个Material或GameObject。

上述方法应该适用于大部分情况,但无法适用于以下情况:

  • Shader被加载后直接被缓存在代码脚本中;
  • Shader是通过AssetBundle.LoadAll加载的;
  • Shader通过Preload Shader加载的。

此问答来自于UWA 问答社区,如您对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/598acb48fb389f61434c5d4a


资源管理

Q3:Profiler里Assets和Scene Memory的区别是什么?比如Mesh这一项,在Scene Memory的Mesh中看到的只有2个合并的Mesh:CombinedMesh(root: scene),在Assets的Mesh里看到的有100多个,包括场景里的非合并Mesh,动态加载出来的角色Mesh等。并且Assets的Mesh中的某些点击后选择右边的Referenced By,在Hierarchy里会自动选中场景中的物体,怎么看也不像是单纯的模板,而是实例化出来的东西。
请输入图片描述
请输入图片描述

Resources.Load/Assetbundle.Load出来的ParticleSystem都是放Assets下的,可以认为是模板资源,并不在场景里。Instantiate出来的放在Scene Memory下,是出现在场景里的。所以两边都有是正常的。

在Unity里资源至少分为两类:一类是可以被引用的,比如Mesh、Texture,如果要渲染多个相同的Mesh,并不需要对Mesh实例化,只需要在场景里多创建几份MeshRenderer/MeshFilter去引用它即可。所以Assets下的Mesh不应该被认为是实例化出来的东西,这些Mesh仅仅是通过AsssetBundle.Load/Resources.Load加载出来的,只是被场景里的东西引用了;但SceneMemory下的Mesh通常是通过new Mesh或者Instantiate创建的,这部分可以说是实例化出来的了,另外像CombinedMesh是Unity自己创建的,也可以算是实例化的。

另一类是不可被引用的,通常是组件资源,比如ParticleSystem,如果要渲染多个相同的ParticleSystem,就需要实例化多份出来,ParticleSystem的模板在Assets下,而实例化出来的在SceneMemory下。

此问答来自于UWA 问答社区,如您对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/5989765c9ba6ca7742b05636


骨骼动画

Q4:开启GPUSkinning,有合并数量限制(骨骼数量),在此我有几个疑惑:
1)动态修改合并后的Mesh数据,比如顶点色,如何递交GPU呢?
2)SkinnedMeshRender并没有相关的函数去刷新?
如果前两个问题解决起来麻烦,那么对于SkinnedMeshRender组件,自定义类,去实现相同的功能是否可行,若可行,是否需要注意某些细节,以适应Unity引擎。

问题一,可以看下这篇文章,里面简单提到了把动画放进纹理的方法(第三部分),理论上没有骨骼限制了。具体实现可以看文中Github地址。

问题二,直接更新或者覆盖SkinnedMeshRenderer的SharedMesh即可,但可能会对性能有影响,如果大量使用是需要具体问题具体分析的。

此问答来自于UWA 问答社区,如您对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/5989530a315ce4f3357f3062


制作

Q5:我准备用如下代码,使得GameObject旋转到某一角度:
请输入图片描述
但发现,当PredefAngleX取某些值时(例如45),旋转后if语句总是True,导致无法进入else进行下一步。但此时不管是Inspector还是Debug.Log出来,Transform.localEulerAngles.x和PredefAngleX都显示为45。 然后PredefAngleX取另外一些值时又正常(旋转后if能变为False)。MoveTowards用在Transform.Position则总是正常的。这是为什么呢?

浮点数精度导致。 建议体质把if条件换成如下:
if (Mathf.Approximately(transform.localEulerAngles.x, predefAngleX)){…}

比较相差Epsilon的数值可以用以下方法:
Mathf.Approximately ( https://docs.unity3d.com/ScriptReference/Mathf.Approximately.html )

此问答来自于UWA 问答社区,感谢 Switch.N提供了回答,如对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/59893fa4315ce4f3357f305f


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