Unity对项目性能优化的实现

Unity对项目性能优化的实现,第1张

简化模型

最小化模型网格中的顶点和面的数量,避免复杂的网格。

使用纹理贴图代替复杂的网格

考虑用法线贴图对比高度贴图。法线贴图适用于伪造模型表面凸起和凹陷光照的简单纹理贴图。

高度贴图使用一张纹理来制作一种非常传统的3D表面几何图形。高度贴图比法线贴图优越是因为它们不仅定义了表面凸起和凹陷,而且提供了平行视差。作为一个着色器,它们的计算开销很大,只是没有网格的开销那么大。

限制需要绘制的对象

遮挡剔除(occlusion culling),在摄像机看不见对象时禁用对它们的渲染,因为它们被其他对象遮挡了。

Global Fog(迷雾限制),减少场景中细节渲染,其基于距离,比迷雾限制更远的对象将不会被绘制。

对细节分级或LOD分组,近处的物体用细节模型渲染,远处的模型则用简化的模型渲染,是一种简化几何对象的好方法。

光照和阴影的性能

节约使用实时光影,当某个对象投射阴影时会生成一个阴影贴图,它会被用于渲染其他可能接受阴影的对象。阴影有很高的渲染开销而且通常需要高端的GPU硬件。

其他技术如灯光探测器(实时或烘焙)和着色器的选择。

优化脚本

Update()回调函数每一帧都会被调用。移除不用的更新,使用一个状态变量和if语句在它们不需要时停止计算。内存管理,数学与物理。

批量处理

Unity是将不同的网格归类到一个单独的批处理中,这个批处理会被立即放进图形硬件。这比单独发送网格快很多。网格实际上先被编译进一个OpenGL顶点缓存对象或一个VBO,这是渲染流水线的低层细节。

每一个批处理调用一次绘制,在一个场景中减少调用绘制次数的比减少顶点或三角形的实际数量的效果更有意义。

共有两种类型的批处理——静态批处理和动态批处理

首先,确认在Player Settings中启用Static Batching和Dynamic Batching。

对于静态批处理,简单地通过在Unity的Inspector中为场景内的每个对象勾选Static复选框以标记对象为静态。把一个对象标记为静态是告诉它将永远不能移动,动画或缩放。Unity将自动把这些共享相同材质网格放在一起形成一个大网格。

共享相同材质的网格,所有这样的网格在一个批处理中必须有相同的材质设置——相同的纹理,着色器,着色参数及材质的指针对象。

对于动态批处理,那些没有标记为Static的对象,Unity将尝试把它们放进批处理,即使它会是一个更慢的过程,因为它需要考虑逐帧动画(CPU开销)。共享材质的需求依然存在,当然还有其他的限制,比如顶点个数(小于300个顶点)和统一的Transform Scale规则。只有Mesh Renderers和Particle Systems使用批处理,这意味着蒙皮网格,衣服,尾迹渲染以及其他一些类型的渲染组件并没有使用批处理。

多通道像素填充

多通道像素填充就是某些高级渲染器的工作方式。光照和材质效果,比如多光照,动态阴影及透明度(Trransparent和Fade Render模式)都是以这种方式实现的。

针对项目,可以选择优化并避免通道像素填充在一起,或者理解清楚什么样的场景需要高性能,什么样的场景需要高保真,需要仔细的策划这个场景。

使用Light Probes以很低的成本模拟动态对象的动态光照。

在Quality Settings中把同时发生的光照的全部数量设置为1。

其他渲染技巧

创建2048分辨率的纹理并导入到默认的1024的设置,这样可以加速渲染。

针对无阴影使用高质量的设置渲染到Android时,需要切换目标平台到PC,使用高分辨率烘焙光照并开启硬阴影和软阴影,再切换回Android。

针对低级设备的优化或将程序分为高低版本。

Unity内置的性能评估工具——Stats窗格和Profiler窗格

Game面板中可开启Stats窗格

CPU:获取到当前占用CPU进行计算的时间绝对值,或时间点,如果Unity主进程处于挂断或休眠状态时,CPU time将会保持不变。

Batches:即Batched Draw Calls,是Unity内置的Draw Call Batching技术。

什么叫做“Draw call”,CPU每次通知GPU发出一个 glDrawElements (OpenGl中的图元渲染函数)或者 DrawIndexedPrimitive (DirectX中的顶点绘制方法)的过程称为一次Draw call,一般来说,引擎每对一个物体进行一次DrawCall,就会产生一个Batch,这个Batch里包含着该物体所有的网格和顶点数据,当渲染另一个相同的物体时,引擎会直接调用Batch里的信息,将相关顶点数据直接送到GPU,从而让渲染过程更加高效,即Batching技术是将所有材质相近的物体进行合并渲染。

Tris:摄像机视野(field of view)内渲染的三角面总数量

Verts:摄像机视野(field of view)内渲染的顶点总数

Screen:当前Game屏幕的分辨率大小,58M表示总的内存使用数值

SetPass calls:描述渲染性能开销

Shadow casters:表示场景中有多少个可以投射阴影的物体,一般这些物体都作为场景中的光源。

visible skinned meshed:渲染皮肤网格的数量

Animations:正在播放动画的数量

Network:网络情况

Unity中的Profiler选项是一个性能探测工具,可以报告游戏中的哥哥区域花费的时长,包括渲染和脚本。它记录游戏中随着时间的统计数据并以时间线图表展现出来。点击可以逐帧查看细节

可以直接通过以下代码进行判断

[code]csharpcode: 

//当网络不可用时              

  if (ApplicationinternetReachability== NetworkReachabilityNotReachable)              

  { 

              //Do sth

    }

如果项目需要耗费的流量比较大,可以通过下面的方法判断,并提示用户

[code]csharpcode:   

//当用户使用WiFi时  

   if (ApplicationinternetReachability == NetworkReachabilityReachableViaLocalAreaNetwork)               

                   //Do sth 

  }                 //当用户使用移动网络时          

     

if (ApplicationinternetReachability == NetworkReachabilityReachableViaCarrierDataNetwork)             

   {

                    //Do sth               

   }

这是涵盖Assets,Resources和资源管理的一列文章中的第二篇。

这篇文章涵盖了Unity序列化系统的内部原理和Unity是如何在不同的Objects间保持健壮的引用的,包括Unity编辑器与运行时。也讨论了Objects和Assets在技术上的区别。这里涉及的主题对理解如何高效地加载和卸载Unity资源起到很重要的作用。合适的资源管理的关键是hi保持短的加载时间和较低的内存使用。

为了明白Unity是如何合理地管理数据的,了解Unity是如何标识和序列化数据是很重要的。第一个关键点就是区分Assets和UnityEngineObjects。

Asset就是硬盘的上的一个文件,存储在Unity工程的Assets文件夹。Textures,3d模型或音频都是常见的Assets类型。一些Assets包含了Unity的内部数据类型,如材质,而另一些Assets需要处理成Unity内部数据类型,如FBX文件。

UnityEngineObject或具有大写'O'的对象,是描述特定资源实例的一组集合。可以是Unity使用的任何资源类型,比如mesh,sprite,AudioClip或AnimationClip。所有这些Objects都是继承自 UnityEngineObject 。

虽然大多数类型都是内置的,但有两种特殊类型:

Assets和Objects是一对多的关系,也就是说任何给定的Asset文件包含一到多个的Objects。

所有的nityEngineObjects可以引用其他UnityEngineObjects。这些Objects可能是在同一个Asset文件,也可能从其他Asset文件引入。比如一个材质通常引用一个或多个Texture Objects。这些Texture Objects通常从一个或多个texture Asset文件引入(比如Pngs,Jpgs)。

当序列化的时候,这些引用由两个独立的数据组成:File GUID和Local ID。File GUID标识目标Asset文件保存在哪里。局部唯一的Local ID标识Asset文件内的每个Object,因为Asset可能包含多个Objects。

File GUID保存在meta文件。这些meta文件是在Unity引入Asset的时候生成的,并且存储在和Asset同一个目录下。

以上的标识码和引用关系可以通过文本编辑查看:创建一个新的UnityObject,修改Editor Setting暴露显示Mate文件,序列化Assets为text。创建一个material,然后导入texture到工程。把这个material指认给一个场景中的cube并保存。

使用文本编辑器打开跟上满material关联的meta文件,靠近顶部会出现标记为"guid"的信息。这一行定义了material Asset's文件的GUID。而查找Local ID需要使用文本编辑器打开material文件。material Object's 的定义是这样的:

在上面的例子中,&之后的数据就是material的Local ID。如果这个material Object在Asset中GUID标识为"abcdefg",可以将material 对象唯一地标识为文件GUID "abcdefg"的组合,Local ID为"2100000"。

为什么需要Unity的File GUID和Local ID 回答的坚定的,因为它提供了一个灵活的、不依赖平台的工作流。

文件GUID为文件特定路径提供了抽象描述。只要一个特定的文件GUID与特定的文件关联起来,那么文件在磁盘的路径变得无关紧要了。文件可以自由移动,而不需要更新所有引用这个文件的Objects。

任何一个Asset可能包含(或通过import产生)多个UnityEngineObject 资源,这个时候需要Local ID明确的区分不同的Object。

如果一个File GUID关联的Asset文件丢失了,对该Asset的所有对象的引用也会丢失。所以meta文件必须要保存在跟它关联的同名文件目录下,这很重要。注:Unity会重新生成删除的或丢失的meta文件。

Unity Editor有Paths到GUIDs的映射表。无论Asset加载还是引入,都会记录在映射表。映射表由Asset指定路径链接到Asset File GUID。如果Unity Editor打开的状态下,meta文件丢失,并且Asset路径没有变化,Editor能够确保Asset保留相同的File GUID。

如果Unity Editor在关闭状态下丢失了meta文件,或者Asset路径变化了但meta没有跟着移动,那么所有引用该Asset的Objects都会被破坏。

在11内部Assets和Objects章节说过,非Unity原生Asset类型必须导入到Unity。这是通过asset导入器来完成的。虽然这些导入器通常自动调用,但它们也可以通过 AssetImport API暴露接口。比如, TextureImporter API在导入单个texture Assets(如PNG文件)提供了访问设置的接口。

导入的结果就是引入一个或多个UnityEngineObjects。这些Objects在Unity Editor是可见的,以父Asset下有多个子Asset形式显示,比如多个sprites嵌套在一个texture Asset,会当成sprite图集导入。其中这些每个Object共享一个File GUID,因为他们的源数据保存在相同的Asset文件。它们将通过导入的texture Asset内部的Local ID进行区分。

导入进程会将源Asset转化为适合所选择目标平台的Asset。导入进程可能包含了一些重要的 *** 作,比如texture压缩。因为导入进程通常是一个耗时的进程,被导入的Asset缓存到Library文件夹,避免下次启动Editor又重新导入Assets。

切确的说,导入后的内容存储在以对应Asset的File GUID前两位数字命名的文件夹中。这个文件夹存储在Library/metadata/文件夹中。Asset中单个的Objects被序列化到一个名字与Asset的File GUID相同的二进制文件中。

导入进程对所有的Assets有效,不仅仅对非原生Assets。原生assets不需要漫长的转换过程或重新序列化。

File GUIDs和Local IDs都是健壮性比较好的,但GUID比较慢,运行时需要消耗更多性能。Unity内部维护一个缓存,它将File GUIDs和Local IDs转化成简单的,唯一的整数。当新的Objects在缓存注册的时候,会被分配一个简单的,单调递增的整数,这就叫Instance ID。

缓存维护了给定的Instance ID,File GUID和定义Object源数据位置的Local ID与 Object在内存中的instance(如果有的话)之间的映射。解析Instance ID可以快速返回Instance ID代表的加载Object。如果目标Object还没有加载,File GUID和Local ID可以解析到Object源数据,可以让Unity及时加载出Object。

项目启动的时候,Instance ID缓存初始化所有的Objects(如场景中的引用),只要这些Objects在Resources文件夹。另外,当新的assets在运行时导入进来或从AssetBundle加载Objects时,Instance ID会加进缓存中。当提供访问指定File GUID和Local ID的AssetBundle卸载(uploaded)时,Instance ID才会从缓存中移除。发生这种情况的时候,Instance ID,File GUID,Local ID之间的映射会被删除(节约内存)。如果这个AssetBundle重新加载了,会从这个重新加载的AssetBundle为加载出来的每个Object创建一个新的Instance ID。

深度讨论AssetBundles卸载的含义,这看 Managing Loaded Assets 章节和 AssetBundle Usage Patterns 文章。

在一些平台上,一些事件可以强制让Objects强制从内存中移除。比如,当IOS app暂停的时候,图形Assets会从图形内存中卸载。如果这些Objects是从AssetBundle加载出来的就会被卸载掉,并且Unity不能够重新加载这些源数据。任何现存的引用这些Objects的对象视为无效。前面的这种情况,场景中可能出现不可见的meshes或紫红色的。

实践Tips:运行时,上述控制流并非字面上那么精确。当运行时,有大量大量加载 *** 作的时候,File GUIDs和Local IDs表现没有那么好。当创建一个Unity项目的时候,File GUIDs和Local IDs切确地映射成了一种更简单的格式。

以上就是关于Unity对项目性能优化的实现全部的内容,包括:Unity对项目性能优化的实现、unity中如何判断是否连接上网络、【Unity】打包(二)等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9806447.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-02
下一篇2023-05-02

发表评论

登录后才能评论

评论列表(0条)

    保存